Skip to content

Commit

Permalink
Merge pull request #68 from hmcts/feature/VIH-4611-vho-call-event
Browse files Browse the repository at this point in the history
Feature/vih 4611 vho call event
  • Loading branch information
shaed-parkar authored Aug 28, 2019
2 parents 4102fb6 + 649a252 commit 6a90507
Show file tree
Hide file tree
Showing 25 changed files with 717 additions and 220 deletions.
1 change: 1 addition & 0 deletions VideoAPI/Testing.Common/Helper/ApiUriFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public class ConsultationEndpoints

public string HandleConsultationRequest => $"{ApiRoot}";
public string LeaveConsultationRequest => $"{ApiRoot}/leave";
public string RespondToAdminConsultationRequest => $"{ApiRoot}/vhofficer/respond";

}

Expand Down
39 changes: 39 additions & 0 deletions VideoAPI/Video.API/Controllers/ConsultationController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ await NotifyConsultationResponse(conference, requestedBy, requestedFor,
[HttpPost("leave")]
[SwaggerOperation(OperationId = "LeavePrivateConsultation")]
[ProducesResponseType((int) HttpStatusCode.NoContent)]
[ProducesResponseType((int) HttpStatusCode.NotFound)]
[ProducesResponseType((int) HttpStatusCode.BadRequest)]
public async Task<IActionResult> LeavePrivateConsultation(LeaveConsultationRequest request)
{
Expand Down Expand Up @@ -159,6 +160,44 @@ public async Task<IActionResult> LeavePrivateConsultation(LeaveConsultationReque
return NoContent();
}

[HttpPost("vhofficer/respond")]
[SwaggerOperation(OperationId = "RespondToAdminConsultationRequest")]
[ProducesResponseType((int) HttpStatusCode.NoContent)]
[ProducesResponseType((int) HttpStatusCode.NotFound)]
[ProducesResponseType((int) HttpStatusCode.BadRequest)]
public async Task<IActionResult> RespondToAdminConsultationRequest(AdminConsultationRequest request)
{
_logger.LogDebug($"RespondToAdminConsultationRequest");

var getConferenceByIdQuery = new GetConferenceByIdQuery(request.ConferenceId);
var conference =
await _queryHandler.Handle<GetConferenceByIdQuery, Conference>(getConferenceByIdQuery);

if (conference == null)
{
_logger.LogError($"Unable to find conference {request.ConferenceId}");
return NotFound();
}

var participant = conference.GetParticipants().SingleOrDefault(x => x.Id == request.ParticipantId);
if (participant == null)
{
_logger.LogError($"Unable to find participant request by with id {request.ParticipantId}");
return NotFound();
}

if (request.Answer.Value == ConsultationAnswer.Accepted)
{
await _videoPlatformService.TransferParticipantAsync(conference.Id, participant.Id,
participant.CurrentRoom.Value, request.ConsultationRoom);
await _hubContext.Clients.Group(participant.Username.ToLowerInvariant()).AdminConsultationMessage
(conference.Id, request.ConsultationRoom, participant.Username.ToLowerInvariant(),
ConsultationAnswer.Accepted);
}

return NoContent();
}

/// <summary>
/// This method raises a notification to the requestee informing them of an incoming consultation request
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Collections.Generic;
using FluentValidation;
using VideoApi.Contract.Requests;
using VideoApi.Domain.Enums;

namespace Video.API.Validations
{
public class AdminConsultationRequestValidation : AbstractValidator<AdminConsultationRequest>
{
public static readonly string NoConferenceIdErrorMessage = "ConferenceId is required";
public static readonly string NoParticipantIdErrorMessage = "ParticipantId is required";
public static readonly string NoAnswerErrorMessage = "Answer to request is required";
public static readonly string NotValidConsultationRoomMessage = "Room must be a consultation room";

public AdminConsultationRequestValidation()
{
RuleFor(x => x.ConferenceId).NotEmpty().WithMessage(NoConferenceIdErrorMessage);
RuleFor(x => x.ParticipantId).NotEmpty().WithMessage(NoParticipantIdErrorMessage);
RuleFor(x => x.Answer).NotEqual(ConsultationAnswer.None).WithMessage(NoAnswerErrorMessage);
RuleFor(x => x.ConsultationRoom).Custom((type, context) =>
{
var validRooms = new List<RoomType> {RoomType.ConsultationRoom1, RoomType.ConsultationRoom2};
if (!validRooms.Contains(type))
{
context.AddFailure("ConsultationRoom", NotValidConsultationRoomMessage);
}
});
}
}
}
33 changes: 33 additions & 0 deletions VideoAPI/VideoApi.Contract/Requests/AdminConsultationRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.ComponentModel.DataAnnotations;
using VideoApi.Domain.Enums;

namespace VideoApi.Contract.Requests
{
/// <summary>
/// Request a private consultation with another participant
/// </summary>
public class AdminConsultationRequest
{
/// <summary>
/// The conference UUID
/// </summary>
public Guid ConferenceId { get; set; }

/// <summary>
/// UUID of participant VH Officer attempted to call
/// </summary>
public Guid ParticipantId { get; set; }

/// <summary>
/// Consultation Room to use
/// </summary>
public RoomType ConsultationRoom { get; set; }

/// <summary>
/// Response to a consultation request (i.e. 'Accepted or Rejected')
/// </summary>
[EnumDataType(typeof(ConsultationAnswer))]
public ConsultationAnswer? Answer { get; set; }
}
}
2 changes: 1 addition & 1 deletion VideoAPI/VideoApi.Contract/Requests/ConsultationRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class ConsultationRequest
[EnumDataType(typeof(ConsultationAnswer))]
public ConsultationAnswer? Answer { get; set; }
}

public enum ConsultationAnswer
{
/// <summary>
Expand Down
3 changes: 2 additions & 1 deletion VideoAPI/VideoApi.Domain/Enums/EventType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public enum EventType
MediaPermissionDenied,
ParticipantJoining,
SelfTestFailed,
Suspend
Suspend,
VhoCall
}
}
1 change: 0 additions & 1 deletion VideoAPI/VideoApi.Events/Handlers/Core/EventHandlerBase.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using VideoApi.DAL.Commands.Core;
using VideoApi.DAL.Exceptions;
Expand Down
43 changes: 43 additions & 0 deletions VideoAPI/VideoApi.Events/Handlers/VhOfficerCallEventHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using VideoApi.DAL.Commands.Core;
using VideoApi.DAL.Queries.Core;
using VideoApi.Domain.Enums;
using VideoApi.Events.Handlers.Core;
using VideoApi.Events.Hub;
using VideoApi.Events.Models;
using VideoApi.Events.ServiceBus;

