public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            var httpMethod          = context.HttpContext.Request.Method;
            var routeValues         = context.RouteData.Values;
            var workflowTypeEntries = await _workflowTypeRouteEntries.GetWorkflowRouteEntriesAsync(httpMethod, routeValues);

            var workflowEntries = await _workflowRouteEntries.GetWorkflowRouteEntriesAsync(httpMethod, routeValues);

            if (workflowTypeEntries.Any())
            {
                var workflowTypeIds = workflowTypeEntries.Select(x => Int32.Parse(x.WorkflowId)).ToList();
                var workflowTypes   = (await _workflowTypeStore.GetAsync(workflowTypeIds)).ToDictionary(x => x.Id);
                var correlationId   = routeValues.GetValue <string>("correlationid");

                foreach (var entry in workflowTypeEntries)
                {
                    if (workflowTypes.TryGetValue(Int32.Parse(entry.WorkflowId), out var workflowType))
                    {
                        var activity = workflowType.Activities.Single(x => x.ActivityId == entry.ActivityId);

                        if (activity.IsStart)
                        {
                            // If this is not a singleton workflow or there is not already an halted instance, start a new workflow.
                            if (!workflowType.IsSingleton || !await _workflowStore.HasHaltedInstanceAsync(workflowType.WorkflowTypeId))
                            {
                                await _workflowManager.StartWorkflowAsync(workflowType, activity, null, correlationId);
                            }
                        }
                    }
                }
            }

            if (workflowEntries.Any())
            {
                var workflowIds   = workflowEntries.Select(x => x.WorkflowId).ToList();
                var workflows     = (await _workflowStore.GetAsync(workflowIds)).ToDictionary(x => x.WorkflowId);
                var correlationId = routeValues.GetValue <string>("correlationid");

                foreach (var entry in workflowEntries)
                {
                    if (workflows.TryGetValue(entry.WorkflowId, out var workflow) &&
                        (String.IsNullOrWhiteSpace(correlationId) ||
                         workflow.CorrelationId == correlationId))
                    {
                        var blockingActivity = workflow.BlockingActivities.Single(x => x.ActivityId == entry.ActivityId);
                        await _workflowManager.ResumeWorkflowAsync(workflow, blockingActivity);
                    }
                }
            }

            await next();
        }
