public static async Task SagaOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { TransactionItem item = context.GetInput <TransactionItem>(); var commandProducers = new Dictionary <string, Func <Task <ActivityResult <ProducerResult> > > > { [nameof(ValidateTransferCommand)] = () => ActivityFactory.ProduceValidateTransferCommandAsync(item, context, log), [nameof(TransferCommand)] = () => ActivityFactory.ProduceTransferCommandAsync(item, context, log), [nameof(CancelTransferCommand)] = () => ActivityFactory.ProduceCancelTransferCommandAsync(item, context, log), [nameof(IssueReceiptCommand)] = () => ActivityFactory.ProduceIssueReceiptCommandAsync(item, context, log) }; var sagaStatePersisters = new Dictionary <string, Func <Task <bool> > > { [nameof(SagaState.Pending)] = () => SagaFactory.PersistSagaStateAsync(item, SagaState.Pending, context, log), [nameof(SagaState.Success)] = () => SagaFactory.PersistSagaStateAsync(item, SagaState.Success, context, log), [nameof(SagaState.Cancelled)] = () => SagaFactory.PersistSagaStateAsync(item, SagaState.Cancelled, context, log), [nameof(SagaState.Fail)] = () => SagaFactory.PersistSagaStateAsync(item, SagaState.Fail, context, log), }; try { var orchestrator = new DurableOrchestrator(commandProducers, sagaStatePersisters); SagaState sagaState = await orchestrator.OrchestrateAsync(item, context, log); log.LogInformation($@"Saga state = {nameof(sagaState)} [{context.InstanceId}]"); } catch (ArgumentException ex) { log.LogError(ex.Message); } }
public async Task Saga_should_fail_with_invalid_external_events() { var transactionItem = CreateFakeTransactionItem(); var commandProducers = new Dictionary <string, Func <Task <ActivityResult <ProducerResult> > > > { [nameof(ValidateTransferCommand)] = () => Task.Run(() => new ActivityResult <ProducerResult> { Item = new ProducerResult() }), [nameof(TransferCommand)] = () => Task.Run(() => new ActivityResult <ProducerResult> { Item = new ProducerResult() }), [nameof(IssueReceiptCommand)] = () => Task.Run(() => new ActivityResult <ProducerResult> { Item = new ProducerResult() }) }; var sagaStatePersisters = new Dictionary <string, Func <Task <bool> > > { [nameof(SagaState.Fail)] = () => Task.Run(() => true) }; var loggerMock = new Mock <ILogger>(); var mockContext = new Mock <IDurableOrchestrationContext>(); var orchestrator = new DurableOrchestrator(commandProducers, sagaStatePersisters); await Assert.ThrowsAsync <KeyNotFoundException>(() => orchestrator.OrchestrateAsync(transactionItem, mockContext.Object, loggerMock.Object)); }
public async Task Saga_should_succeed_with_successful_compensation_workflow() { var transactionItem = CreateFakeTransactionItem(); var commandProducers = new Dictionary <string, Func <Task <ActivityResult <ProducerResult> > > > { [nameof(ValidateTransferCommand)] = () => Task.Run(() => new ActivityResult <ProducerResult> { Item = new ProducerResult() }), [nameof(TransferCommand)] = () => Task.Run(() => new ActivityResult <ProducerResult> { Item = new ProducerResult() }), [nameof(CancelTransferCommand)] = () => Task.Run(() => new ActivityResult <ProducerResult> { Item = new ProducerResult() }), [nameof(IssueReceiptCommand)] = () => Task.Run(() => new ActivityResult <ProducerResult> { Item = new ProducerResult() }) }; var sagaStatePersisters = new Dictionary <string, Func <Task <bool> > > { [nameof(SagaState.Pending)] = () => Task.Run(() => true), [nameof(SagaState.Success)] = () => Task.Run(() => true), [nameof(SagaState.Cancelled)] = () => Task.Run(() => true), }; var loggerMock = new Mock <ILogger>(); var mockContext = new Mock <IDurableOrchestrationContext>(); mockContext .Setup(x => x.WaitForExternalEvent <string>(nameof(Sources.Validator))) .ReturnsAsync(nameof(TransferValidatedEvent)); mockContext .SetupSequence(x => x.WaitForExternalEvent <string>(nameof(Sources.Transfer))) .ReturnsAsync(nameof(TransferSucceededEvent)) .ReturnsAsync(nameof(TransferCanceledEvent)); mockContext .Setup(x => x.WaitForExternalEvent <string>(nameof(Sources.Receipt))) .ReturnsAsync(nameof(OtherReasonReceiptFailedEvent)); var orchestrator = new DurableOrchestrator(commandProducers, sagaStatePersisters); SagaState sagaState = await orchestrator.OrchestrateAsync(transactionItem, mockContext.Object, loggerMock.Object); Assert.Equal(SagaState.Cancelled, sagaState); }
public async Task Saga_should_not_be_orchestrated_with_invalid_events() { var transactionItem = CreateFakeTransactionItem(); var commandProducers = new Dictionary <string, Func <Task <ActivityResult <ProducerResult> > > > { [nameof(ValidateTransferCommand)] = () => Task.Run(() => new ActivityResult <ProducerResult> { Item = new ProducerResult() }), [nameof(TransferCommand)] = () => Task.Run(() => new ActivityResult <ProducerResult> { Item = new ProducerResult() }), [nameof(IssueReceiptCommand)] = () => Task.Run(() => new ActivityResult <ProducerResult> { Item = new ProducerResult() }) }; var sagaStatePersisters = new Dictionary <string, Func <Task <bool> > > { [nameof(SagaState.Pending)] = () => Task.Run(() => true), [nameof(SagaState.Success)] = () => Task.Run(() => true), [nameof(SagaState.Fail)] = () => Task.Run(() => true), }; var loggerMock = new Mock <ILogger>(); var mockContext = new Mock <IDurableOrchestrationContext>(); mockContext .Setup(x => x.WaitForExternalEvent <string>(nameof(Sources.Validator))) .ReturnsAsync(nameof(ReceiptIssuedEvent)); var orchestrator = new DurableOrchestrator(commandProducers, sagaStatePersisters); SagaState sagaState = await orchestrator.OrchestrateAsync(transactionItem, mockContext.Object, loggerMock.Object); Assert.Equal(SagaState.Fail, sagaState); }