Skip to content

Commit

Permalink
VIH-11294 capture UnexpectedEventOrderException and log it only (#720)
Browse files Browse the repository at this point in the history
* VIH-11294 capture UnexpectedEventOrderException and log it only
add an API test for this scenario

* Update to allow models to be updated regardless of timestamp
  • Loading branch information
shaed-parkar authored Feb 14, 2025
1 parent f81f203 commit 4d9b7d9
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 29 deletions.
7 changes: 6 additions & 1 deletion VideoApi/VideoApi.Common/Helpers/TraceCategory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,12 @@ public enum TraceCategory
/// <summary>
/// Supplier API Exception
/// </summary>
SupplierApiException
SupplierApiException,

/// <summary>
/// Callback Event Exception
/// </summary>
CallbackEventException

}
}
6 changes: 3 additions & 3 deletions VideoApi/VideoApi.Events/Handlers/Core/EventHandlerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ protected void ValidateParticipantEventReceivedAfterLastUpdate(CallbackEvent cal
if (!eventReceivedAfterLastUpdate) return;
var innerException = new InvalidOperationException(
$"Participant {SourceParticipant.Id} has already been updated since this event {callbackEvent.EventType} with the time {callbackEvent.TimeStampUtc}. Current Status: {SourceParticipant.GetCurrentStatus()}");
throw new UnexpectedEventOrderException(callbackEvent, innerException);
Logger.LogError(new UnexpectedEventOrderException(callbackEvent, innerException), "Unexpected event order for participant");
}

protected void ValidateJvsEventReceivedAfterLastUpdate(CallbackEvent callbackEvent)
Expand All @@ -72,7 +72,7 @@ protected void ValidateJvsEventReceivedAfterLastUpdate(CallbackEvent callbackEve
if(!eventReceivedAfterLastUpdate) return;
var innerException = new InvalidOperationException(
$"Endpoint {SourceEndpoint.Id} has already been updated since this event {callbackEvent.EventType} with the time {callbackEvent.TimeStampUtc}. Current Status: {SourceEndpoint.State}");
throw new UnexpectedEventOrderException(callbackEvent, innerException);
Logger.LogError(new UnexpectedEventOrderException(callbackEvent, innerException), "Unexpected event order for endpoint");
}

protected void ValidateTelephoneParticipantEventReceivedAfterLastUpdate(CallbackEvent callbackEvent)
Expand All @@ -82,7 +82,7 @@ protected void ValidateTelephoneParticipantEventReceivedAfterLastUpdate(Callback
if(!eventReceivedAfterLastUpdate) return;
var innerException = new InvalidOperationException(
$"TelephoneParticipant {SourceTelephoneParticipant.Id} has already been updated since this event {callbackEvent.EventType} with the time {callbackEvent.TimeStampUtc}. Current Status: {SourceTelephoneParticipant.State}");
throw new UnexpectedEventOrderException(callbackEvent, innerException);
Logger.LogError(new UnexpectedEventOrderException(callbackEvent, innerException), "Unexpected event order for telephone participant");
}
}
}
14 changes: 14 additions & 0 deletions VideoApi/VideoApi.IntegrationTests/Features/Callbacks.feature
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,20 @@ Feature: Callbacks
| EndpointDisconnected | Disconnected |
| EndpointTransfer | InConsultation |

Scenario Outline: Should accept and process an old endpoint event request
Given I have a conference with endpoints
And I have a valid conference event request for event type <EventType1>
When I send the request to the endpoint
And I have a valid conference event request for event type <EventType2>
When I send the request to the endpoint
And I have an old valid conference event request for event type <EventType3>
When I send the request to the endpoint
Then the response should have the status NoContent and success status True
And the endpoint status should be <EndpointStatus>
Examples:
| EventType1 | EventType2 | EventType3 | EndpointStatus |
| EndpointJoined | EndpointJoined | EndpointDisconnected | Disconnected |

Scenario Outline: Should accept and process a phone event request
Given I have a conference
And I have a participant consultation room
Expand Down
13 changes: 13 additions & 0 deletions VideoApi/VideoApi.IntegrationTests/Steps/CallbacksSteps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public CallbackSteps(TestContext context)
}

