Exemplo n.º 1
0
        public async Task <CommandResult> Handle(AppendStepLogCommand request, CancellationToken cancellationToken)
        {
            var stopwatch = new Stopwatch();

            stopwatch.Start();
            var step = await _entitiesRepository.GetFirstOrDefaultAsync <Step>(e => e.Id == request.StepId);

            if (StepStatuses.IsCompleteStatus(step.Status))
            {
                throw new InvalidStepStatusException("Cannot append log to step, step status is complete with " + step.Status);
            }

            step.UpdateJournal(new Domain.Entities.JournalEntries.JournalEntry()
            {
                CreatedBy = request.CreatedBy,
                CreatedOn = DateTime.UtcNow,
                Updates   = new List <Domain.ValueObjects.Update>()
                {
                    new Update()
                    {
                        Type      = UpdateType.Append,
                        FieldName = "logs",
                        Value     = new StepLog()
                        {
                            CreatedOn = DateTime.UtcNow,
                            Message   = request.Log
                        },
                    }
                }
            });

            //await _entitiesRepository.UpdateStep(step);

            var createdWorkflowTemplateId = await _node.Handle(new AddShardWriteOperation()
            {
                Data             = step,
                WaitForSafeWrite = true,
                Operation        = ConsensusCore.Domain.Enums.ShardOperationOptions.Update
            });

            return(new CommandResult()
            {
                ElapsedMs = stopwatch.ElapsedMilliseconds,
                ObjectRefId = step.Id.ToString(),
                Type = CommandResultTypes.Update
            });
        }
Exemplo n.º 2
0
        public async Task <IActionResult> Create(CreateStepCommand command, bool?wait_for_completion, string timeout = "30s")
        {
            var stopwatch = new Stopwatch();

            stopwatch.Start();
            try
            {
                command.CreatedBy = ClaimsUtility.GetId(User);
                var result = await Mediator.Send(command);

                Step step = (await Mediator.Send(new GetEntityQuery <Step>()
                {
                    Expression = s => s.Id == new Guid(result.ObjectRefId),
                    Exclude = (s) => s.Journal
                })).Result;


                if (wait_for_completion.HasValue && wait_for_completion.Value)
                {
                    var ms = DateTimeMathsUtility.GetMs(timeout);

                    while (!StepStatuses.IsCompleteStatus(step.Status) && stopwatch.ElapsedMilliseconds < ms)
                    {
                        step = (await Mediator.Send(new GetEntityQuery <Step>()
                        {
                            Expression = s => s.Id == new Guid(result.ObjectRefId)
                        })).Result;
                    }
                }


                return(Ok(new HttpCommandResult <Step>("step", result, step)));
            }
            catch (BaseException e)
            {
                Logger.LogError(e.Message);
                stopwatch.Stop();
                return(BadRequest(e.ToExceptionResult(stopwatch.ElapsedMilliseconds)));
            }
        }
