private static async Task HandleUnhandledObjectOfTypeAsync <TObject>(
            FileStandardStream stream,
            string concern,
            HandleRecordAsyncSpecificProtocolBase <TObject> handler)
        {
            var streamRecordHandlingProtocol = stream.GetStreamRecordHandlingProtocols <TObject>();

            var recordToHandle = await streamRecordHandlingProtocol.ExecuteAsync(new TryHandleRecordOp <TObject>(concern));

            if (recordToHandle != null)
            {
                try
                {
                    var handleRecordOp = new HandleRecordOp <TObject>(recordToHandle);

                    await handler.ExecuteAsync(handleRecordOp);

                    stream.GetStreamRecordHandlingProtocols().Execute(new CompleteRunningHandleRecordOp(recordToHandle.InternalRecordId, concern));
                }
                catch (SelfCancelRunningExecutionException ex)
                {
                    stream.GetStreamRecordHandlingProtocols().Execute(new SelfCancelRunningHandleRecordOp(recordToHandle.InternalRecordId, concern, ex.Details));
                }
                catch (Exception ex)
                {
                    stream.GetStreamRecordHandlingProtocols().Execute(new FailRunningHandleRecordOp(recordToHandle.InternalRecordId, concern, ex.ToString()));

                    throw;
                }
            }
        }
        /// <inheritdoc />
        public override void Execute(
            HandleRecordOp <ExecuteOpRequestedEvent <TOperation> > operation)
        {
            var operationToExecute = operation.RecordToHandle.Payload.Operation;

            this.executeOperationProtocol.Execute(operationToExecute);
            Thread.Sleep(this.waitTimeBeforeQueuing);

            var operationClone = operation.RecordToHandle.Payload.DeepClone();

            this.requeueStream.Put(
                operationClone,
                existingRecordStrategy: this.existingRecordStrategyOnRequeue,
                recordRetentionCount: this.recordRetentionCountOnRequeue);
        }
        /// <inheritdoc />
        public override async Task ExecuteAsync(
            HandleRecordOp <ExecuteOpRequestedEvent <long, UploadFileToSlackOp> > operation)
        {
            new { operation }.AsArg().Must().NotBeNull();

            // Pull some info out of the operation.
            var slackOperationTrackingCodeId = operation.RecordToHandle.Payload.Id;

            var uploadFileToSlackOp = operation.RecordToHandle.Payload.Operation;

            var inheritableTags = operation.RecordToHandle.Metadata.Tags;

            // Write the request to the event stream
            var uploadFileToSlackRequestedEvent = new UploadFileToSlackRequestedEvent <long>(slackOperationTrackingCodeId, DateTime.UtcNow, uploadFileToSlackOp.UploadFileToSlackRequest);

            var tags = this.buildUploadFileToSlackRequestedEventTagsProtocol.ExecuteBuildTags(slackOperationTrackingCodeId, uploadFileToSlackRequestedEvent, inheritableTags);

            await this.slackEventStream.PutWithIdAsync(slackOperationTrackingCodeId, uploadFileToSlackRequestedEvent, tags, ExistingRecordStrategy.DoNotWriteIfFoundById);

            // Execute the operation
            var uploadFileToSlackResponse = await this.uploadFileToSlackProtocol.ExecuteAsync(uploadFileToSlackOp);

            // Put response to event stream based on success/failure
            UploadFileToSlackResponseEventBase <long> uploadFileToSlackResponseEvent;

            if (uploadFileToSlackResponse.UploadFileToSlackResult == UploadFileToSlackResult.Succeeded)
            {
                uploadFileToSlackResponseEvent = new SucceededInUploadingFileToSlackEvent <long>(slackOperationTrackingCodeId, DateTime.UtcNow, uploadFileToSlackResponse);
            }
            else
            {
                uploadFileToSlackResponseEvent = new FailedToUploadFileToSlackEvent <long>(slackOperationTrackingCodeId, DateTime.UtcNow, uploadFileToSlackResponse);
            }

            tags = this.buildUploadFileToSlackResponseEventTagsProtocol.ExecuteBuildTags(slackOperationTrackingCodeId, uploadFileToSlackResponseEvent, inheritableTags);

            await this.slackEventStream.PutWithIdAsync(slackOperationTrackingCodeId, uploadFileToSlackResponseEvent, tags, ExistingRecordStrategy.DoNotWriteIfFoundByIdAndType);
        }