Exemplo n.º 2
0
        public async Task <IActionResult> Invoke(string token)
        {
            if (!_securityTokenService.TryDecryptToken <WorkflowPayload>(token, out var payload))
            {
                _logger.LogWarning("Invalid SAS token provided");
                return(NotFound());
            }

            // Get the workflow type.
            var workflowType = await _workflowTypeStore.GetAsync(payload.WorkflowId);

            if (workflowType == null)
            {
                if (_logger.IsEnabled(LogLevel.Warning))
                {
                    _logger.LogWarning("The provided workflow with ID '{WorkflowTypeId}' could not be found.", payload.WorkflowId);
                }

                return(NotFound());
            }

            if (!workflowType.IsEnabled)
            {
                if (_logger.IsEnabled(LogLevel.Warning))
                {
                    _logger.LogWarning("The provided workflow with ID '{WorkflowTypeId}' is not enabled.", payload.WorkflowId);
                }

                return(NotFound());
            }

            // Get the activity record using the activity ID provided by the token.
            var startActivity = workflowType.Activities.FirstOrDefault(x => x.ActivityId == payload.ActivityId);

            if (startActivity == null)
            {
                if (_logger.IsEnabled(LogLevel.Warning))
                {
                    _logger.LogWarning("The provided activity with ID '{ActivityId}' could not be found.", payload.ActivityId);
                }

                return(NotFound());
            }

            // Instantiate and bind an actual HttpRequestEvent object to check its settings.
            var httpRequestActivity = _activityLibrary.InstantiateActivity <HttpRequestEvent>(startActivity);

            if (httpRequestActivity == null)
            {
                if (_logger.IsEnabled(LogLevel.Warning))
                {
                    _logger.LogWarning("Activity with name '{ActivityName}' could not be found.", startActivity.Name);
                }

                return(NotFound());
            }

            // Check if the HttpRequestEvent is configured to perform antiforgery token validation. If so, perform the validation.
            if (httpRequestActivity.ValidateAntiforgeryToken && (!await _antiforgery.IsRequestValidAsync(HttpContext)))
            {
                _logger.LogWarning("Antiforgery token validation failed.");
                return(BadRequest());
            }

            // If the activity is a start activity, start a new workflow.
            if (startActivity.IsStart)
            {
                // If a singleton, try to acquire a lock per workflow type.
                (var locker, var locked) = await _distributedLock.TryAcquireWorkflowTypeLockAsync(workflowType);

                if (locked)
                {
                    await using var acquiredLock = locker;

                    // Check if this is not a workflow singleton or there's not already an halted instance on any activity.
                    if (!workflowType.IsSingleton || !await _workflowStore.HasHaltedInstanceAsync(workflowType.WorkflowTypeId))
                    {
                        if (_logger.IsEnabled(LogLevel.Debug))
                        {
                            _logger.LogDebug("Invoking new workflow of type '{WorkflowTypeId}' with start activity '{ActivityId}'", workflowType.WorkflowTypeId, startActivity.ActivityId);
                        }

                        await _workflowManager.StartWorkflowAsync(workflowType, startActivity);
                    }
                }
            }
            else
            {
                // Otherwise, we need to resume all halted workflows on this activity.
                var workflows = await _workflowStore.ListAsync(workflowType.WorkflowTypeId, new[] { startActivity.ActivityId });

                if (!workflows.Any())
                {
                    if (_logger.IsEnabled(LogLevel.Warning))
                    {
                        _logger.LogWarning("No workflow found that is blocked on activity '{ActivityId}'", startActivity.ActivityId);
                    }

                    return(NotFound());
                }

                foreach (var workflow in workflows)
                {
                    var blockingActivity = workflow.BlockingActivities.FirstOrDefault(x => x.ActivityId == startActivity.ActivityId);

                    if (blockingActivity != null)
                    {
                        if (_logger.IsEnabled(LogLevel.Debug))
                        {
                            _logger.LogDebug("Resuming workflow with ID '{WorkflowId}' on activity '{ActivityId}'", workflow.WorkflowId, blockingActivity.ActivityId);
                        }

                        // If atomic, try to acquire a lock per workflow instance.
                        (var locker, var locked) = await _distributedLock.TryAcquireWorkflowLockAsync(workflow);

                        if (!locked)
                        {
                            continue;
                        }

                        await using var acquiredLock = locker;

                        // If atomic, check if the workflow still exists.
                        var haltedWorkflow = workflow.IsAtomic ? await _workflowStore.GetAsync(workflow.WorkflowId) : workflow;

                        if (haltedWorkflow == null)
                        {
                            continue;
                        }

                        // And if it is still halted on this activity.
                        blockingActivity = haltedWorkflow.BlockingActivities.FirstOrDefault(x => x.ActivityId == startActivity.ActivityId);
                        if (blockingActivity != null)
                        {
                            await _workflowManager.ResumeWorkflowAsync(haltedWorkflow, blockingActivity);
                        }
                    }
                }
            }

            return(GetWorkflowActionResult());
        }
        public async Task <IActionResult> Invoke(string token)
        {
            if (!_securityTokenService.TryDecryptToken <WorkflowPayload>(token, out var payload))
            {
                _logger.LogWarning("Invalid SAS token provided");
                return(NotFound());
            }

            // Get the workflow type.
            var workflowType = await _workflowTypeStore.GetAsync(payload.WorkflowId);

            if (workflowType == null)
            {
                _logger.LogWarning("The provided workflow type with ID '{WorkflowTypeId}' could not be found.", payload.WorkflowId);
                return(NotFound());
            }

            // Get the activity record using the activity ID provided by the token.
            var startActivity = workflowType.Activities.FirstOrDefault(x => x.ActivityId == payload.ActivityId);

            if (startActivity == null)
            {
                _logger.LogWarning("The provided activity with ID '{ActivityId}' could not be found.", payload.ActivityId);
                return(NotFound());
            }

            // Instantiate and bind an actual HttpRequestEvent object to check its settings.
            var httpRequestActivity = _activityLibrary.InstantiateActivity <HttpRequestEvent>(startActivity);

            if (httpRequestActivity == null)
            {
                _logger.LogWarning("Activity with name '{ActivityName}' could not be found.", startActivity.Name);
                return(NotFound());
            }

            // Check if the HttpRequestEvent is configured to perform antiforgery token validation. If so, perform the validation.
            if (httpRequestActivity.ValidateAntiforgeryToken && (!await _antiforgery.IsRequestValidAsync(HttpContext)))
            {
                _logger.LogWarning("Antiforgery token validation failed.");
                return(BadRequest());
            }

            // If the activity is a start activity, start a new workflow.
            if (startActivity.IsStart)
            {
                _logger.LogDebug("Invoking new workflow of type {WorkflowTypeId} with start activity {ActivityId}", workflowType.WorkflowTypeId, startActivity.ActivityId);
                await _workflowManager.StartWorkflowAsync(workflowType, startActivity);
            }
            else
            {
                // Otherwise, we need to resume a halted workflow.
                var workflow = (await _workflowStore.ListAsync(workflowType.WorkflowTypeId, new[] { startActivity.ActivityId })).FirstOrDefault();

                if (workflow == null)
                {
                    _logger.LogWarning("No workflow found that is blocked on activity {ActivityId}", startActivity.ActivityId);
                    return(NotFound());
                }

                var blockingActivity = workflow.BlockingActivities.FirstOrDefault(x => x.ActivityId == startActivity.ActivityId);

                if (blockingActivity != null)
                {
                    _logger.LogDebug("Resuming workflow with ID {WorkflowId} on activity {ActivityId}", workflow.WorkflowId, blockingActivity.ActivityId);
                    await _workflowManager.ResumeWorkflowAsync(workflow, blockingActivity);
                }
            }

            return(GetWorkflowActionResult());
        }
