Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VIH-9129 Updated logic for pushing updates to endpoints #250

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BookingsApi.Client" Version="2.0.11" />
<PackageReference Include="BookingsApi.Client" Version="2.0.12-pr-0857-0020" />
<PackageReference Include="LaunchDarkly.ServerSdk" Version="7.0.3" />
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.21.0" />
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
"net6.0": {
"BookingsApi.Client": {
"type": "Direct",
"requested": "[2.0.11, )",
"resolved": "2.0.11",
"contentHash": "4dJFAlR2hZqpKSKZgRrV6bzGvbXOia45Ej35cEPQhfjGrN66vP2ad184vvFmA9zVdS2jKqw08plKKe8OOraNNw==",
"requested": "[2.0.12-pr-0857-0020, )",
"resolved": "2.0.12-pr-0857-0020",
"contentHash": "oF8q0wZ8MCk2AywAx+DxzGPw40ZGYSocL1M6TjlP00GgkIW0e1kHbrfNcpZ8MRZ3hFhR+TIUwouR18Eu06FsNg==",
"dependencies": {
"Microsoft.AspNetCore.Mvc.Core": "2.2.5"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="BookingsApi.Client" Version="2.0.11" />
<PackageReference Include="BookingsApi.Client" Version="2.0.12-pr-0857-0020" />
<PackageReference Include="LaunchDarkly.ServerSdk" Version="7.0.3" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.2" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.9.0.77355">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="TimeZoneConverter" Version="3.3.0" />
<PackageReference Include="UserApi.Client" Version="1.49.4" />
<PackageReference Include="VideoApi.Client" Version="1.51.6" />
<PackageReference Include="NotificationApi.Client" Version="1.52.3" />
<PackageReference Include="UserApi.Client" Version="1.49.4" />
<PackageReference Include="VideoApi.Client" Version="1.51.7-pr-0645-0003" />
<PackageReference Include="NotificationApi.Client" Version="1.52.3" />

<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.25.0" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ public class EndpointUpdatedIntegrationEvent : IIntegrationEvent
public Guid HearingId { get; set; }
public string Sip { get; set; }
public string DisplayName { get; set; }
public string DefenceAdvocate { get; set; }

public List<string> EndpointParticipants { get; set; }
public List<string> EndpointParticipantsAdded { get; set; }
public List<string> EndpointParticipantsRemoved { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ public static AddEndpointRequest MapToRequest(EndpointDto source)
{
SipAddress = source.Sip,
DisplayName = source.DisplayName,
Pin = source.Pin,
DefenceAdvocate = source.DefenceAdvocateContactEmail
Pin = source.Pin
};
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,49 +1,32 @@
using VideoApi.Contract.Requests;

namespace BookingQueueSubscriber.Services.Mappers
namespace BookingQueueSubscriber.Services.Mappers;

public static class HearingToBookConferenceMapper
{
public static class HearingToBookConferenceMapper
public static BookNewConferenceRequest MapToBookNewConferenceRequest(HearingDto hearingDto,
IEnumerable<ParticipantDto> participantDtos,
IEnumerable<EndpointDto> endpointDtos)
{
public static BookNewConferenceRequest MapToBookNewConferenceRequest(HearingDto hearingDto,
IEnumerable<ParticipantDto> participantDtos,
IEnumerable<EndpointDto> endpointDtos)
{
var participants = participantDtos
.Select(ParticipantToParticipantRequestMapper.MapToParticipantRequest)
.ToList();
var participants = participantDtos
.Select(ParticipantToParticipantRequestMapper.MapToParticipantRequest)
.ToList();

var request = new BookNewConferenceRequest
{
CaseNumber = hearingDto.CaseNumber,
CaseName = hearingDto.CaseName,
CaseType = hearingDto.CaseType,
ScheduledDuration = hearingDto.ScheduledDuration,
ScheduledDateTime = hearingDto.ScheduledDateTime,
HearingRefId = hearingDto.HearingId,
Participants = participants,
HearingVenueName = hearingDto.HearingVenueName,
AudioRecordingRequired = hearingDto.RecordAudio,
Endpoints = PopulateAddEndpointRequests(endpointDtos, participantDtos).ToList(),
CaseTypeServiceId = hearingDto.CaseTypeServiceId
};

return request;
}

private static List<AddEndpointRequest> PopulateAddEndpointRequests(IEnumerable<EndpointDto> endpointDtos, IEnumerable<ParticipantDto> participantDtos)
var request = new BookNewConferenceRequest
{
var addEndpointRequests = new List<AddEndpointRequest>();
foreach (var endpointDto in endpointDtos)
{
addEndpointRequests.Add(new AddEndpointRequest
{
DefenceAdvocate = participantDtos.SingleOrDefault(x => x.ContactEmail == endpointDto.DefenceAdvocateContactEmail)?.Username,
DisplayName = endpointDto.DisplayName,
Pin = endpointDto.Pin,
SipAddress = endpointDto.Sip
});
}
return addEndpointRequests;
}
CaseNumber = hearingDto.CaseNumber,
CaseName = hearingDto.CaseName,
CaseType = hearingDto.CaseType,
ScheduledDuration = hearingDto.ScheduledDuration,
ScheduledDateTime = hearingDto.ScheduledDateTime,
HearingRefId = hearingDto.HearingId,
Participants = participants,
HearingVenueName = hearingDto.HearingVenueName,
AudioRecordingRequired = hearingDto.RecordAudio,
Endpoints = endpointDtos?.Select(EndpointToRequestMapper.MapToRequest).ToList(),
CaseTypeServiceId = hearingDto.CaseTypeServiceId
};

return request;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@ public class EndpointDto
public string DisplayName { get; set; }
public string Sip { get; set; }
public string Pin { get; set; }
public string DefenceAdvocateContactEmail { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@ public class UpdateConferenceEndpointsRequest
{
public IList<EndpointResponse> ExistingEndpoints { get; set; } = new List<EndpointResponse>();
public IList<EndpointResponse> NewEndpoints { get; set; } = new List<EndpointResponse>();
public IList<Guid> RemovedEndpoints { get; set; } = new List<Guid>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,12 @@ public EndpointAddedHandler(IVideoApiService videoApiService, IVideoWebService v
public async Task HandleAsync(EndpointAddedIntegrationEvent eventMessage)
{
var conference = await _videoApiService.GetConferenceByHearingRefId(eventMessage.HearingId);
ParticipantDetailsResponse defenceAdvocate = null;
if (!string.IsNullOrEmpty(eventMessage.Endpoint.DefenceAdvocateContactEmail))
{
defenceAdvocate = conference.Participants.Single(x => x.ContactEmail ==
eventMessage.Endpoint.DefenceAdvocateContactEmail);
}

await _videoApiService.AddEndpointToConference(conference.Id, new AddEndpointRequest
{
DisplayName = eventMessage.Endpoint.DisplayName,
SipAddress = eventMessage.Endpoint.Sip,
Pin = eventMessage.Endpoint.Pin,
DefenceAdvocate = defenceAdvocate?.Username
Pin = eventMessage.Endpoint.Pin
});

var endpoints = await _videoApiService.GetEndpointsForConference(conference.Id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,9 @@ public async Task HandleAsync(EndpointUpdatedIntegrationEvent eventMessage)
_logger.LogError("Unable to find conference by hearing id {HearingId}", eventMessage.HearingId);
else
{
var defenceAdvocate = await HandleEndpointDefenceAdvocateUpdate(conference, eventMessage);

await _videoApiService.UpdateEndpointInConference(conference.Id, eventMessage.Sip,
new UpdateEndpointRequest
{
DisplayName = eventMessage.DisplayName,
DefenceAdvocate = defenceAdvocate?.Username
});
await HandleLinkedParticipantUpdate(conference, eventMessage);

await _videoApiService.UpdateEndpointInConference(conference.Id, eventMessage.Sip, new UpdateEndpointRequest { DisplayName = eventMessage.DisplayName });

var endpoints = await _videoApiService.GetEndpointsForConference(conference.Id);

Expand All @@ -53,83 +48,53 @@ await _videoApiService.UpdateEndpointInConference(conference.Id, eventMessage.Si
}
}

private async Task<ParticipantDetailsResponse> HandleEndpointDefenceAdvocateUpdate(ConferenceDetailsResponse conference, EndpointUpdatedIntegrationEvent endpointEvent)
private async Task HandleLinkedParticipantUpdate(ConferenceDetailsResponse conference, EndpointUpdatedIntegrationEvent endpointEvent)
{
var newDefenceAdvocate = await FindDefenceAdvocateInConference(conference, endpointEvent);
var endpoints = await _videoApiService.GetEndpointsForConference(conference.Id);
var endpointBeingUpdated = endpoints.SingleOrDefault(x => x.SipAddress == endpointEvent.Sip);

try
{
//Has the defence advocate changed?
if (endpointBeingUpdated is not null &&!ReferenceEquals(endpointBeingUpdated.DefenceAdvocate, endpointEvent.DefenceAdvocate))
await NotifyDefenceAdvocates(conference, endpointEvent, newDefenceAdvocate, endpointBeingUpdated);
if (endpointBeingUpdated is not null)
{
//Push the new linked participant notification to VideoWeb
foreach (var newLinkedParticipant in endpointEvent.EndpointParticipantsAdded)
await _videoWebService.PushLinkedNewParticipantToEndpoint(conference.Id, newLinkedParticipant, endpointEvent.DisplayName);

await NotifyExistingParticipants(conference, endpointEvent, endpointBeingUpdated);
}
}
catch (Exception e)
{
_logger.LogError(e, "Error notifying defence advocates");
}
return newDefenceAdvocate;
}

private async Task<ParticipantDetailsResponse> FindDefenceAdvocateInConference(ConferenceDetailsResponse conference, EndpointUpdatedIntegrationEvent endpointEvent)
{
ParticipantDetailsResponse newDefenceAdvocate = null;

if (!string.IsNullOrEmpty(endpointEvent.DefenceAdvocate))
{
for (var retry = 0; retry <= RetryLimit; retry++)
{
newDefenceAdvocate = GetDefenceAdvocate(conference, endpointEvent.DefenceAdvocate);
if (newDefenceAdvocate is not null)
break;

if (retry == RetryLimit)
throw new ArgumentException(
$"Unable to find defence advocate {endpointEvent.DefenceAdvocate} from EndpointUpdatedIntegrationEvent in conference {conference.Id}");

Thread.Sleep(RetrySleep);
//refresh conference details
conference = await _videoApiService.GetConferenceByHearingRefId(conference.HearingId);
}
}

return newDefenceAdvocate;
}

private async Task NotifyDefenceAdvocates(ConferenceDetailsResponse conference,
EndpointUpdatedIntegrationEvent endpointEvent,
ParticipantDetailsResponse newDefenceAdvocate,
EndpointResponse endpointBeingUpdated)
private async Task NotifyExistingParticipants(ConferenceDetailsResponse conference, EndpointUpdatedIntegrationEvent endpointEvent, EndpointResponse endpointBeingUpdated)
{
//Has a new rep been linked
if (newDefenceAdvocate is not null)
await _videoWebService.PushLinkedNewParticipantToEndpoint(conference.Id, newDefenceAdvocate.Username, endpointEvent.DisplayName);

//Was there a previously linked Rep
if (endpointBeingUpdated.DefenceAdvocate is not null)
foreach (var participantBeingRemoved in endpointEvent.EndpointParticipantsRemoved)
{
var previousDefenceAdvocateName = endpointBeingUpdated.DefenceAdvocate;
var oldDefenceAdvocate = GetDefenceAdvocate(conference, previousDefenceAdvocateName)
?? throw new ArgumentException($"Unable to find defence advocate in participant list {previousDefenceAdvocateName}");
await _videoWebService.PushUnlinkedParticipantFromEndpoint(conference.Id, participantBeingRemoved, endpointEvent.DisplayName);

await _videoWebService.PushUnlinkedParticipantFromEndpoint(conference.Id, oldDefenceAdvocate.Username, endpointEvent.DisplayName);
var oldParticipant = GetParticipant(conference, participantBeingRemoved);

//if old rep is in a private consultation with endpoint, and new rep is not also present in the same room, force closure of the consultation
if (endpointBeingUpdated.Status == EndpointState.InConsultation &&
!IsParticipantIsInPrivateConsultationWithEndpoint(newDefenceAdvocate, endpointBeingUpdated) &&
IsParticipantIsInPrivateConsultationWithEndpoint(oldDefenceAdvocate, endpointBeingUpdated))
//if old rep is in a private consultation with endpoint, check if another linked rep is there, else, force closure of the consultation
if (endpointBeingUpdated.Status == EndpointState.InConsultation && IsParticipantIsInPrivateConsultationWithEndpoint(oldParticipant, endpointBeingUpdated))
{
await _videoApiService.CloseConsultation(conference.Id, oldDefenceAdvocate.Id);
await _videoWebService.PushCloseConsultationBetweenEndpointAndParticipant(conference.Id, oldDefenceAdvocate.Username, endpointEvent.DisplayName);
var otherEndpointParticipants = endpointEvent.EndpointParticipants.Select(x => GetParticipant(conference, x));
if(otherEndpointParticipants.Any(x => IsParticipantIsInPrivateConsultationWithEndpoint(x, endpointBeingUpdated)))
continue;
await _videoApiService.CloseConsultation(conference.Id, oldParticipant.Id);
await _videoWebService.PushCloseConsultationBetweenEndpointAndParticipant(conference.Id, oldParticipant.Username, endpointEvent.DisplayName);
}
}
}

private static ParticipantDetailsResponse GetDefenceAdvocate(ConferenceDetailsResponse conference, string defenceAdvocate)
private static ParticipantDetailsResponse GetParticipant(ConferenceDetailsResponse conference, string username)
{
return conference.Participants.SingleOrDefault(x => x.Username == defenceAdvocate) ??
conference.Participants.SingleOrDefault(x => x.ContactEmail == defenceAdvocate);
return conference.Participants.SingleOrDefault(x => x.Username == username) ??
conference.Participants.SingleOrDefault(x => x.ContactEmail == username);
}

private static bool IsParticipantIsInPrivateConsultationWithEndpoint(ParticipantDetailsResponse participant, EndpointResponse endpoint)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
"net6.0": {
"BookingsApi.Client": {
"type": "Direct",
"requested": "[2.0.11, )",
"resolved": "2.0.11",
"contentHash": "4dJFAlR2hZqpKSKZgRrV6bzGvbXOia45Ej35cEPQhfjGrN66vP2ad184vvFmA9zVdS2jKqw08plKKe8OOraNNw==",
"requested": "[2.0.12-pr-0857-0020, )",
"resolved": "2.0.12-pr-0857-0020",
"contentHash": "oF8q0wZ8MCk2AywAx+DxzGPw40ZGYSocL1M6TjlP00GgkIW0e1kHbrfNcpZ8MRZ3hFhR+TIUwouR18Eu06FsNg==",
"dependencies": {
"Microsoft.AspNetCore.Mvc.Core": "2.2.5"
}
Expand Down Expand Up @@ -74,9 +74,9 @@
},
"VideoApi.Client": {
"type": "Direct",
"requested": "[1.51.6, )",
"resolved": "1.51.6",
"contentHash": "d1KNPOYF/mXiIfik3xSuAsqJV9XKh+sNqrN23QO4a0+3jcGW7YGS2bF2QEIN7pnPllPEIuIsW8a1B4klEboZAw==",
"requested": "[1.51.7-pr-0645-0003, )",
"resolved": "1.51.7-pr-0645-0003",
"contentHash": "+p3+dc5sLg0+rVMlmqIBVitU+fDro6cvw894nw4w3NcqkAstfiPyJ6ykyoPLiFZLAR6GjUayKeh/d4Y4gITMNQ==",
"dependencies": {
"Microsoft.AspNetCore.Mvc.Core": "2.2.5",
"Newtonsoft.Json": "13.0.3"
Expand Down Expand Up @@ -1763,7 +1763,7 @@
"bookingqueuesubscriber.common": {
"type": "Project",
"dependencies": {
"BookingsApi.Client": "[2.0.11, )",
"BookingsApi.Client": "[2.0.12-pr-0857-0020, )",
"LaunchDarkly.ServerSdk": "[7.0.3, )",
"Microsoft.ApplicationInsights": "[2.21.0, )",
"Microsoft.Azure.Functions.Extensions": "[1.1.0, )",
Expand Down
Loading
Loading