Beispiel #4
0
        /// <inheritdoc />
        public override async Task ExecuteAsync(
            HandleRecordOp <ExecuteOpRequestedEvent <long, SendEmailOp> > operation)
        {
            new { operation }.AsArg().Must().NotBeNull();

            // Pull some info out of the operation.
            var emailTrackingCodeId = operation.RecordToHandle.Payload.Id;

            var sendEmailOp = operation.RecordToHandle.Payload.Operation;

            var inheritableTags = operation.RecordToHandle.Metadata.Tags;

            // Write the request to the event stream
            var sendEmailRequestedEvent = new SendEmailRequestedEvent <long>(emailTrackingCodeId, DateTime.UtcNow, sendEmailOp.SendEmailRequest);

            var tags = this.buildSendEmailRequestedEventTagsProtocol.ExecuteBuildTags(emailTrackingCodeId, sendEmailRequestedEvent, inheritableTags);

            await this.emailEventStream.PutWithIdAsync(emailTrackingCodeId, sendEmailRequestedEvent, tags, ExistingRecordStrategy.DoNotWriteIfFoundById);

            // Execute the operation
            var emailResponse = await this.sendEmailProtocol.ExecuteAsync(sendEmailOp);

            // Put response to event stream based on success/failure
            SendEmailResponseEventBase <long> emailResponseEventBase;

            if (emailResponse.SendEmailResult == SendEmailResult.Success)
            {
                emailResponseEventBase = new SucceededInSendingEmailEvent <long>(emailTrackingCodeId, DateTime.UtcNow, emailResponse);
            }
            else
            {
                emailResponseEventBase = new FailedToSendEmailEvent <long>(emailTrackingCodeId, DateTime.UtcNow, emailResponse);
            }

            tags = this.buildSendEmailResponseEventTagsProtocol.ExecuteBuildTags(emailTrackingCodeId, emailResponseEventBase, inheritableTags);

            await this.emailEventStream.PutWithIdAsync(emailTrackingCodeId, emailResponseEventBase, tags, ExistingRecordStrategy.DoNotWriteIfFoundByIdAndType);
        }
Beispiel #5
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);
        }