Exemplo n.º 4
0
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            var httpMethod          = context.HttpContext.Request.Method;
            var routeValues         = context.RouteData.Values;
            var workflowTypeEntries = await _workflowTypeRouteEntries.GetWorkflowRouteEntriesAsync(httpMethod, routeValues);

            var workflowEntries = await _workflowRouteEntries.GetWorkflowRouteEntriesAsync(httpMethod, routeValues);

            if (workflowTypeEntries.Any())
            {
                var workflowTypeIds = workflowTypeEntries.Select(x => Int32.Parse(x.WorkflowId)).ToList();
                var workflowTypes   = (await _workflowTypeStore.GetAsync(workflowTypeIds)).ToDictionary(x => x.Id);
                var correlationId   = routeValues.GetValue <string>("correlationid");

                foreach (var entry in workflowTypeEntries)
                {
                    if (workflowTypes.TryGetValue(Int32.Parse(entry.WorkflowId), out var workflowType))
                    {
                        var activity = workflowType.Activities.Single(x => x.ActivityId == entry.ActivityId);

                        if (activity.IsStart)
                        {
                            // If a singleton, try to acquire a lock per workflow type.
                            (var locker, var locked) = await _distributedLock.TryAcquireWorkflowTypeLockAsync(workflowType);

                            if (!locked)
                            {
                                continue;
                            }

                            await using var acquiredLock = locker;

                            // Check if this is a workflow singleton and there's already an halted instance on any activity.
                            if (workflowType.IsSingleton && await _workflowStore.HasHaltedInstanceAsync(workflowType.WorkflowTypeId))
                            {
                                continue;
                            }

                            await _workflowManager.StartWorkflowAsync(workflowType, activity, null, correlationId);
                        }
                    }
                }
            }

            if (workflowEntries.Any())
            {
                var workflowIds   = workflowEntries.Select(x => x.WorkflowId).ToList();
                var workflows     = (await _workflowStore.GetAsync(workflowIds)).ToDictionary(x => x.WorkflowId);
                var correlationId = routeValues.GetValue <string>("correlationid");

                foreach (var entry in workflowEntries)
                {
                    if (workflows.TryGetValue(entry.WorkflowId, out var workflow) &&
                        (String.IsNullOrWhiteSpace(correlationId) ||
                         workflow.CorrelationId == correlationId))
                    {
                        // If atomic, try to acquire a lock per workflow instance.
                        (var locker, var locked) = await _distributedLock.TryAcquireWorkflowLockAsync(workflow);

                        if (!locked)
                        {
                            continue;
                        }

                        await using var acquiredLock = locker;

                        // If atomic, check if the workflow still exists and is still correlated.
                        var haltedWorkflow = workflow.IsAtomic ? await _workflowStore.GetAsync(workflow.Id) : workflow;

                        if (haltedWorkflow == null || (!String.IsNullOrWhiteSpace(correlationId) && haltedWorkflow.CorrelationId != correlationId))
                        {
                            continue;
                        }

                        // And if it is still halted on this activity.
                        var blockingActivity = haltedWorkflow.BlockingActivities.SingleOrDefault(x => x.ActivityId == entry.ActivityId);
                        if (blockingActivity != null)
                        {
                            await _workflowManager.ResumeWorkflowAsync(haltedWorkflow, blockingActivity);
                        }
                    }
                }
            }

            await next();
        }