Ejemplo n.º 1
0
        public ResponseStateMachine(IOptions <ServiceBusRabbitMQOptions> configBus, IOptions <WorkflowConfig> configWorkflow)
        {
            _configBus = configBus.Value;

            InstanceState(x => x.State);

            //Response events
            Event(() => ResponseCreated, x => x.CorrelateById(context => context.Message.Response.ResponseId));
            Event(() => ResponseUpdated, x => x.CorrelateById(context => context.Message.Response.ResponseId));
            Event(() => ResponseActionCallback, x => x.CorrelateById(context => context.Message.ResponseId));
            Event(() => ResponseClosed, x => x.CorrelateById(context => context.Message.Response.ResponseId));
            Event(() => ResponseEventClusterTagged, x => x.CorrelateById(context => context.Message.Response.ResponseId));

            //The event is submitted from the Response endpoint
            Initially(
                When(ResponseCreated)
                .Then(context => context.Instance.CorrelationId = context.Data.Response.ResponseId)
                .Then(context => Console.WriteLine($"Response-{context.Instance.CorrelationId}: Saga Initiated."))
                .Then(context => Console.WriteLine($"Response--{context.Instance.CorrelationId}: Event Received."))
                .ThenPublishActions(ResponseUpdateType.NewResponse)     //Run open actions
                .Then(context => Console.Out.WriteLine($"Response--{context.Instance.CorrelationId}: ResponseCreated."))
                .TransitionTo(Waiting)
                );

            //Waiting state
            During(Waiting,
                   //Triggers when event clusters have been associated
                   When(ResponseEventClusterTagged)
                   .ThenAsync(context => context.Publish(new ResponseUIUpdateRequestedEvent()
            {
                ResponseId = context.Instance.CorrelationId,
                ResponseUI = new ResponseUIModel()
                {
                    UpdateType = ResponseUpdateType.UpdateResponse.ToString(),
                    Response   = context.Data.Response,
                    ResponseId = context.Data.Response.ResponseId
                }
            })),

                   //Triggers when the response was closed
                   When(ResponseClosed)
                   .Then(context => Console.Out.WriteLine($"Response--{context.Instance.CorrelationId}: ResponseClosed."))
                   .ThenPublishActions(ResponseUpdateType.CloseResponse), //Run close actions

                   //Triggers when the was updated with new actions
                   When(ResponseUpdated)
                   .Then(context => Console.Out.WriteLine($"Response--{context.Instance.CorrelationId}: ResponseActionsUpdated."))
                   .ThenPublishActions(ResponseUpdateType.UpdateResponseActions), //Run update action,

                   //Triggers call back on actions, ends the saga if all close actions are performed successfully
                   When(ResponseActionCallback)
                   .Then(context => Console.Out.WriteLine($"Response--{context.Instance.CorrelationId}: ResponseActionCallback"))
                   .ThenAsync(context => context.Publish(new ActionCallbackUIUpdatedRequestedEvent() //Track action callbacks
            {
                ResponseId       = context.Data.ResponseId,
                ActionId         = context.Data.ActionId,
                ActionCloseModel = new ActionCallbackUIModel()
                {
                    UpdateType   = ResponseUpdateType.ResponseActionCallback.ToString(),
                    ActionId     = context.Data.ActionId,
                    Status       = context.Data.Status,
                    ErrorMessage = context.Data.ErrorMessage,
                    StartDate    = context.Data.StartDate,
                    EndDate      = context.Data.EndDate,
                    ResponseId   = context.Data.ResponseId
                }
            }))
                   //Add 1 to the action completed count
                   .If(new StateMachineCondition <ResponseState, IEventSagaReceiveResponseActionCallback>(bc =>
                                                                                                          bc.Data.ActionCorrelationId == bc.Instance.ActionCorrelationId), x => x
                       .Then(context => context.Instance.ActionsCompletedCount++))
                   //When the count is complete, we send a UI update
                   .If(new StateMachineCondition <ResponseState, IEventSagaReceiveResponseActionCallback>(bc =>
                                                                                                          bc.Data.ActionCorrelationId == bc.Instance.ActionCorrelationId && bc.Instance.ActionsCompletedCount >= bc.Instance.ActionsTotal), x => x
                       .ThenAsync(context => context.Publish(new ResponseUIUpdateRequestedEvent()
            {
                ResponseId = context.Instance.CorrelationId,
                ResponseUI = new ResponseUIModel()
                {
                    UpdateType = context.Instance.ActionUpdateType.ToString(),
                    ResponseId = context.Instance.CorrelationId
                }
            })))
                   //If the update type was close response, we finalize the saga
                   .If(new StateMachineCondition <ResponseState, IEventSagaReceiveResponseActionCallback>(bc =>
                                                                                                          bc.Data.ActionCorrelationId == bc.Instance.ActionCorrelationId && bc.Instance.ActionsCompletedCount >= bc.Instance.ActionsTotal &&
                                                                                                          bc.Instance.ActionUpdateType == ResponseUpdateType.CloseResponse), x => x
                       .Finalize())
                   );

            //Delete persisted saga after completion
            SetCompletedWhenFinalized();
        }