Beispiel #6
0
        /// <inheritdoc />
        public override async Task ExecuteAsync(
            HandleRecordOp <ExecuteOpRequestedEvent <long, SendNotificationOp> > operation)
        {
            new { operation }.AsArg().Must().NotBeNull();

            // Pull some info out of the operation.
            var trackingCodeId     = operation.RecordToHandle.Payload.Id;
            var sendNotificationOp = operation.RecordToHandle.Payload.Operation;
            var notification       = sendNotificationOp.Notification;
            var tags = operation.RecordToHandle.Metadata.Tags;

            // Write SendNotificationRequestedEvent to the Event Stream (creates the Notification Aggregate).
            await this.PutSendNotificationRequestedEventAsync(sendNotificationOp, trackingCodeId, tags);

            // Get the audience and write CannotGetOrUseAudienceEvent
            // to the Event Stream if there is none of if we should stop on failures.
            var getAudienceResult = await this.GetAudienceAsync(notification);

            var getAudienceOutcome = getAudienceResult.GetOutcome();

            if ((getAudienceOutcome != GetAudienceOutcome.GotAudienceWithNoFailuresReported) &&
                (getAudienceOutcome != GetAudienceOutcome.GotAudienceWithReportedFailuresIgnored))
            {
                await this.PutCannotGetOrUseAudienceEventAsync(getAudienceResult, trackingCodeId, tags);

                return;
            }

            // Get the delivery channel configs and write CannotGetOrUseDeliveryChannelConfigsEvent
            // to the Event Stream if there are none or if we should stop on failures.
            var audience = getAudienceResult.Audience;

            var getDeliveryChannelConfigsResult = await this.GetDeliveryChannelConfigsAsync(notification, getAudienceResult.Audience);

            var getDeliveryChannelConfigsOutcome = getDeliveryChannelConfigsResult.GetOutcome();

            if ((getDeliveryChannelConfigsOutcome != GetDeliveryChannelConfigsOutcome.GotDeliveryChannelConfigsWithNoFailuresReported) &&
                (getDeliveryChannelConfigsOutcome != GetDeliveryChannelConfigsOutcome.GotDeliveryChannelConfigsWithReportedFailuresIgnored))
            {
                await this.PutCannotGetOrUseDeliveryChannelConfigsEventAsync(getAudienceResult, getDeliveryChannelConfigsResult, trackingCodeId, tags);

                return;
            }

            // Prepare the notification to be sent to the audience on all channels configured for sending.
            var channelConfigs = getDeliveryChannelConfigsResult.Configs;

            var channelToPrepareToSendOnChannelResultMap = new Dictionary <IDeliveryChannel, PrepareToSendOnChannelResult>();

            var channelToOperationInstructionsMap = new Dictionary <IDeliveryChannel, IReadOnlyList <ChannelOperationInstruction> >();

            foreach (var channelConfig in channelConfigs)
            {
                if (channelConfig.Action == DeliveryChannelAction.AudienceOptedOut)
                {
                    continue;
                }
                else if (channelConfig.Action == DeliveryChannelAction.SendOnChannel)
                {
                    // no-op
                }
                else
                {
                    throw new NotSupportedException(Invariant($"This {nameof(DeliveryChannelAction)} is not supported: {channelConfig.Action}."));
                }

                var channel = channelConfig.Channel;

                var prepareToSendOnChannelResult = await this.PrepareToSendOnChannelAsync(notification, audience, channel, tags);

                var prepareToSendOnChannelOutcome = prepareToSendOnChannelResult.GetOutcome();

                channelToPrepareToSendOnChannelResultMap.Add(channel, prepareToSendOnChannelResult);

                if ((prepareToSendOnChannelOutcome == PrepareToSendOnChannelOutcome.PreparedToSendOnChannelWithNoFailuresReported) ||
                    (prepareToSendOnChannelOutcome == PrepareToSendOnChannelOutcome.PreparedToSendOnChannelWithReportedFailuresIgnored))
                {
                    new { this.channelToOperationStreamMap }.AsOp().Must().ContainKey(channel, Invariant($"Staging to send on channel {channel.GetType().ToStringReadable()} but there is no operation stream associated with that channel."));

                    channelToOperationInstructionsMap.Add(channel, prepareToSendOnChannelResult.ChannelOperationInstructions);
                }
                else
                {
                    if (this.cannotPrepareToSendOnChannelAction == CannotPrepareToSendOnChannelAction.ContinueAndAttemptPreparingToSendOnNextChannel)
                    {
                        // no-op
                    }
                    else if (this.cannotPrepareToSendOnChannelAction == CannotPrepareToSendOnChannelAction.StopAndNotDoNotSendOnAnyChannel)
                    {
                        // discard all channel operations
                        channelToOperationInstructionsMap = new Dictionary <IDeliveryChannel, IReadOnlyList <ChannelOperationInstruction> >();

                        break;
                    }
                    else
                    {
                        throw new NotSupportedException(Invariant($"This {nameof(CannotPrepareToSendOnChannelAction)} is not supported: {this.cannotPrepareToSendOnChannelAction}."));
                    }
                }
            }

            var prepareToSendNotificationResult = new PrepareToSendNotificationResult(channelToPrepareToSendOnChannelResultMap, this.cannotPrepareToSendOnChannelAction, channelToOperationInstructionsMap.Keys.ToList());

            var prepareToSendNotificationOutcome = prepareToSendNotificationResult.GetOutcome();

            await this.PutPrepareToSendNotificationEventAsync(prepareToSendNotificationOutcome, getAudienceResult, getDeliveryChannelConfigsResult, prepareToSendNotificationResult, trackingCodeId, tags);

            // Is at least one channel is prepared for sending?  Continue.
            if ((prepareToSendNotificationOutcome != PrepareToSendNotificationOutcome.PreparedToSendOnAllChannels) &&
                (prepareToSendNotificationOutcome != PrepareToSendNotificationOutcome.PreparedToSendOnSomeChannels))
            {
                return;
            }

            // Create a Saga to track channel operations.
            await this.PutSagaAsync(trackingCodeId, channelToOperationInstructionsMap, tags);

            // Push the channel-operations to their streams.
            foreach (var channel in channelToOperationInstructionsMap.Keys)
            {
                var channelOperationInstructions = channelToOperationInstructionsMap[channel];

                await this.PutChannelOperationsAsync(channel, channelOperationInstructions);
            }
        }