private async Task <WorkflowProcessingResult> ProcessMessageInternal(IWorkflowMessage workflowMessage, CancellationToken cancellationToken)
        {
            // TODO: this need to be removed
            Guard.ArgumentNotNull(workflowMessage, nameof(workflowMessage));
            Guard.ArgumentNotNull(workflowMessage.WorkflowInstanceId, nameof(workflowMessage.WorkflowInstanceId));
            Debug.Assert(workflowMessage.WorkflowInstanceId != null, "workflowMessage.WorkflowInstanceId != null");

            var workflowInstance = await GetWorkflowInstanceWithLock(workflowMessage.WorkflowInstanceId.Value, cancellationToken).ConfigureAwait(false);

            if (null == workflowInstance)
            {
                throw new WorkflowException($"Workflow message [{workflowMessage.WorkflowMessageId:D}] is referring to non existing workflow instance");
            }

            var workflowConfiguration = await GetWorkflowConfiguration(workflowInstance.WorkflowId, cancellationToken).ConfigureAwait(false);

            Guard.ArgumentNotNull(workflowConfiguration, nameof(workflowConfiguration));

            var stateConfiguration = workflowConfiguration.GetStateConfigurationByCode(workflowInstance.CurrentStateCode);

            Guard.ArgumentNotNull(stateConfiguration, nameof(stateConfiguration));

            var workflowContext = new WorkflowContext(this, WorkflowEngineBuilder, workflowInstance, workflowConfiguration);

            // ensure that domain entity is available for all/any coded logic for read-only purposes
            await TryGetDomainEntity(workflowContext, cancellationToken).ConfigureAwait(false);

            var messageExecutionContext = new MessageExecutionContext(workflowContext, workflowMessage);
            var messageExecutionResult  = await _messageProcessorLazy.Value.ProcessMessage(messageExecutionContext, cancellationToken).ConfigureAwait(false);

            if (messageExecutionResult.Status == MessageExecutionStatus.Failed)
            {
                Log.Error("An error has occurred during processing message [{messageId}] for {workflowInstanceId}",
                          workflowMessage.WorkflowMessageId, workflowInstance.Id);

                await TransitionToState(workflowContext, workflowConfiguration.GetFailedStateConfiguration(), null, cancellationToken).ConfigureAwait(false);

                return(new WorkflowProcessingResult(workflowInstance, workflowContext.WorkflowExecutionState));
            }

            if (messageExecutionResult.Status == MessageExecutionStatus.ContinueAfterAsyncTransition)
            {
                if (workflowInstance.CurrentStateProgress != StateExecutionProgress.AwaitingAsyncTransition)
                {
                    Log.Error("Can not process message [{messageId}] for {workflowInstanceId} because workflow instance is not in [{subState}]",
                              workflowMessage.WorkflowMessageId, workflowInstance.Id, StateExecutionProgress.AwaitingAsyncTransition);

                    await TransitionToState(workflowContext, workflowConfiguration.GetFailedStateConfiguration(), null, cancellationToken).ConfigureAwait(false);

                    return(new WorkflowProcessingResult(workflowInstance, workflowContext.WorkflowExecutionState));
                }

                workflowContext.WorkflowInstance.CurrentStateProgress = StateExecutionProgress.AfterAsyncTransition;
                await ProcessState(workflowContext, stateConfiguration, cancellationToken).ConfigureAwait(false);
            }

            return(new WorkflowProcessingResult(workflowInstance, workflowContext.WorkflowExecutionState));
        }