Ejemplo n.º 2
0
 public ServiceBusRabbitMQ(IOptions <ServiceBusRabbitMQOptions> config,
                           ILogger <ServiceBusRabbitMQ> logger)
 {
     _logger = logger;
     _config = config.Value;
 }
Ejemplo n.º 3
0
        public DeviceSynchronizationStateMachine(IOptions <ServiceBusRabbitMQOptions> configBus)
        {
            _configBus = configBus.Value;

            InstanceState(x => x.State);

            #region Initialization Events
            //Devices Updates
            Event(() => EventReceived, x => x.CorrelateById(context => context.Message.DeviceId).SelectId(context => Guid.NewGuid()));
            Event(() => EventDeviceDeleted, x => x.CorrelateById(context => context.Message.CorrelationId));
            Event(() => DeviceCreatedOrUpdated, x => x.CorrelateById(context => context.Message.CorrelationId));
            //UI Update notification, can be ignored if saga doesn't exist anymore
            Event(() => DeviceUIUpdated, x => { x.CorrelateById(context => context.Message.CorrelationId); x.OnMissingInstance(m => m.Discard()); });

            Event(() => DeviceDeleteRequestedFault, x => x.CorrelateById(context => context.Message.Message.CorrelationId));
            Event(() => DeviceCreateOrUpdateRequestedFault, x => x.CorrelateById(context => context.Message.Message.CorrelationId));
            #endregion

            //The event is submitted from Azure Service Queue
            Initially(
                When(EventReceived)
                .Then(context => context.Instance.DeviceId   = context.Data.DeviceId)
                .Then(context => context.Instance.ChangeType = context.Data.ChangeType)
                .ThenAsync(context => Console.Out.WriteLineAsync($"DeviceSynchronization-{context.Instance.CorrelationId}: Saga Initiated."))
                .ThenAsync(context => Console.Out.WriteLineAsync($"DeviceSynchronization--{context.Instance.CorrelationId}: Event Received."))
                .If(new StateMachineCondition <DeviceSynchronizationState, IEventSagaReceivedDeviceChange>(bc =>
                                                                                                           bc.Instance.ChangeType == "twinChangeNotification" || bc.Instance.ChangeType == "createDeviceIdentity" ||
                                                                                                           bc.Instance.ChangeType == "ping"), x => x
                    .ThenAsync(context => context.Publish(new DeviceCreateOrUpdateRequestedEvent()
            {
                CorrelationId = context.Instance.CorrelationId,
                DeviceId      = context.Data.DeviceId,
                ChangeType    = context.Data.ChangeType,
                Date          = context.Data.Date,
                Data          = context.Data.Data
            }))
                    .TransitionTo(Waiting)
                    )
                .If(new StateMachineCondition <DeviceSynchronizationState, IEventSagaReceivedDeviceChange>(bc =>
                                                                                                           bc.Instance.ChangeType == "deleteDeviceIdentity"), x => x
                    .ThenAsync(context => context.Publish(new DeviceDeleteRequestedEvent()
            {
                CorrelationId = context.Instance.CorrelationId,
                DeviceId      = context.Data.DeviceId
            }))
                    .TransitionTo(Waiting)
                    ));

            //State while the Device is being created / updated
            During(Waiting,
                   When(DeviceCreatedOrUpdated)
                   .ThenAsync(context => Console.Out.WriteLineAsync($"DeviceSynchronization---{context.Instance.CorrelationId}: Device Updated: {context.Instance.ChangeType}."))
                   .If(new StateMachineCondition <DeviceSynchronizationState, IDeviceCreatedOrUpdated>(bc => bc.Data.NotifyUI), x => x
                       .ThenAsync(context => context.Publish(new DeviceUIUpdateRequestedEvent()
            {
                CorrelationId = context.Instance.CorrelationId,
                DeviceUI      = new DeviceUIModel()
                {
                    DeviceId   = context.Instance.DeviceId,
                    Device     = context.Data.Device,
                    UpdateType = context.Instance.ChangeType == "twinChangeNotification" || context.Instance.ChangeType == "ping"  ? DeviceUpdateType.UpdateDevice.ToString() : DeviceUpdateType.NewDevice.ToString()
                }
            }))
                       )
                   .Finalize(),
                   When(EventDeviceDeleted)
                   .ThenAsync(context => Console.Out.WriteLineAsync($"DeviceSynchronization---{context.Instance.CorrelationId}: Device Deleted: {context.Instance.DeviceId}."))
                   .ThenAsync(context => context.Publish(new DeviceUIUpdateRequestedEvent()
            {
                CorrelationId = context.Instance.CorrelationId,
                DeviceUI      = new DeviceUIModel()
                {
                    DeviceId   = context.Instance.DeviceId,
                    UpdateType = DeviceUpdateType.DeleteDevice.ToString()
                }
            }))
                   .Finalize()
                   );

            //Stateless
            DuringAny(
                When(DeviceDeleteRequestedFault)
                .ThenAsync(context => Console.Out.WriteLineAsync($"DeviceSynchronization---{context.Instance.CorrelationId}: !!FAULT!! Device Deleted: {context.Data.Message}")),
                When(DeviceCreateOrUpdateRequestedFault)
                .ThenAsync(context => Console.Out.WriteLineAsync($"DeviceSynchronization---{context.Instance.CorrelationId}: !!FAULT!! Device Updated: {context.Data.Message}"))
                //    //UI Update acknoledgement
                //    When(DeviceUIUpdated)
                //        .ThenAsync(context => Console.Out.WriteLineAsync($"DeviceSynchronization---{context.Instance.CorrelationId}: UI Updated."))
                );


            //Delete persisted saga after completion
            SetCompletedWhenFinalized();
        }
