static ChannelOperationOutcomeInfoTest() { ConstructorArgumentValidationTestScenarios .AddScenario(() => new ConstructorArgumentValidationTestScenario <ChannelOperationOutcomeInfo> { Name = "constructor should throw ArgumentOutOfRangeException when parameter 'outcome' is ChannelOperationOutcome.Unknown", ConstructionFunc = () => { var referenceObject = A.Dummy <ChannelOperationOutcomeInfo>(); var result = new ChannelOperationOutcomeInfo( referenceObject.ChannelOperationTrackingCodeId, referenceObject.EventType, ChannelOperationOutcome.Unknown); return(result); }, ExpectedExceptionType = typeof(ArgumentOutOfRangeException), ExpectedExceptionMessageContains = new[] { "Unknown", }, }); }
/// <inheritdoc /> public override async Task ExecuteAsync( HandleRecordOp <ExecuteOpRequestedEvent <long, ProcessSendNotificationSagaOp> > operation) { new { operation }.AsArg().Must().NotBeNull(); // Pull some info out of the operation. var executeOpRequestedEvent = operation.RecordToHandle.Payload; var processSendNotificationSagaOp = executeOpRequestedEvent.Operation; var inheritableTags = (operation.RecordToHandle.Metadata.Tags?.DeepClone() ?? new List <NamedValue <string> >()).ToList(); // Poll for failure event var channels = processSendNotificationSagaOp.ChannelToOperationsMonitoringInfoMap.Keys; var channelToOperationsOutcomeInfoMap = new Dictionary <IDeliveryChannel, IReadOnlyCollection <ChannelOperationOutcomeInfo> >(); foreach (var channel in channels) { new { this.channelToEventStreamMap }.AsOp().Must().ContainKey(channel, Invariant($"Looking for success/failure events on event stream for chanel {channel.GetType().ToStringReadable()} but there is no event stream associated with that channel.")); var eventStream = this.channelToEventStreamMap[channel]; var operationsMonitoringInfo = processSendNotificationSagaOp.ChannelToOperationsMonitoringInfoMap[channel]; var operationsOutcomeInfo = new List <ChannelOperationOutcomeInfo>(); channelToOperationsOutcomeInfoMap.Add(channel, operationsOutcomeInfo); foreach (var operationMonitoringInfo in operationsMonitoringInfo) { ChannelOperationOutcomeInfo operationOutcomeInfo; var failureEventMetadata = await eventStream.GetLatestRecordMetadataByIdAsync( operationMonitoringInfo.ChannelOperationTrackingCodeId, operationMonitoringInfo.FailedEventType, recordNotFoundStrategy : RecordNotFoundStrategy.ReturnDefault); if (failureEventMetadata != null) { // Merge-in the tags on the failure event. AddMissingTags(inheritableTags, failureEventMetadata.Tags); operationOutcomeInfo = new ChannelOperationOutcomeInfo(operationMonitoringInfo.ChannelOperationTrackingCodeId, operationMonitoringInfo.FailedEventType, ChannelOperationOutcome.Failed); } else { var successEventMetadata = await eventStream.GetLatestRecordMetadataByIdAsync( operationMonitoringInfo.ChannelOperationTrackingCodeId, operationMonitoringInfo.SucceededEventType, recordNotFoundStrategy : RecordNotFoundStrategy.ReturnDefault); if (successEventMetadata != null) { // Merge-in the tags on the success event. AddMissingTags(inheritableTags, successEventMetadata.Tags); operationOutcomeInfo = new ChannelOperationOutcomeInfo(operationMonitoringInfo.ChannelOperationTrackingCodeId, operationMonitoringInfo.SucceededEventType, ChannelOperationOutcome.Succeeded); } else { // Neither succeeded nor failed, cancel the run and let this handler try again in the future. throw new SelfCancelRunningExecutionException(Invariant($"Notification '{executeOpRequestedEvent.Id}' is being sent on the '{channel.GetType().ToStringReadable()}' channel via channel-operation '{operationMonitoringInfo.ChannelOperationTrackingCodeId}', however the events that indicate whether the operation succeeded ('{operationMonitoringInfo.SucceededEventType}') or failed ('{operationMonitoringInfo.FailedEventType}') do not exist in the channel event stream. Is the operation still executing? Cancelling the handling of this Saga operation.")); } } operationsOutcomeInfo.Add(operationOutcomeInfo); } } // Write the AttemptToSendNotificationEventBase to the Event Stream. var notificationTrackingCodeId = processSendNotificationSagaOp.NotificationTrackingCodeId; var attemptToSendNotificationResult = new AttemptToSendNotificationResult(channelToOperationsOutcomeInfoMap); var attemptToSendNotificationOutcome = attemptToSendNotificationResult.GetOutcome(); AttemptToSendNotificationEventBase attemptToSendNotificationEvent; switch (attemptToSendNotificationOutcome) { case AttemptToSendNotificationOutcome.SentOnAllPreparedChannels: attemptToSendNotificationEvent = new SentOnAllPreparedChannelsEvent(notificationTrackingCodeId, DateTime.UtcNow, attemptToSendNotificationResult); break; case AttemptToSendNotificationOutcome.SentOnSomePreparedChannels: attemptToSendNotificationEvent = new SentOnSomePreparedChannelsEvent(notificationTrackingCodeId, DateTime.UtcNow, attemptToSendNotificationResult); break; case AttemptToSendNotificationOutcome.CouldNotSendOnAnyPreparedChannel: attemptToSendNotificationEvent = new CouldNotSendOnAnyPreparedChannelEvent(notificationTrackingCodeId, DateTime.UtcNow, attemptToSendNotificationResult); break; default: throw new NotSupportedException(Invariant($"This {nameof(AttemptToSendNotificationOutcome)} is not supported: {attemptToSendNotificationOutcome}.")); } var tags = this.buildAttemptToSendNotificationEventTagsProtocol.ExecuteBuildTags(notificationTrackingCodeId, attemptToSendNotificationEvent, inheritableTags); await this.notificationEventStream.PutWithIdAsync(notificationTrackingCodeId, attemptToSendNotificationEvent, tags, ExistingRecordStrategy.DoNotWriteIfFoundByIdAndType); }