public override Task <UpdateStepRequest> HandleStep(Step step) { var updateRequest = new UpdateStepRequest() { Id = step.Id }; switch (step.StepTemplateId) { case "Fibonacci_stepTemplate:0": var completeStep = true; if (testSuspension) { var n = random.Next(0, 2); if (n == 1) { completeStep = false; } var logSuccessfullySent = SendStepLog(step.Id, "Test").GetAwaiter().GetResult(); } if (completeStep) { var result = CalculateFibonacci((Int64)DynamicDataUtility.GetData(step.Inputs, "n-1").Value, (Int64)(DynamicDataUtility.GetData(step.Inputs, "n-2").Value)); updateRequest.Outputs = new Dictionary <string, object>() { { "n", result } }; updateRequest.Status = StepStatuses.Successful; return(Task.FromResult(updateRequest)); } else { updateRequest.Status = StepStatuses.Suspended; return(Task.FromResult(updateRequest)); } case "Pass_Password:0": if (((string)(DynamicDataUtility.GetData(step.Inputs, "secret").Value) == "This is a test")) { { updateRequest.Outputs = new Dictionary <string, object>() { { "secret", (string)(DynamicDataUtility.GetData(step.Inputs, "secret").Value) } }; updateRequest.Status = StepStatuses.Successful; return(Task.FromResult(updateRequest)); } } else { throw new Exception("Failed to get password."); } } throw new NotImplementedException(); }
public async Task <CommandResult <Workflow> > Handle(CreateWorkflowCommand request, CancellationToken cancellationToken) { var stopwatch = new Stopwatch(); stopwatch.Start(); WorkflowTemplate template = await _entitiesRepository.GetFirstOrDefaultAsync <WorkflowTemplate>(wft => wft.ReferenceId == request.WorkflowTemplateId); if (template == null) { throw new WorkflowTemplateNotFoundException("Workflow Template " + request.WorkflowTemplateId + " not found."); } if (template.InputDefinitions.Count() < request.Inputs.Count()) { throw new InvalidInputsException("Invalid number of inputs, number passed was " + request.Inputs.Count() + " which is less then defined " + template.InputDefinitions.Count()); } var verifiedWorkflowInputs = new Dictionary <string, object>(); if (template.InputDefinitions != null) { foreach (var input in template.InputDefinitions) { if (!request.Inputs.ContainsKey(input.Key)) { throw new MissingInputException("Workflow input data is missing " + input.Key); } } foreach (var input in request.Inputs) { if (template.InputDefinitions.ContainsKey(input.Key) && template.InputDefinitions[input.Key].Type == InputDataTypes.Secret && !InputDataUtility.IsInputReference(input, out _, out _)) { verifiedWorkflowInputs.Add(input.Key.ToLower(), SecurityUtility.SymmetricallyEncrypt((string)input.Value, ClusterStateService.GetEncryptionKey())); } else { verifiedWorkflowInputs.Add(input.Key.ToLower(), input.Value); } } } var createdWorkflowId = Guid.NewGuid(); var startingLogicBlock = template.LogicBlocks.Where(lb => lb.Value.Dependencies.Evaluate(new List <Step>())).ToList(); var createdWorkflowTemplateId = await _node.Handle(new AddShardWriteOperation() { Data = new Domain.Entities.Workflows.Workflow( createdWorkflowId, request.WorkflowTemplateId, verifiedWorkflowInputs, //Encrypted inputs request.Name, request.CreatedBy, DateTime.UtcNow, request.ExecutionTemplateId, request.ExecutionScheduleId ), WaitForSafeWrite = true, Operation = ConsensusCore.Domain.Enums.ShardOperationOptions.Create }); var workflow = await _entitiesRepository.GetFirstOrDefaultAsync <Workflow>(w => w.Id == createdWorkflowId); //When there are no conditions to be met // Needs to happen before first step is added DateTimeOffset WorkflowStartTime = DateTime.Now; foreach (var block in startingLogicBlock) { try { foreach (var subBlock in block.Value.SubsequentSteps) { var newStepTemplate = await _entitiesRepository.GetFirstOrDefaultAsync <StepTemplate>(st => st.ReferenceId == subBlock.Value.StepTemplateId); if (newStepTemplate == null) { throw new StepTemplateNotFoundException("Template " + subBlock.Value.StepTemplateId + " not found."); } var verifiedInputs = new Dictionary <string, object>(); foreach (var mapping in subBlock.Value.Mappings) { string mappedValue = ""; if ((mapping.Value.DefaultValue != null && (mapping.Value.OutputReferences == null || mapping.Value.OutputReferences.Count() == 0)) || (mapping.Value.DefaultValue != null && mapping.Value.OutputReferences.First() != null && mapping.Value.DefaultValue.Priority > mapping.Value.OutputReferences.First().Priority)) { // Change the ID to match the output verifiedInputs.Add(mapping.Key, mapping.Value.DefaultValue.Value); } else if (mapping.Value.OutputReferences != null) { verifiedInputs.Add(mapping.Key, DynamicDataUtility.GetData(request.Inputs, mapping.Value.OutputReferences.First().OutputId).Value); } } await _mediator.Send(new CreateStepCommand() { StepTemplateId = subBlock.Value.StepTemplateId, CreatedBy = SystemUsers.QUEUE_MANAGER, Description = null, Inputs = verifiedInputs, WorkflowId = createdWorkflowId, Name = subBlock.Key }); } workflow.UpdateJournal( new JournalEntry() { CreatedBy = request.CreatedBy, CreatedOn = DateTime.UtcNow, Updates = new List <Update>() { new Update() { FieldName = "completedlogicblocks", Type = UpdateType.Append, Value = block.Key //Add the logic block } } }); await _node.Handle(new AddShardWriteOperation() { Data = workflow, WaitForSafeWrite = true, Operation = ConsensusCore.Domain.Enums.ShardOperationOptions.Update }); } catch (Exception e) { _logger.LogCritical("Failed to action logic block " + block.Key + " with error " + e.Message + Environment.NewLine + e.StackTrace); } } stopwatch.Stop(); return(new CommandResult <Workflow>() { Result = workflow, ObjectRefId = createdWorkflowId.ToString(), ElapsedMs = stopwatch.ElapsedMilliseconds, Type = CommandResultTypes.Create }); }
public async override Task <UpdateStepRequest> HandleStep(Step step) { var updateRequest = new UpdateStepRequest() { Id = step.Id }; switch (step.StepTemplateId) { case "_GenerateSystemReport:0": var totalStepCount = (await _mediator.Send(new GetEntitiesQuery <Step> { Page = 0, Size = 0, })).Count; var totalUnassignedStepCount = (await _mediator.Send(new GetEntitiesQuery <Step> { Page = 0, Size = 0, Expression = e => e.Status == StepStatuses.Unassigned })).Count; var totalActiveBotCount = (await _mediator.Send(new GetEntitiesQuery <BotKey> { Page = 0, Size = 0, Expression = e => e.IsDisabled == false })).Count; updateRequest.Outputs = new Dictionary <string, object>() { { "report", "Total Steps: " + totalStepCount + ", Total Unassigned Steps:" + totalUnassignedStepCount + ", Total Active Bots: " + totalActiveBotCount }, { "slack_report", JsonConvert.SerializeObject(new[] { new { type = "section", fields = new[] { new { text = "Total Unassigned Steps: " + totalUnassignedStepCount, type = "mrkdwn" } } }, new { type = "section", fields = new[] { new { text = "Total Steps: " + totalStepCount, type = "mrkdwn" } } }, new { type = "section", fields = new[] { new { text = "Total Active Bots: " + totalActiveBotCount, type = "mrkdwn" } } } }) }, { "markdown", "" } }; updateRequest.Status = StepStatuses.Successful; updateRequest.StatusCode = 0; return(updateRequest); case "_SendSlackMessage:0": var client = new SlackClient(_clientFactory); await client.PostMessage((string)DynamicDataUtility.GetData(step.Inputs, "webhook_url").Value, new { username = (string)DynamicDataUtility.GetData(step.Inputs, "from").Value, icon_emoji = step.Inputs.ContainsKey("icon_emoji") ? (string)DynamicDataUtility.GetData(step.Inputs, "icon_emoji").Value : null, icon_url = step.Inputs.ContainsKey("icon_url") ? (string)DynamicDataUtility.GetData(step.Inputs, "icon_url").Value : null, channel = step.Inputs.ContainsKey("channel") ? (string)DynamicDataUtility.GetData(step.Inputs, "channel").Value : null, blocks = step.Inputs.ContainsKey("blocks") ? JsonConvert.DeserializeObject((string)DynamicDataUtility.GetData(step.Inputs, "blocks").Value) : null, text = step.Inputs.ContainsKey("text") ? (string)DynamicDataUtility.GetData(step.Inputs, "text").Value : null }); updateRequest.Status = StepStatuses.Successful; return(updateRequest); } updateRequest.Status = StepStatuses.Error; updateRequest.Log = "Bot does not have a catch for " + step.StepTemplateId; return(updateRequest); }
public async Task <CommandResult> Handle(ScanWorkflowCommand request, CancellationToken cancellationToken) { var stopwatch = new Stopwatch(); stopwatch.Start(); List <string> messages = new List <string>(); bool workflowStillRunning = false; var workflow = await _entitiesRepository.GetFirstOrDefaultAsync <Workflow>(w => w.Id == request.WorkflowId); if (workflow == null) { throw new MissingWorkflowException("Failed to find workflow " + request.WorkflowId + " as workflow does not exist."); } //Get the workflow template var workflowTemplate = await _entitiesRepository.GetFirstOrDefaultAsync <WorkflowTemplate>(wt => wt.ReferenceId == workflow.WorkflowTemplateId); if (workflowTemplate == null) { throw new WorkflowTemplateNotFoundException("Failed to scan workflow " + request.WorkflowId + " workflow template " + workflow.WorkflowTemplateId + "."); } //Get all the steps related to this task var workflowSteps = (await _entitiesRepository.GetAsync <Step>(s => s.WorkflowId == request.WorkflowId)).ToList(); foreach (var workflowStep in workflowSteps) { workflowStep.Outputs = DynamicDataUtility.DecryptDynamicData((await _entitiesRepository.GetFirstOrDefaultAsync <StepTemplate>(st => st.ReferenceId == workflowStep.StepTemplateId)).OutputDefinitions, workflowStep.Outputs, EncryptionProtocol.AES256, ClusterStateService.GetEncryptionKey()); if (!workflowStep.IsComplete()) { messages.Add("Workflow step " + workflowStep.Id + " (" + workflowStep.Name + ")" + " is running."); workflowStillRunning = true; } } bool stepCreated = false; if (!workflowStillRunning) { if (workflow.CompletedLogicBlocks == null) { workflow.CompletedLogicBlocks = new List <string>(); } //Evaluate all logic blocks that have not been completed var logicBlocks = workflowTemplate.LogicBlocks.Where(lb => !workflow.CompletedLogicBlocks.Contains(lb.Key)).ToList(); foreach (var logicBlock in logicBlocks) { var lockId = Guid.NewGuid(); bool lockObtained = false; while (!lockObtained) { while (_clusterStateService.IsLogicBlockLocked(request.WorkflowId, logicBlock.Key)) { Console.WriteLine("Found " + ("workflow:" + request.WorkflowId + ">logicBlock:" + logicBlock) + " Lock"); await Task.Delay(1000); } int entryNumber = await _clusterStateService.LockLogicBlock(lockId, request.WorkflowId, logicBlock.Key); //Check whether you got the lock lockObtained = _clusterStateService.WasLockObtained(lockId, request.WorkflowId, logicBlock.Key); } //When the logic block is released, recheck whether this logic block has been evaluated workflow = await _entitiesRepository.GetFirstOrDefaultAsync <Workflow>(w => w.Id == request.WorkflowId); workflow.Inputs = DynamicDataUtility.DecryptDynamicData(workflowTemplate.InputDefinitions, workflow.Inputs, EncryptionProtocol.AES256, ClusterStateService.GetEncryptionKey()); if (workflow.CompletedLogicBlocks == null) { workflow.CompletedLogicBlocks = new List <string>(); } //If the logic block is ready to be processed, submit the steps if (logicBlock.Value.Dependencies.Evaluate(workflowSteps) && !workflow.CompletedLogicBlocks.Contains(logicBlock.Key)) { foreach (var substep in logicBlock.Value.SubsequentSteps) { if (workflowSteps.Where(s => s.Name == substep.Key).Count() == 0) { var verifiedInputs = new Dictionary <string, object>(); foreach (var mapping in substep.Value.Mappings) { //find the mapping with the highest priority var highestPriorityReference = WorkflowTemplateUtility.GetHighestPriorityReference(mapping.Value.OutputReferences, workflowSteps.ToArray()); //if the highest priority reference is null, there are no mappings if (highestPriorityReference == null && mapping.Value.DefaultValue == null) { } // If default value is higher priority else if (mapping.Value.DefaultValue != null && (highestPriorityReference == null || highestPriorityReference.Priority < mapping.Value.DefaultValue.Priority)) { verifiedInputs.Add(mapping.Key, mapping.Value.DefaultValue.Value); } // If the step ref is not -1 it is a step in the array but the workflow else if (highestPriorityReference.StepName != ReservedValues.WorkflowStartStepName) { var parentStep = workflowSteps.Where(ss => ss.Name == highestPriorityReference.StepName).FirstOrDefault(); //If there is a AND and there is no parent step, throw a error if (parentStep == null) { throw new InvalidWorkflowProcessingException("Missing source for mapping " + mapping.Key + " from step " + highestPriorityReference.StepName); } else { if (parentStep != null) { try { // Get the output value based on the pre-requisite id var outPutValue = DynamicDataUtility.GetData(parentStep.Outputs, highestPriorityReference.OutputId); // Validate it is in the definition verifiedInputs.Add(mapping.Key, outPutValue.Value); } catch (Exception e) { //TO DO Move this to logger Console.WriteLine("Found error at mapping " + mapping.Key + " for step " + substep.Key); throw e; } } } } //Get the value from the workflow ref else { // Validate it is in the definition verifiedInputs.Add(mapping.Key, DynamicDataUtility.GetData(workflow.Inputs, highestPriorityReference.OutputId).Value); } } stepCreated = true; //Add the step TODO, add step priority await _mediator.Send(new CreateStepCommand() { StepTemplateId = substep.Value.StepTemplateId, CreatedBy = SystemUsers.QUEUE_MANAGER, Inputs = verifiedInputs, WorkflowId = workflow.Id, Name = substep.Key //create the step with the right subsequent step }); messages.Add("Started workflow step " + substep.Key); } } //Mark it as evaluated workflow.UpdateJournal( new JournalEntry() { CreatedBy = request.CreatedBy, CreatedOn = DateTime.UtcNow, Updates = new List <Update>() { new Update() { FieldName = "completedlogicblocks", Type = UpdateType.Append, Value = logicBlock.Key } } }); //await _workflowsRepository.UpdateWorkflow(workflow); await _node.Handle(new AddShardWriteOperation() { Data = workflow, WaitForSafeWrite = true, Operation = ConsensusCore.Domain.Enums.ShardOperationOptions.Update }); } await _clusterStateService.UnlockLogicBlock(lockId, request.WorkflowId, logicBlock.Key); } //Check if there are no longer any steps that are unassigned or assigned var workflowStatus = workflow.Status; workflowSteps = (await _entitiesRepository.GetAsync <Step>(s => s.WorkflowId == request.WorkflowId)).ToList(); var highestStatus = StepStatuses.GetHighestPriority(workflowSteps.Select(s => s.Status).ToArray()); var newWorkflowStatus = stepCreated ? WorkflowStatuses.ConvertStepStatusToWorkflowStatus(StepStatuses.Unassigned) : WorkflowStatuses.ConvertStepStatusToWorkflowStatus(highestStatus); if (newWorkflowStatus != workflow.Status) { workflow.UpdateJournal( new JournalEntry() { CreatedBy = request.CreatedBy, CreatedOn = DateTime.UtcNow, Updates = new List <Update>() { new Update() { FieldName = "status", Type = UpdateType.Override, Value = newWorkflowStatus } } }); //await _workflowsRepository.UpdateWorkflow(workflow); await _node.Handle(new AddShardWriteOperation() { Data = workflow, WaitForSafeWrite = true, Operation = ConsensusCore.Domain.Enums.ShardOperationOptions.Update }); messages.Add("Updated workflow status " + newWorkflowStatus + "."); } } return(new CommandResult() { ObjectRefId = request.WorkflowId.ToString(), ElapsedMs = stopwatch.ElapsedMilliseconds, Type = CommandResultTypes.Update, IsSuccessful = true, Messages = messages.ToArray() }); }