Ejemplo n.º 4
0
        public EventProcessingStateMachine(IOptions <ServiceBusRabbitMQOptions> configBus, IOptions <WorkflowConfig> configWorkflow)
        {
            _configBus      = configBus.Value;
            _configWorkflow = configWorkflow.Value.EventProcessingWorkflow;

            InstanceState(x => x.State);

            #region Initialization Events
            //Main events being added, only one will be processed at a time
            Event(() => EventReceived, x => x.CorrelateBy(context => context.SagaDeviceId, context => $"{context.Message.EventType}_{context.Message.DeviceId}").SelectId(context => Guid.NewGuid()));
            Event(() => EventCloseReceived, x => x.CorrelateBy(context => context.SagaDeviceId, context => $"{context.Message.EventType}_{context.Message.DeviceId}"));

            Event(() => EventClusterCreatedOrUpdated, x => x.CorrelateById(context => context.Message.EventCluster.EventClusterId));
            Event(() => EventClusterClosed, x => x.CorrelateById(context => context.Message.EventCluster.EventClusterId));

            Event(() => EventClusterCreateOrUpdateRequestedFault, x => x.CorrelateById(context => context.Message.Message.EventClusterId));

            //UI Update notification, can be ignored if saga doesn't exist anymore
            //Event(() => EventClusterUIUpdated, x => { x.CorrelateById(context => context.Message.CorrelationId); x.OnMissingInstance(m => m.Discard()); });

            Schedule(() => EventClusterLifespanElapsed, x => x.ExpirationId, x =>
            {
                x.Delay    = TimeSpan.FromMinutes(_configWorkflow.EventClusterLifespan);
                x.Received = e => e.CorrelateById(context => context.Message.EventClusterId);
            });
            #endregion

            //The event is submitted from Azure Service Queue
            Initially(
                When(EventReceived)
                .Then(context => context.Instance.EventType         = context.Data.EventType)
                .Then(context => context.Instance.DeviceId          = context.Data.DeviceId)
                .Then(context => context.Instance.LastEventReceived = context.Data.Date)
                .Then(context => context.Instance.SagaDeviceId      = $"{context.Data.EventType}_{context.Data.DeviceId}")
                .ThenAsync(context => Console.Out.WriteLineAsync($"EventProcessing-{context.Instance.CorrelationId}: Saga Initiated."))
                .ThenAsync(context => Console.Out.WriteLineAsync($"EventProcessing--{context.Instance.CorrelationId}: Event Received."))
                .If(new StateMachineCondition <EventProcessingState, IEventSagaReceived>(bc => bc.Data.EventType != "message"), x => x
                    .Schedule(EventClusterLifespanElapsed, p => new EventClusterLifespanElapsed()
            {
                EventClusterId = p.Instance.CorrelationId
            })
                    )
                .ThenAsync(context => context.Publish(new EventClusterCreateOrUpdateRequestedEvent()
            {
                EventClusterId = context.Instance.CorrelationId,
                DeviceId       = context.Data.DeviceId,
                EventType      = context.Data.EventType,
                Date           = context.Data.Date,
                Data           = context.Data.Data
            }))
                .TransitionTo(ListeningToEvents)
                );

            //State while the Event Cluster is being created
            During(ListeningToEvents,
                   //Any new event will be sent to the same saga
                   When(EventReceived)
                   .Then(context => context.Instance.LastEventReceived = context.Data.Date)
                   .ThenAsync(context => Console.Out.WriteLineAsync($"EventProcessing--{context.Instance.CorrelationId}: Event Received."))
                   .If(new StateMachineCondition <EventProcessingState, IEventSagaReceived>(bc => bc.Data.EventType != "message"), x => x
                       .Schedule(EventClusterLifespanElapsed, p => new EventClusterLifespanElapsed()
            {
                EventClusterId = p.Instance.CorrelationId
            })
                       )
                   .ThenAsync(context => context.Publish(new EventClusterCreateOrUpdateRequestedEvent()
            {
                EventClusterId = context.Instance.CorrelationId,
                DeviceId       = context.Data.DeviceId,
                EventType      = context.Data.EventType,
                Date           = context.Data.Date,
                Data           = context.Data.Data
            })),
                   //Result of an event being created or updated
                   When(EventClusterCreatedOrUpdated)
                   .ThenAsync(context => Console.Out.WriteLineAsync($"EventProcessing---{context.Instance.CorrelationId}: Event Cluster Created."))
                   .ThenAsync(context => Console.Out.WriteLineAsync($"EventProcessing---{context.Instance.CorrelationId}: Event Added in Cluster."))
                   //In case of a lot of events being sent at once, skip UI refresh on some events
                   .If(new StateMachineCondition <EventProcessingState, IEventClusterCreatedOrUpdated>(bc =>

                                                                                                       bc.Instance.LastEventReceived == bc.Data.LastEventDate || //If not new event before the reception, send update
                                                                                                       bc.Data.EventCluster.EventCount == 1 ||                   //If first event, send update
                                                                                                       bc.Data.EventCluster.EventCount % 10 == 0)                //If many events at the same time, send every 5 events
                       , x => x
                       .ThenAsync(context => context.Publish(new EventUIUpdateRequestedEvent()                                                                   //Update UI
            {
                CorrelationId  = context.Instance.CorrelationId,
                EventClusterUI = new EventClusterUIModel()
                {
                    EventCluster = context.Data.EventCluster,
                    UpdateType   = context.Data.EventCluster.EventCount > 1 ? EventClusterUpdateType.UpdateEventCluster.ToString() : EventClusterUpdateType.NewEventCluster.ToString()
                }
            }))

                       )
                   //Check response tagging, only for the first generated event
                   .If(new StateMachineCondition <EventProcessingState, IEventClusterCreatedOrUpdated>(bc =>
                                                                                                       bc.Data.EventCluster.EventCount == 1),
                       x => x
                       .ThenAsync(context => context.Publish(new ResponseTagNewEventClusterRequestedEvent()
            {
                EventClusterId          = context.Instance.CorrelationId,
                EventClusterGeolocation = context.Data.EventCluster.Device.Geolocation
            }))),

                   When(EventCloseReceived)
                   .ThenAsync(context => Console.Out.WriteLineAsync($"EventProcessing--{context.Instance.CorrelationId}: Event Cluster closing requested."))
                   .ThenAsync(context => context.Publish(new EventClusterCloseRequestedEvent()
            {
                EventClusterId = context.Instance.CorrelationId,
                ClosureDate    = DateTime.UtcNow,
                EndDate        = DateTime.UtcNow.AddMinutes(_configWorkflow.EventClusterCooldown)
            })),

                   When(EventClusterClosed)
                   .ThenAsync(context => Console.Out.WriteLineAsync($"EventProcessing---{context.Instance.CorrelationId}: Saga Closed."))
                   .ThenAsync(context => context.Publish(new EventUIUpdateRequestedEvent()
            {
                CorrelationId  = context.Instance.CorrelationId,
                EventClusterUI = new EventClusterUIModel()
                {
                    EventCluster = context.Data.EventCluster,
                    UpdateType   = EventClusterUpdateType.CloseEventCluster.ToString()
                }
            }))
                   .Finalize()
                   );

            //Stateless
            DuringAny(
                //No new event after a while, request to close the cluster
                When(EventClusterLifespanElapsed.Received)
                .ThenAsync(context => Console.Out.WriteLineAsync($"EventProcessing--{context.Instance.CorrelationId}: Event Cluster closing requested."))
                .ThenAsync(context => context.Publish(new EventClusterCloseRequestedEvent()
            {
                EventClusterId = context.Data.EventClusterId,
                ClosureDate    = DateTime.UtcNow,
                EndDate        = DateTime.UtcNow.AddMinutes(_configWorkflow.EventClusterCooldown)
            })),
                //Fault while creating/adding an event to cluster
                When(EventClusterCreateOrUpdateRequestedFault)
                .ThenAsync(context => Console.Out.WriteLineAsync($"EventProcessing---{context.Instance.CorrelationId}: !!FAULT!! Event Cluster: {context.Data.Message}"))
                //UI Update acknoledgement - Disabled for performance improvement
                //When(EventClusterUIUpdated)
                //    .ThenAsync(context => Console.Out.WriteLineAsync($"EventProcessing---{context.Instance.CorrelationId}: UI Updated."))
                );


            //Delete persisted saga after completion
            SetCompletedWhenFinalized();
        }