예제 #2
0
        public async void Subscribe_MalformedEvent_ExceptionShouldBeThrown()
        {
            //prepare
            var messagebytes = Encoding.UTF8.GetBytes(@"{
                                                            ""Version"": ""0.1""
                                                        }");

            EventEmulator eventEmulator = null;

            _mockBus.Setup(x => x.Consume(_mockQueue.Object,
                                          It.IsAny <Func <byte[], MessageProperties, MessageReceivedInfo, Task> >(),
                                          It.IsAny <Action <IConsumerConfiguration> >()))
            .Returns((IQueue queue, Func <byte[],
                                          MessageProperties, MessageReceivedInfo, Task> func,
                      Action <IConsumerConfiguration> configure) =>
            {
                eventEmulator = new EventEmulator(func);
                return(_consumerDisposable.Object);
            });


            Dictionary <string, ISubscription> subscriptions = new Dictionary <string, ISubscription>();

            MessageProperties messageProperties = new MessageProperties();

            MessageExecutionContext actualMessageExecutionContext = null;

            _errorHandlingStrategy.Setup(x => x.HandleErrorAsync(
                                             It.IsAny <MessageExecutionContext>()))
            .Returns <MessageExecutionContext>(
                (executionContext) =>
            {
                actualMessageExecutionContext = executionContext;
                return(Task.FromException <MalformedEventException>(executionContext.Exception));
            });

            _easyNetQSubscriber.Subscribe(subscriptions);

            //Act
            await eventEmulator.Execute(messagebytes, messageProperties, null);

            //check
            ((MalformedEventException)eventEmulator.RaisedException).
            Errors.First().ShouldBeEquivalentTo("EventId token is missed");

            _successHandlingStrategy.Verify(x => x.HandleSuccessAsync(It.IsAny <IntegrationEvent>()), Times.Never);
            _errorHandlingStrategy.Verify(x => x.HandleErrorAsync(It.IsAny <MessageExecutionContext>()), Times.Once);
            actualMessageExecutionContext.BodyBytes.ShouldBeEquivalentTo(messagebytes);
            actualMessageExecutionContext.Exception.Should().Be(eventEmulator.RaisedException);
            actualMessageExecutionContext.MessageProperties.Should().Be(messageProperties);
            actualMessageExecutionContext.IntergrationEventParsingResult.ShouldBeEquivalentTo(DefaultIntergrationEventParser.Parse(messagebytes));
            actualMessageExecutionContext.SerializedMessage.Should().BeNull();
            actualMessageExecutionContext.DeadLetterIntegrationEvent.Should().BeNull();
            actualMessageExecutionContext.Subscription.Should().BeNull();
        }
예제 #3
0
        public async void Subscribe_DiscardEventException_WhenProcessed_ExceptionShouldBeThrown()
        {
            //prepare
            var content = new BookingCreated()
            {
                BookingName = _bookingName
            };
            IntegrationEvent <BookingCreated> integrationEvent =
                new IntegrationEvent <BookingCreated>
            {
                CorrelationId     = Guid.NewGuid(),
                EventCreationDate = DateTime.UtcNow,
                EventId           = Guid.NewGuid(),
                EventType         = _bookingEventType,
                Version           = _version,
                Content           = content
            };

            var messagebytes = SerializeEvent(integrationEvent);

            EventEmulator eventEmulator = null;

            _mockBus.Setup(x => x.Consume(_mockQueue.Object,
                                          It.IsAny <Func <byte[], MessageProperties, MessageReceivedInfo, Task> >(),
                                          It.IsAny <Action <IConsumerConfiguration> >()))
            .Returns((IQueue queue, Func <byte[],
                                          MessageProperties, MessageReceivedInfo, Task> func,
                      Action <IConsumerConfiguration> configure) =>
            {
                eventEmulator = new EventEmulator(func);
                return(_consumerDisposable.Object);
            });


            DiscardEventException discardEventException = new DiscardEventException();
            var mockSubscription = new Mock <ISubscription>();

            mockSubscription.Setup(x => x.InvokeAsync(Encoding.UTF8.GetString(messagebytes)))
            .Returns <string>(
                (serilizedmessage) =>
            {
                return(Task.FromException <DiscardEventException>(discardEventException));
            });


            Dictionary <string, ISubscription> subscriptions = new Dictionary <string, ISubscription> {
                { "bookingcreated", mockSubscription.Object }
            };

            _subsciptionSelector.Setup(x => x.Select(subscriptions, "bookingcreated"))
            .Returns((IDictionary <string, ISubscription> subs, string eventType) => mockSubscription.Object);

            MessageProperties       messageProperties             = new MessageProperties();
            MessageExecutionContext actualMessageExecutionContext = null;

            _errorHandlingStrategy.Setup(x => x.HandleErrorAsync(It.IsAny <MessageExecutionContext>()))
            .Returns <MessageExecutionContext>(
                (messageExecutionContext) =>
            {
                actualMessageExecutionContext = messageExecutionContext;
                return(Task.FromException <DiscardEventException>(messageExecutionContext.Exception));
            });

            _easyNetQSubscriber.Subscribe(subscriptions);

            //Act

            await eventEmulator.Execute(messagebytes, messageProperties, null);

            //check
            eventEmulator.RaisedException.GetType().Should().Be(typeof(DiscardEventException));

            _successHandlingStrategy.Verify(x => x.HandleSuccessAsync(It.IsAny <IntegrationEvent>()), Times.Never);
            _errorHandlingStrategy.Verify(x => x.HandleErrorAsync(It.IsAny <MessageExecutionContext>()), Times.Once);
            actualMessageExecutionContext.BodyBytes.ShouldBeEquivalentTo(messagebytes);
            actualMessageExecutionContext.Exception.Should().Be(eventEmulator.RaisedException);
            actualMessageExecutionContext.MessageProperties.Should().Be(messageProperties);
            actualMessageExecutionContext.IntergrationEventParsingResult.ShouldBeEquivalentTo(DefaultIntergrationEventParser.Parse(messagebytes));
            actualMessageExecutionContext.SerializedMessage.Should().Be(Encoding.UTF8.GetString(messagebytes));
            actualMessageExecutionContext.DeadLetterIntegrationEvent.Should().BeNull();
            actualMessageExecutionContext.Subscription.Should().Be(mockSubscription.Object);
        }