[Given(@"I have a valid conference event request for event type (.*)")]
[When(@"I have a valid conference event request for event type (.*)")]
public void GivenIHaveAnConferenceEventRequestForAnEventType(EventType eventType)
{
var conference = _context.Test.Conference;
Expand All @@ -40,6 +41,18 @@ public void GivenIHaveAnConferenceEventRequestForAnEventType(EventType eventType
_context.HttpContent = new StringContent(jsonBody, Encoding.UTF8, "application/json");
}

[When(@"I have an old valid conference event request for event type (.*)")]
public void GivenIHaveAnOldConferenceEventRequestForAnEventType(EventType eventType)
{
var conference = _context.Test.Conference;
var request = BuildRequest(eventType, conference);
request.TimeStampUtc = DateTime.UtcNow.AddMinutes(-1);
_context.Uri = EventsEndpoints.Event;
_context.HttpMethod = HttpMethod.Post;
var jsonBody = ApiRequestHelper.Serialise(request);
_context.HttpContent = new StringContent(jsonBody, Encoding.UTF8, "application/json");
}

[Given(@"I have a transfer judge into consultation room event")]
public void GivenIHaveTransferToAJudgeIntoConsultationRoomEvent()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Moq;
using VideoApi.DAL.Commands;
using VideoApi.Domain.Enums;
using VideoApi.Events.Exceptions;
using VideoApi.Events.Handlers;
using VideoApi.Events.Models;

Expand Down Expand Up @@ -65,7 +63,7 @@ public async Task Should_send_disconnect_messages_to_participants_and_service_bu
}

[Test]
public void should_not_update_participant_status_if_participant_has_been_updated_since_callback_received()
public async Task should_still_update_participant_status_if_participant_has_been_updated_since_callback_received()
{
var conference = TestConference;
var participantForEvent = conference.GetParticipants().First(x => x.UserRole == UserRole.Individual);
Expand All @@ -81,25 +79,20 @@ public void should_not_update_participant_status_if_participant_has_been_updated
TimeStampUtc = DateTime.UtcNow.AddSeconds(-1)
};


var exception = Assert.ThrowsAsync<UnexpectedEventOrderException>(() => _sut.HandleAsync(callbackEvent));
Assert.That(exception!.CallbackEvent, Is.EqualTo(callbackEvent));
Assert.That(exception!.InnerException!.Message,
Is.EqualTo(
$"Participant {participantForEvent.Id} has already been updated since this event {callbackEvent.EventType} with the time {callbackEvent.TimeStampUtc}. Current Status: {participantForEvent.GetCurrentStatus()}"));
await _sut.HandleAsync(callbackEvent);

CommandHandlerMock.Verify(
x => x.Handle(It.Is<UpdateParticipantStatusAndRoomCommand>(command =>
command.ConferenceId == conference.Id &&
command.ParticipantId == participantForEvent.Id &&
command.ParticipantState == ParticipantState.Disconnected &&
command.Room == null)), Times.Never);
command.Room == null)), Times.Once);

CommandHandlerMock.Verify(
x => x.Handle(It.Is<AddTaskCommand>(command =>
command.ConferenceId == conference.Id &&
command.OriginId == participantForEvent.Id &&
command.TaskType == TaskType.Participant)), Times.Never);
command.TaskType == TaskType.Participant)), Times.Once);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using Moq;
using VideoApi.DAL.Commands;
using VideoApi.Domain.Enums;
using VideoApi.Events.Exceptions;
using VideoApi.Events.Handlers;
using VideoApi.Events.Models;

Expand Down Expand Up @@ -38,7 +37,7 @@ public async Task Should_update_endpoint_status_to_disconnected()
}

[Test]
public void should_not_update_endpoint_status_if_endpoint_has_been_updated_since_callback_received()
public async Task should_still_update_endpoint_status_if_endpoint_has_been_updated_since_callback_received()
{
var conference = TestConference;
var participantForEvent = conference.GetEndpoints()[0];
Expand All @@ -53,11 +52,12 @@ public void should_not_update_endpoint_status_if_endpoint_has_been_updated_since
TimeStampUtc = DateTime.UtcNow.AddSeconds(-1)
};

