/// <summary> /// Processes the task state change. /// </summary> /// <param name="notificationMessage">MediaServicesV2NotificationMessage.</param> /// <param name="jobId">The id of the job.</param> /// <returns>Successfully published message.</returns> private async Task <string> ProcessTaskStateChangeAsync(MediaServicesV2NotificationMessage notificationMessage, string jobId) { _ = notificationMessage ?? throw new ArgumentNullException(nameof(notificationMessage)); if (!notificationMessage.Properties.TryGetValue("newState", out string newState)) { throw new Exception("Could not find NewState property in notification message"); } if (newState.Equals("Scheduled", StringComparison.OrdinalIgnoreCase) || newState.Equals("Processing", StringComparison.OrdinalIgnoreCase)) { return(newState); } if (newState.Equals("Finished", StringComparison.OrdinalIgnoreCase)) { try { var destinationUris = await _mediaServicesV2EncodeOperations.CopyOutputAssetToOutputContainerAsync(jobId).ConfigureAwait(false); } catch (Exception e) { throw new Exception("Failed while CopyOutputAssetToOutputContainerAsync", e); } try { await _mediaServicesV2EncodeOperations.DeleteAssetsForJobAsync(jobId).ConfigureAwait(false); } catch (Exception e) { throw new Exception("Failed while DeleteAssetsForV2JobAsync", e); } return(newState); } if (newState.Equals("Error", StringComparison.OrdinalIgnoreCase)) { await _mediaServicesV2EncodeOperations.DeleteAssetsForJobAsync(jobId).ConfigureAwait(false); throw new Exception("Encode Job Failed"); } if (newState.Equals("Canceled", StringComparison.OrdinalIgnoreCase)) { await _mediaServicesV2EncodeOperations.DeleteAssetsForJobAsync(jobId).ConfigureAwait(false); return(newState); } throw new NotSupportedException($"Unknown newState:{newState}"); }
private static EventGridEvent GetEventFromNotificationMessage(MediaServicesV2NotificationMessage notificationMessage) { var eventGridEventId = System.Guid.NewGuid().ToString(); return(new EventGridEvent { Id = eventGridEventId, Data = JsonConvert.SerializeObject(notificationMessage), EventTime = System.DateTime.UtcNow, EventType = CustomEventTypes.ResponseEncodeMediaServicesV2TranslateCallback, Subject = $"/{CustomEventTypes.ResponseEncodeMediaServicesV2TranslateCallback}/{eventGridEventId}", DataVersion = "1.0", }); }
/// <summary> /// Get the progress. /// </summary> /// <param name="notificationMessage">MediaServicesV2NotificationMessage.</param> /// <returns>Progress percentage.</returns> private static int GetEncodeProgress(MediaServicesV2NotificationMessage notificationMessage) { _ = notificationMessage ?? throw new ArgumentNullException(nameof(notificationMessage)); if (!notificationMessage.Properties.TryGetValue("lastComputedProgress", out string progressString)) { throw new Exception("Could not find LastComputedProgress property in notification message"); } if (!int.TryParse(progressString, out int progress)) { throw new Exception("Could not parse progress from message"); } return(progress); }
public async void HandleAsync_ShouldReturnTrue_WhenTaskStateChangePayloadIsValid(string oldState, string newState, string expectedEventType) { var mediaV2ServiceMock = Mock.Of <IMediaServicesV2EncodeService>(); Mock.Get(mediaV2ServiceMock) .Setup(x => x.GetOperationContextForJobAsync(It.IsAny <string>())) .ReturnsAsync(new JObject()); var egPublisherMock = Mock.Of <IEventGridPublisher>(); dynamic opcontext = new JObject(); opcontext.TestKey = "TestValue"; Mock.Get(egPublisherMock) .Setup(x => x.PublishEventToTopic(It.IsAny <EventGridEvent>())) .ReturnsAsync(true); var handler = new MediaServicesV2CallbackHandler(log, mediaV2ServiceMock, egPublisherMock); var notificationMessage = new MediaServicesV2NotificationMessage() { ETag = "random", EventType = MediaServicesV2NotificationEventType.TaskStateChange, MessageVersion = "1.1", TimeStamp = DateTime.UtcNow, Properties = new Dictionary <string, string>() { { "jobId", "nb:jid:UUID:91ab2b3a-0d00-a812-94e1-f1ea63c713e1" }, { "taskId", "nb:tid:UUID:4c1e6189-cb33-45ca-901a-46dcd3cc5f40" }, { "newState", newState }, { "oldState", oldState }, { "accountName", "justatest" }, { "accountId", "00000000-0000-0000-0000-000000000000" }, { "notificationEndPointId", "nb:nepid:UUID:738eb6b5-a18d-4b7f-961d-bc33e844da13" } } }; var actual = await handler.HandleAsync(GetEventFromNotificationMessage(notificationMessage)).ConfigureAwait(false); actual.ShouldBeTrue(); Mock.Get(egPublisherMock) .Verify(x => x.PublishEventToTopic(It.Is <EventGridEvent>(x => x.EventType == expectedEventType)), Times.Once, "publish should be called with the correct eventtype"); }
/// <inheritdoc/> public async Task <(string JobId, string Status)> HandleNotificationAsync(MediaServicesV2NotificationMessage notificationMessage) { _ = notificationMessage ?? throw new ArgumentNullException(nameof(notificationMessage)); var jobId = notificationMessage.Properties["jobId"]; if (string.IsNullOrWhiteSpace(jobId)) { throw new ArgumentException("notificationMessage.jobId is invalid"); } string newState; try { switch (notificationMessage.EventType) { case MediaServicesV2NotificationEventType.TaskStateChange: newState = await ProcessTaskStateChangeAsync(notificationMessage, jobId).ConfigureAwait(false); break; case MediaServicesV2NotificationEventType.TaskProgress: newState = $"Progress:{GetEncodeProgress(notificationMessage)}"; break; default: _log.LogInformation(notificationMessage.EventType.ToString()); throw new NotSupportedException($"Unsupported AMSv2 event type {notificationMessage.EventType}"); } } catch (Exception e) { var msg = $"Failed while parsing notifiation message for {jobId}."; _log.LogError(msg, e); throw new Exception(msg, e); } return(jobId, newState); }
public async void HandleAsync_ShouldPublishFailureEvent_WhenPayloadIsNotFormatedCorrectly() { var mediaV2ServiceMock = Mock.Of <IMediaServicesV2EncodeService>(); var egPublisherMock = Mock.Of <IEventGridPublisher>(); dynamic opcontext = new JObject(); opcontext.TestKey = "TestValue"; Mock.Get(egPublisherMock) .Setup(x => x.PublishEventToTopic(It.IsAny <EventGridEvent>())) .ReturnsAsync(true); var handler = new MediaServicesV2CallbackHandler(log, mediaV2ServiceMock, egPublisherMock); var notificationMessage = new MediaServicesV2NotificationMessage() { ETag = "random", EventType = MediaServicesV2NotificationEventType.TaskStateChange, MessageVersion = "1.1", TimeStamp = DateTime.UtcNow, Properties = new Dictionary <string, string>() { { "jobId", "nb:jid:UUID:91ab2b3a-0d00-a812-94e1-f1ea63c713e1" }, { "taskId", "nb:tid:UUID:4c1e6189-cb33-45ca-901a-46dcd3cc5f40" }, { "noState", "Wrong" }, { "oldState", "OldState" }, { "accountName", "justatest" }, { "accountId", "00000000-0000-0000-0000-000000000000" }, { "notificationEndPointId", "nb:nepid:UUID:738eb6b5-a18d-4b7f-961d-bc33e844da13" } } }; await handler.HandleAsync(GetEventFromNotificationMessage(notificationMessage)).ConfigureAwait(false); Mock.Get(egPublisherMock) .Verify(x => x.PublishEventToTopic(It.Is <EventGridEvent>(e => e.EventType == CustomEventTypes.ResponseFailure))); }
/// <summary> /// Initializes a new instance of the <see cref="GridwichMediaServicesV2MessageProcessingException" /> class. /// </summary> /// <param name="notificationMessage">The notification message.</param> /// <param name="message">The base exception message you want to set.</param> /// <param name="logEventId">The last LogEventId that was logged relevant to this exception.</param> /// <param name="operationContext">The OperationContext for this exception.</param> /// <param name="innerException">The cause behind this exception.</param> public GridwichMediaServicesV2MessageProcessingException(MediaServicesV2NotificationMessage notificationMessage, string message, EventId logEventId, JObject operationContext, Exception innerException) : base(message, logEventId, operationContext, innerException) { SafeAddToData(MessageKey, notificationMessage); }
public async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { _logger.LogEvent(LogEventIds.FunctionAppShuttingDown, LogEventIds.FunctionAppShuttingDown.Name); throw new OperationCanceledException("Function invoked with a canceled cancellation token."); } try { cancellationToken.Register(() => { _logger.LogEvent(LogEventIds.FunctionAppShuttingDown, LogEventIds.FunctionAppShuttingDown.Name); }); _logger.LogEvent(LogEventIds.CallbackFunctionTriggered, $"C# HTTP trigger function processed a request. Path={req?.Path}"); // TODO: not sure why we have to get the byte array.. might be legacy code // MemoryStream stream = new MemoryStream(); // await req.Body.CopyToAsync(stream); // byte[] bodyByteArray = stream.ToArray(); string requestBody; using (var sr = new StreamReader(req.Body)) { requestBody = await sr.ReadToEndAsync().ConfigureAwait(false); } if (req.Headers.TryGetValue("ms-signature", out _)) { // TODO: need to verify headers here. However not sure how to get the signing key. MediaServicesV2NotificationMessage notificationMessage = JsonConvert.DeserializeObject <MediaServicesV2NotificationMessage>(requestBody); var eventGridEventId = System.Guid.NewGuid().ToString(); var eventToPublish = new Microsoft.Azure.EventGrid.Models.EventGridEvent { Id = eventGridEventId, Data = notificationMessage, EventTime = System.DateTime.UtcNow, EventType = CustomEventTypes.ResponseEncodeMediaServicesV2TranslateCallback, Subject = $"/{CustomEventTypes.ResponseEncodeMediaServicesV2TranslateCallback}/{eventGridEventId}", DataVersion = "1.0", }; await _publisher.PublishEventToTopic(eventToPublish).ConfigureAwait(false); _logger.LogEvent(LogEventIds.CallbackFunctionNotificationMessageProcessed, "processed notification message"); } else { _logger.LogEvent(LogEventIds.RequestIsMissingVerifyWebHookRequestSignature, "VerifyWebHookRequestSignature failed."); return(new BadRequestObjectResult("VerifyWebHookRequestSignature failed.")); } return(new OkObjectResult("OK")); } catch (OperationCanceledException oce) { _logger.LogEventObject(LogEventIds.OperationCancelException, oce); throw; } }