예제 #4
0
        public async void Subscribe_CorrectlyFormedEvent_NoConsumerForEventTypeMatched()
        {
            //prepare
            IntegrationEvent <BookingCreated> integrationEvent =
                new IntegrationEvent <BookingCreated>()
            {
                CorrelationId     = Guid.NewGuid(),
                EventCreationDate = DateTime.UtcNow,
                EventId           = Guid.NewGuid(),
                EventType         = _bookingEventType,
                Version           = _version,
                Content           = new BookingCreated
                {
                    BookingName = _bookingName
                }
            };

            var messagebytes = SerializeEvent(integrationEvent);

            EventEmulator eventEmulator = null;

            _mockBus.Setup(x => x.Consume(_mockQueue.Object,
                                          It.IsAny <Func <byte[], MessageProperties, MessageReceivedInfo, Task> >(),
                                          It.IsAny <Action <IConsumerConfiguration> >()))
            .Returns((IQueue queue, Func <byte[],
                                          MessageProperties, MessageReceivedInfo, Task> func, Action <IConsumerConfiguration> configure) =>
            {
                eventEmulator = new EventEmulator(func);
                return(_consumerDisposable.Object);
            });



            Dictionary <string, ISubscription> subscriptions = new Dictionary <string, ISubscription>();

            _subsciptionSelector.Setup(x => x.Select(subscriptions, _bookingEventType))
            .Returns((IDictionary <string, ISubscription> subs, string eventType) => null);

            MessageProperties messageProperties = new MessageProperties();

            MessageExecutionContext actualMessageExecutionContext = null;

            _errorHandlingStrategy.Setup(x => x.HandleErrorAsync(It.IsAny <MessageExecutionContext>()))
            .Returns <MessageExecutionContext>(
                (messageExecutionContext) =>
            {
                actualMessageExecutionContext = messageExecutionContext;
                return(Task.FromException <DiscardEventException>(messageExecutionContext.Exception));
            });



            _easyNetQSubscriber.Subscribe(subscriptions);


            //Act

            await eventEmulator.Execute(messagebytes, messageProperties, null);

            //check
            _successHandlingStrategy.Verify(x => x.HandleSuccessAsync(It.IsAny <IntegrationEvent>()), Times.Never);
            _errorHandlingStrategy.Verify(x => x.HandleErrorAsync(It.IsAny <MessageExecutionContext>()), Times.Once);
            actualMessageExecutionContext.BodyBytes.ShouldBeEquivalentTo(messagebytes);
            actualMessageExecutionContext.Exception.Should().Be(eventEmulator.RaisedException);
            actualMessageExecutionContext.MessageProperties.Should().Be(messageProperties);
            actualMessageExecutionContext.IntergrationEventParsingResult.ShouldBeEquivalentTo(DefaultIntergrationEventParser.Parse(messagebytes));
            actualMessageExecutionContext.SerializedMessage.Should().BeNull();
            actualMessageExecutionContext.DeadLetterIntegrationEvent.Should().BeNull();
            actualMessageExecutionContext.Subscription.Should().BeNull();

            eventEmulator.RaisedException.Should().BeOfType <DiscardEventException>().Which.Message
            .ShouldBeEquivalentTo("The consumer for bookingcreated event type is not matched");
        }
        public async void HandleErrorAsync_ExceptionThatShouldBeCatchedANDAttemptNumberN_MessageShouldBeSentToAppropriateRetryQueue(bool explicitAcknowledgments,
                                                                                                                                    int numberOfAttempt, bool storedeadletter)
        {
            //prepare
            MessageProperties messageProperties = new MessageProperties();

            if (numberOfAttempt != 0)
            {
                messageProperties.Headers = new Dictionary <string, object>();
                messageProperties.Headers["retrycount"] = numberOfAttempt;
            }


            BookingCreated bookingCreated = new BookingCreated {
                BookingName = "test"
            };
            string bookingCreatedEventType = "bookingCreatedEventType";
            IntegrationEvent <BookingCreated> integrationEvent = new IntegrationEvent <BookingCreated>(bookingCreated, bookingCreatedEventType);
            var       message   = JsonConvert.SerializeObject(integrationEvent);
            var       body      = Encoding.UTF8.GetBytes(message);
            Exception exception = new IOException("IOException");

            ExecutionHandlingStrategy executionHandlingStrategy = new ExecutionHandlingStrategy(_subscriberName, _easyNetQPublisher,
                                                                                                _maxRetry, _modelNamingConventionController.Object, explicitAcknowledgments, _publisher.Object, storedeadletter);

            string retryRoutingKey = string.Concat("retry.", numberOfAttempt + 1, ".", _subscriberName);

            _modelNamingConventionController.Setup(x => x.GetRetryRoutingKey(_subscriberName, numberOfAttempt + 1))
            .Returns((string subscribername, int retryindex) => retryRoutingKey);



            string            actualRouting    = string.Empty;
            MessageProperties actualProperties = null;

            byte[]    actualMsg      = null;
            IExchange actualExchange = null;

            _mockBus.Setup(x => x.ExchangeDeclareAsync(_exchangeName, ExchangeType.Topic,
                                                       false, true, false, false, null, false))
            .Returns((string exchangename, string exchanegType,
                      bool passive, bool durable, bool autoDelete, bool internalflag,
                      string alternateExchange, bool delayed) =>
                     { return(Task.FromResult(_mockExchange.Object)); });

            _mockBus.Setup(x => x.PublishAsync(It.IsAny <IExchange>(), It.IsAny <string>(), false, It.IsAny <MessageProperties>(), It.IsAny <byte[]>()))
            .Returns((IExchange exchange, string routing, bool mandatory,
                      MessageProperties properties, byte[] msg) =>
                     { return(Task.FromResult(true)); })
            .Callback <IExchange, string, bool, MessageProperties, byte[]>(
                (exchange, routing, mandatory, properties, msg) =>
            {
                actualExchange   = exchange;
                actualRouting    = routing;
                actualProperties = properties;
                actualMsg        = msg;
            });


            //act
            Mock <ISubscription>    mockedSubscription      = new Mock <ISubscription>();
            MessageExecutionContext messageExecutionContext =
                new MessageExecutionContext(body, messageProperties, null)
            {
                Exception         = exception,
                SerializedMessage = message,
                Subscription      = mockedSubscription.Object,
                IntergrationEventParsingResult = null,
                DeadLetterIntegrationEvent     = null
            };
            await executionHandlingStrategy.HandleErrorAsync(messageExecutionContext);


            //check
            actualRouting.Should().Be(retryRoutingKey);
            actualMsg.Should().BeEquivalentTo(body);
            (int.Parse(actualProperties.Headers["retrycount"].ToString())).Should().Be(numberOfAttempt + 1);
            actualExchange.Should().Be(_mockExchange.Object);

            _publisher.Verify(m => m.PublishEventAsync(It.IsAny <IntegrationEvent <string> >(), It.IsAny <string>(), It.IsAny <bool>()), Times.Never);
            mockedSubscription.Verify(s => s.NotifyAboutDeadLetterAsync(It.IsAny <string>(), It.IsAny <Exception>()), Times.Never);
            mockedSubscription.Verify(s => s.InvokeAsync(It.IsAny <string>()), Times.Never);
        }
        public async void HandleErrorAsync_ExceptionThatShouldBeCatchedANDLastAttempt_MessageShouldBeSentToAppropriateDeadLetterQueue(bool explicitAcknowledgments,
                                                                                                                                      bool storedeadletter)
        {
            //prepare
            MessageProperties messageProperties = new MessageProperties();

            messageProperties.Headers = new Dictionary <string, object>();
            messageProperties.Headers["retrycount"] = _maxRetry;

            BookingCreated bookingCreated = new BookingCreated {
                BookingName = "test"
            };
            string bookingCreatedEventType = "bookingCreatedEventType";
            IntegrationEvent <BookingCreated> integrationEvent = new IntegrationEvent <BookingCreated>(bookingCreated, bookingCreatedEventType);
            var       message   = JsonConvert.SerializeObject(integrationEvent);
            var       body      = Encoding.UTF8.GetBytes(message);
            Exception exception = new IOException("IOException");

            ExecutionHandlingStrategy executionHandlingStrategy = new ExecutionHandlingStrategy(_subscriberName, _easyNetQPublisher,
                                                                                                _maxRetry, _modelNamingConventionController.Object, explicitAcknowledgments, _publisher.Object, storedeadletter);

            _mockBus.Setup(x => x.ExchangeDeclareAsync(_exchangeName, ExchangeType.Topic,
                                                       false, true, false, false, null, false))
            .Returns((string exchangename, string exchanegType,
                      bool passive, bool durable, bool autoDelete, bool internalflag,
                      string alternateExchange, bool delayed) =>
                     { return(Task.FromResult(_mockExchange.Object)); });


            _modelNamingConventionController.Setup(x => x.GetDeadLetterQueueMessageEventType(bookingCreatedEventType))
            .Returns((string eventType) => string.Concat("deadletter.", eventType));

            _modelNamingConventionController.Setup(x => x.GetDeadLetterQueueRoutingKey(_subscriberName))
            .Returns((string subscriberName) => string.Concat("deadletter.", subscriberName));

            //Main flow
            string            actualRouting    = string.Empty;
            MessageProperties actualProperties = null;

            byte[]    actualMsg      = null;
            IExchange actualExchange = null;

            _mockBus.Setup(x => x.PublishAsync(It.IsAny <IExchange>(), It.IsAny <string>(), false, It.IsAny <MessageProperties>(), It.IsAny <byte[]>()))
            .Returns((IExchange exchange, string routing, bool mandatory,
                      MessageProperties properties, byte[] msg) =>
                     { return(Task.FromResult(true)); })
            .Callback <IExchange, string, bool, MessageProperties, byte[]>(
                (exchange, routing, mandatory, properties, msg) =>
            {
                actualExchange   = exchange;
                actualRouting    = routing;
                actualProperties = properties;
                actualMsg        = msg;
            });
            //END Main flow

            //Tracking flow
            IntegrationEvent <DeadLetterEventDescriptor <JObject> > actualTrackingMessage = null;
            string actualTrackingMessageRouting = string.Empty;
            bool   actualPersistantMode         = false;

            _publisher.Setup(x => x.PublishEventAsync(It.IsAny <IntegrationEvent <DeadLetterEventDescriptor <JObject> > >(), It.IsAny <string>(), true))
            .Returns(Task.FromResult(true))
            .Callback <IntegrationEvent <DeadLetterEventDescriptor <JObject> >, string, bool>(
                (trackingMessage, routing, persistantMode) =>
            {
                actualTrackingMessage        = trackingMessage;
                actualTrackingMessageRouting = routing;
                actualPersistantMode         = persistantMode;
            });

            //END Tracking flow


            //act
            Mock <ISubscription>    mockedSubscription      = new Mock <ISubscription>();
            MessageExecutionContext messageExecutionContext =
                new MessageExecutionContext(body, messageProperties, null)
            {
                Exception         = exception,
                SerializedMessage = message,
                Subscription      = mockedSubscription.Object,
                IntergrationEventParsingResult = null,
                DeadLetterIntegrationEvent     = null
            };

            await executionHandlingStrategy.HandleErrorAsync(messageExecutionContext);

            //check
            var expectedDeadLetterEventDescriptor =
                new DeadLetterEventDescriptor <JObject>
            {
                CountOfAttempts  = _maxRetry,
                LastAttemptError = exception.ToString(),
                Original         = JsonConvert.DeserializeObject <IntegrationEvent <JObject> >(message)
            };

            var expectedDeadletterqueueevent = new IntegrationEvent <DeadLetterEventDescriptor <JObject> >
            {
                CorrelationId = integrationEvent.CorrelationId,
                EventType     = string.Concat("deadletter.", bookingCreatedEventType),
                Content       = expectedDeadLetterEventDescriptor
            };

            //Check publishing dead letter message
            if (storedeadletter)
            {
                var actuaDeadLetterQueuelSerializedMessage = Encoding.UTF8.GetString(actualMsg);
                IntegrationEvent <DeadLetterEventDescriptor <JObject> > actualDeadLetterQueueIntegrationEvent =
                    JsonConvert.DeserializeObject <IntegrationEvent <DeadLetterEventDescriptor <JObject> > >(actuaDeadLetterQueuelSerializedMessage);


                actualDeadLetterQueueIntegrationEvent.CorrelationId.Should().Be(expectedDeadletterqueueevent.CorrelationId);
                actualDeadLetterQueueIntegrationEvent.Content.ShouldBeEquivalentTo(expectedDeadletterqueueevent.Content);
                actualDeadLetterQueueIntegrationEvent.EventType.Should()
                .Be(expectedDeadletterqueueevent.EventType);

                actualRouting.Should().Be(string.Concat("deadletter.", _subscriberName));
                actualExchange.Should().Be(_mockExchange.Object);
                actualProperties.DeliveryMode.Should().Be(2);//persistant delivery mode
            }
            else
            {
                _mockBus.Verify(v => v.PublishAsync(It.IsAny <IExchange>(), It.IsAny <string>(), false, It.IsAny <MessageProperties>(), It.IsAny <byte[]>()), Times.Never);
            }

            //Check Subsciption notification
            mockedSubscription.Verify(s => s.NotifyAboutDeadLetterAsync(messageExecutionContext.SerializedMessage, messageExecutionContext.Exception), Times.Once);
            mockedSubscription.Verify(s => s.InvokeAsync(It.IsAny <string>()), Times.Never);

            //Check Tracking flow
            if (explicitAcknowledgments == false)
            {
                //check, that No deadletter message for tracking is sent.
                _publisher.Verify(v => v.PublishEventAsync(It.IsAny <IntegrationEvent <DeadLetterEventDescriptor <JObject> > >(), It.IsAny <string>(), It.IsAny <bool>()), Times.Never);
            }
            else
            {
                //check, that deadltter message for tracking is equal to the one sent to the deadletter queue. Routing key must be the same
                _publisher.Verify(v => v.PublishEventAsync(It.IsAny <IntegrationEvent <DeadLetterEventDescriptor <JObject> > >(), It.IsAny <string>(), It.IsAny <bool>()), Times.Once);

                actualTrackingMessageRouting.Should().Be(string.Concat("deadletter.", _subscriberName));
                actualPersistantMode.Should().BeTrue();
                actualTrackingMessage.CorrelationId.Should().Be(expectedDeadletterqueueevent.CorrelationId);
                actualTrackingMessage.Content.ShouldBeEquivalentTo(expectedDeadletterqueueevent.Content);
                actualTrackingMessage.EventType.Should()
                .Be(expectedDeadletterqueueevent.EventType);
            }
        }