var exception = Assert.ThrowsAsync<UnexpectedEventOrderException>(() => _sut.HandleAsync(callbackEvent));
Assert.That(exception!.CallbackEvent, Is.EqualTo(callbackEvent));
Assert.That(exception!.InnerException!.Message,
Is.EqualTo(
$"Endpoint {participantForEvent.Id} has already been updated since this event {callbackEvent.EventType} with the time {callbackEvent.TimeStampUtc}. Current Status: {participantForEvent.State}"));
await _sut.HandleAsync(callbackEvent);

CommandHandlerMock.Verify(
x => x.Handle(It.Is<UpdateEndpointStatusAndRoomCommand>(command =>
command.ConferenceId == conference.Id && command.EndpointId == participantForEvent.Id &&
command.Status == EndpointState.Disconnected && command.Room == null)), Times.Once);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using Moq;
using VideoApi.DAL.Commands;
using VideoApi.Domain.Enums;
using VideoApi.Events.Exceptions;
using VideoApi.Events.Handlers;
using VideoApi.Events.Models;

Expand Down Expand Up @@ -38,7 +37,7 @@ public async Task Should_remove_telephone_participant()
}

[Test]
public void should_throw_exception_if_telephone_participant_has_been_updated_since_callback_received()
public async Task should_still_update_if_telephone_participant_has_been_updated_since_callback_received()
{
var conference = TestConference;
var telephoneParticipant = TestConference.GetTelephoneParticipants()[0];
Expand All @@ -55,9 +54,10 @@ public void should_throw_exception_if_telephone_participant_has_been_updated_sin
Reason = "Telephone User Disconnected"
};

var exception = Assert.ThrowsAsync<UnexpectedEventOrderException>(() => _sut.HandleAsync(callbackEvent));
Assert.That(exception!.CallbackEvent, Is.EqualTo(callbackEvent));
Assert.That(exception!.InnerException!.Message,
Is.EqualTo($"TelephoneParticipant {telephoneParticipant.Id} has already been updated since this event {callbackEvent.EventType} with the time {callbackEvent.TimeStampUtc}. Current Status: {telephoneParticipant.State}"));
await _sut.HandleAsync(callbackEvent);

CommandHandlerMock.Verify(
x => x.Handle(It.Is<RemoveTelephoneParticipantCommand>(command =>
command.ConferenceId == conference.Id && command.TelephoneParticipantId == telephoneParticipant.Id)), Times.Once);
}
}
11 changes: 10 additions & 1 deletion VideoApi/VideoApi/Extensions/ExceptionMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using VideoApi.Common.Helpers;
using VideoApi.DAL.Exceptions;
using VideoApi.Domain.Validations;
using VideoApi.Events.Exceptions;
using VideoApi.Services.Clients;

namespace VideoApi.Extensions
Expand Down Expand Up @@ -46,7 +47,8 @@ public async Task InvokeAsync(HttpContext httpContext)
}
catch (EntityNotFoundException ex)
{
ApplicationLogger.TraceException(TraceCategory.APIException.ToString(), "Entity Not Found", ex, null, null);
ApplicationLogger.TraceException(TraceCategory.APIException.ToString(), "Entity Not Found", ex, null,
null);
await HandleExceptionAsync(httpContext, HttpStatusCode.NotFound, ex);
}
catch (VideoDalException ex)
Expand All @@ -73,6 +75,13 @@ public async Task InvokeAsync(HttpContext httpContext)
await HandleExceptionAsync(httpContext, ex.StatusCode, ex);
}
}
catch (UnexpectedEventOrderException ex)
{
ApplicationLogger.TraceException(TraceCategory.CallbackEventException.ToString(),
"Unexpected Event Order Exception", ex, null, null);
httpContext.Response.ContentType = "application/json";
httpContext.Response.StatusCode = (int) HttpStatusCode.NoContent;
}
catch (Exception ex)
{
ApplicationLogger.TraceException(TraceCategory.APIException.ToString(), "API Exception", ex, null,
Expand Down

0 comments on commit 4d9b7d9

Please sign in to comment.