Exemplo n.º 3
0
        public async Task <CommandResult> Handle(CompleteStepCommand request, CancellationToken cancellationToken)
        {
            var stopwatch = new Stopwatch();

            stopwatch.Start();

            var stepToComplete = await _entitiesRepository.GetFirstOrDefaultAsync <Step>(s => s.Id == request.Id);

            if (stepToComplete.IsComplete())
            {
                // if (JsonConvert.SerializeObject(stepToComplete.Outputs) == JsonConvert.SerializeObject(request.Outputs))
                // {
                Logger.LogWarning("Step " + request.Id + " is already complete with status " + stepToComplete.Status + ".");
                throw new DuplicateStepUpdateException();
                //}
            }

            if (!StepStatuses.IsCompleteStatus(request.Status))
            {
                throw new InvalidStepStatusInputException(request.Status + " is not a valid completion status.");
            }

            var stepTemplate = await _entitiesRepository.GetFirstOrDefaultAsync <StepTemplate>(st => st.ReferenceId == stepToComplete.StepTemplateId);

            if (request.Outputs == null)
            {
                request.Outputs = new Dictionary <string, object>();
            }

            var botkey = await _entitiesRepository.GetFirstOrDefaultAsync <BotKey>(bk => bk.Id == request.BotId);

            var unencryptedOuputs = DynamicDataUtility.DecryptDynamicData(stepTemplate.OutputDefinitions, request.Outputs, EncryptionProtocol.RSA, botkey.PublicEncryptionKey, true);
            var finalUpdate       = new List <Domain.ValueObjects.Update>()
            {
                new Update()
                {
                    Type      = UpdateType.Override,
                    FieldName = "status",
                    Value     = request.Status,
                },
                new Update()
                {
                    Type      = UpdateType.Override,
                    FieldName = "outputs",
                    Value     = DynamicDataUtility.EncryptDynamicData(stepTemplate.OutputDefinitions, unencryptedOuputs, EncryptionProtocol.AES256, ClusterStateService.GetEncryptionKey())
                },
                new Update()
                {
                    Type      = UpdateType.Override,
                    FieldName = "statuscode",
                    Value     = request.StatusCode,
                }
            };

            if (request.Log != null)
            {
                finalUpdate.Add(
                    new Update()
                {
                    Type      = UpdateType.Append,
                    FieldName = "logs",
                    Value     = new StepLog()
                    {
                        CreatedOn = DateTime.UtcNow,
                        Message   = request.Log
                    },
                });
            }

            stepToComplete.UpdateJournal(new Domain.Entities.JournalEntries.JournalEntry()
            {
                CreatedOn = DateTime.UtcNow,
                CreatedBy = request.CreatedBy,
                Updates   = finalUpdate
            });

            await _node.Handle(new AddShardWriteOperation()
            {
                Data             = stepToComplete,
                WaitForSafeWrite = true,
                Operation        = ConsensusCore.Domain.Enums.ShardOperationOptions.Update
            });

            Logger.LogInformation("Updated step " + stepToComplete.Id + " with status " + stepToComplete.Status);

            var updatedStep = await _entitiesRepository.GetFirstOrDefaultAsync <Step>(s => s.Id == stepToComplete.Id);

            if (updatedStep.WorkflowId != null)
            {
                var workflow = await _entitiesRepository.GetFirstOrDefaultAsync <Workflow>(w => w.Id == updatedStep.WorkflowId.Value);

                if (workflow == null)
                {
                    throw new MissingWorkflowException("Failed to continue workflow " + updatedStep.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 continue workflow " + updatedStep.WorkflowId + " workflow template " + workflow.WorkflowTemplateId + ".");
                }

                //Get all the steps related to this task
                var workflowSteps = (await _entitiesRepository.GetAsync <Step>(s => s.WorkflowId == updatedStep.WorkflowId.Value)).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());
                }
                //Keep track of whether a step has been added
                bool stepCreated = false;

                //Evaluate all logic blocks that have not been completed
                var logicBlocks = workflowTemplate.LogicBlocks.Where(lb => !workflow.CompletedLogicBlocks.Contains(lb.Key) && lb.Value.Dependencies.ContainsStep(updatedStep.Name)).ToList();

                foreach (var logicBlock in logicBlocks)
                {
                    Logger.LogInformation("Processing logic block " + logicBlock.Key + " for workflow " + workflow.Id);
                    var  lockId       = Guid.NewGuid();
                    bool lockObtained = false;
                    while (!lockObtained)
                    {
                        while (_clusterStateService.IsLogicBlockLocked(updatedStep.WorkflowId.Value, logicBlock.Key))
                        {
                            Console.WriteLine("Found " + ("workflow:" + updatedStep.WorkflowId + ">logicBlock:" + logicBlock) + " Lock");
                            await Task.Delay(1000);
                        }

                        int entryNumber = await _clusterStateService.LockLogicBlock(lockId, updatedStep.WorkflowId.Value, logicBlock.Key);

                        //Check whether you got the lock
                        lockObtained = _clusterStateService.WasLockObtained(lockId, updatedStep.WorkflowId.Value, logicBlock.Key);
                    }

                    //When the logic block is released, recheck whether this logic block has been evaluated
                    workflow = await _entitiesRepository.GetFirstOrDefaultAsync <Workflow>(w => w.Id == updatedStep.WorkflowId.Value);

                    workflow.Inputs = DynamicDataUtility.DecryptDynamicData(workflowTemplate.InputDefinitions, workflow.Inputs, EncryptionProtocol.AES256, ClusterStateService.GetEncryptionKey());

                    //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)
                            {
                                Logger.LogCritical("Encountered duplicate subsequent step for workflow " + workflow.Id + ". Ignoring the generation of duplicate.");
                            }
                            else
                            {
                                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
                                });
                            }
                        }

                        //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, updatedStep.WorkflowId.Value, 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 == updatedStep.WorkflowId.Value)).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
                    });
                }
            }

            return(new CommandResult()
            {
                ObjectRefId = request.Id.ToString(),
                ElapsedMs = stopwatch.ElapsedMilliseconds,
                Type = CommandResultTypes.Update
            });
        }