using System.Linq; using System.Threading; using System.Threading.Tasks; using Elsa.Activities.Http.Models; using Elsa.Activities.Http.RequestHandlers.Results; using Elsa.Activities.Http.Services; using Elsa.Models; using Elsa.Persistence; using Elsa.Services; using Microsoft.AspNetCore.Http; namespace Elsa.Activities.Http.RequestHandlers.Handlers { public class SignalRequestHandler : IRequestHandler { private readonly HttpContext httpContext; private readonly ITokenService tokenService; private readonly IWorkflowRunner workflowRunner; private readonly IWorkflowRegistry workflowRegistry; private readonly IWorkflowFactory workflowFactory; private readonly IWorkflowInstanceStore workflowInstanceStore; private readonly CancellationToken cancellationToken; public SignalRequestHandler( IHttpContextAccessor httpContextAccessor, ITokenService tokenService, IWorkflowRunner workflowRunner, IWorkflowRegistry workflowRegistry, IWorkflowFactory workflowFactory, IWorkflowInstanceStore workflowInstanceStore) { httpContext = httpContextAccessor.HttpContext; this.tokenService = tokenService; this.workflowRunner = workflowRunner; this.workflowRegistry = workflowRegistry; this.workflowFactory = workflowFactory; this.workflowInstanceStore = workflowInstanceStore; cancellationToken = httpContext.RequestAborted; } public async Task HandleRequestAsync() { var signal = DecryptToken(); if (signal == null) return new NotFoundResult(); var workflowInstance = await GetWorkflowInstanceAsync(signal); if (workflowInstance == null) return new NotFoundResult(); if (!CheckIfExecuting(workflowInstance)) return new BadRequestResult($"Cannot signal a workflow with status other than {WorkflowStatus.Running}. Actual workflow status: {workflowInstance.Status}."); await ResumeWorkflowAsync(workflowInstance, signal); return new EmptyResult(); } private Signal DecryptToken() { var token = httpContext.Request.Query["token"]; return tokenService.TryDecryptToken(token, out Signal signal) ? signal : default; } private async Task GetWorkflowInstanceAsync(Signal signal) => await workflowInstanceStore.GetByIdAsync(signal.WorkflowInstanceId, cancellationToken); private bool CheckIfExecuting(WorkflowInstance workflowInstance) => workflowInstance.Status == WorkflowStatus.Running; private async Task ResumeWorkflowAsync(WorkflowInstance workflowInstance, Signal signal) { var input = new Variables(); input.SetVariable("Signal", signal.Name); var workflowDefinition = await workflowRegistry.GetWorkflowDefinitionAsync( workflowInstance.DefinitionId, VersionOptions.SpecificVersion(workflowInstance.Version), cancellationToken); var workflow = workflowFactory.CreateWorkflow(workflowDefinition, input, workflowInstance); var blockingSignalActivities = workflow.BlockingActivities.ToList(); await workflowRunner.ResumeAsync(workflow, blockingSignalActivities, cancellationToken); } } }