static CouldNotSendOnAnyPreparedChannelEventTest()
        {
            ConstructorArgumentValidationTestScenarios
            .AddScenario(() =>
                         new ConstructorArgumentValidationTestScenario <CouldNotSendOnAnyPreparedChannelEvent>
            {
                Name             = "constructor should throw ArgumentOutOfRangeException when parameter 'attemptToSendNotificationResult' GetOutcome() is not AttemptToSendNotificationOutcome.CouldNotSendOnAnyPreparedChannel",
                ConstructionFunc = () =>
                {
                    var referenceObject = A.Dummy <CouldNotSendOnAnyPreparedChannelEvent>();

                    var result = new CouldNotSendOnAnyPreparedChannelEvent(
                        referenceObject.Id,
                        referenceObject.TimestampUtc,
                        A.Dummy <AttemptToSendNotificationResult>().ThatIs(_ => _.GetOutcome() != AttemptToSendNotificationOutcome.CouldNotSendOnAnyPreparedChannel));

                    return(result);
                },
                ExpectedExceptionType            = typeof(ArgumentOutOfRangeException),
                ExpectedExceptionMessageContains = new[] { "attemptToSendNotificationOutcome", "CouldNotSendOnAnyPreparedChannel" },
            });
        }
Beispiel #2
0
        /// <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);
        }