namespace VideoApi.Events.Handlers
{
public class VhOfficerCallEventHandler : EventHandlerBase
{
public VhOfficerCallEventHandler(IQueryHandler queryHandler, ICommandHandler commandHandler,
IServiceBusQueueClient serviceBusQueueClient, IHubContext<EventHub, IEventHubClient> hubContext) : base(
queryHandler, commandHandler, serviceBusQueueClient, hubContext)
{
}

public override EventType EventType => EventType.VhoCall;

protected override async Task PublishStatusAsync(CallbackEvent callbackEvent)
{
var targetRoom = ValidationConsultationRoom(callbackEvent);
await HubContext.Clients.Group(SourceParticipant.Username.ToLowerInvariant())
.AdminConsultationMessage(SourceConference.Id, targetRoom,
SourceParticipant.Username.ToLowerInvariant());
}

private RoomType ValidationConsultationRoom(CallbackEvent callbackEvent)
{
if (!callbackEvent.TransferTo.HasValue || callbackEvent.TransferTo.Value != RoomType.ConsultationRoom1
&& callbackEvent.TransferTo.Value != RoomType.ConsultationRoom2)
{
throw new ArgumentException("No consultation room provided");
}

return callbackEvent.TransferTo.Value;
}
}
}
4 changes: 4 additions & 0 deletions VideoAPI/VideoApi.Events/Hub/EventHubClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using VideoApi.Contract.Requests;
using VideoApi.Domain.Enums;
using VideoApi.Services;

Expand All @@ -12,6 +13,9 @@ public interface IEventHubClient
Task ParticipantStatusMessage(string email, ParticipantState participantState);
Task ConferenceStatusMessage(Guid conferenceId, ConferenceState conferenceState);
Task ConsultationMessage(Guid conferenceId, string requestedBy, string requestedFor, string result);

Task AdminConsultationMessage(Guid conferenceId, RoomType room, string requestedFor,
ConsultationAnswer? answer = null);
Task HelpMessage(Guid conferenceId, string participantName);
}

Expand Down
1 change: 1 addition & 0 deletions VideoAPI/VideoApi.Events/VideoApi.Events.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

<ItemGroup>
<ProjectReference Include="..\VideoApi.Common\VideoApi.Common.csproj" />
<ProjectReference Include="..\VideoApi.Contract\VideoApi.Contract.csproj" />
<ProjectReference Include="..\VideoApi.DAL\VideoApi.DAL.csproj" />
<ProjectReference Include="..\VideoApi.Domain\VideoApi.Domain.csproj" />
<ProjectReference Include="..\VideoApi.Services\VideoApi.Services.csproj" />
Expand Down
105 changes: 0 additions & 105 deletions VideoAPI/VideoApi.IntegrationTests/Api/Consultations.feature

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Feature: Respond to Admin Consultations
In order to manage private consultations with the VH Admin team
As an API service
I want to respond to private consultations with the VH Admin team

Scenario: Respond to a VH Officer Consultation with an invalid request
Given I have an invalid respond to admin consultation request
When I send the request to the endpoint
Then the response should have the status BadRequest and success status False
And the error response message should also contain 'ConferenceId is required'
And the error response message should also contain 'ParticipantId is required'
And the error response message should also contain 'Room must be a consultation room'
And the error response message should also contain 'Answer to request is required'

Scenario: Respond to a VH Officer Consultation for a non-existent conference
Given I have a conference
Given I have a nonexistent respond to admin consultation request
When I send the request to the endpoint
Then the response should have the status NotFound and success status False

Scenario: Respond to a VH Officer Consultation for a non-existent participant
Given I have a conference
Given I have a respond to admin consultation request with a non-existent participant
When I send the request to the endpoint
Then the response should have the status NotFound and success status False

Scenario: Respond to a VH Officer Consultation successfully
Given I have a conference
And I have a valid respond to admin consultation request
When I send the request to the endpoint
Then the response should have the status NoContent and success status True
Loading

0 comments on commit 6a90507

Please sign in to comment.