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 Task <CommandResult <Step> > Handle(AssignStepCommand request, CancellationToken cancellationToken) { var stopwatch = new Stopwatch(); stopwatch.Start(); List <Guid> ignoreUnassignedSteps = new List <Guid>(); if (_clusterStateService.GetSettings.AssignmentEnabled) { var assignedStepSuccessfully = false; Step unassignedStep = null; var dateChecked = DateTime.UtcNow; BotKey botkey; if (!_cache.TryGetValue(request.BotId, out botkey)) { // Set cache options. var cacheEntryOptions = new MemoryCacheEntryOptions() // Keep in cache for this time, reset time if accessed. .SetSlidingExpiration(TimeSpan.FromSeconds(10)); botkey = await _entitiesRepository.GetFirstOrDefaultAsync <BotKey>(bk => bk.Id == request.BotId); // Save data in cache. _cache.Set(request.BotId, botkey, cacheEntryOptions); } if (botkey.IsDisabled) { return(new CommandResult <Step>(new BotKeyAssignmentException("Bot " + botkey.Id + " is disabled.")) { Type = CommandResultTypes.Update, ElapsedMs = stopwatch.ElapsedMilliseconds }); } ignoreUnassignedSteps.AddRange(_clusterStateService.GetState().Locks.Where(l => l.Key.Contains("_object")).Select(ol => new Guid(ol.Key.Split(':').Last()))); do { unassignedStep = (await _entitiesRepository.GetAsync <Step>(s => s.Status == StepStatuses.Unassigned && request.StepTemplateIds.Contains(s.StepTemplateId) && !ignoreUnassignedSteps.Contains(s.Id), null, "CreatedOn:1", 1, 0)).FirstOrDefault(); if (unassignedStep != null) { var assigned = await _node.Handle(new RequestDataShard() { Type = unassignedStep.ShardType, ObjectId = unassignedStep.Id, CreateLock = true, LockTimeoutMs = 10000 }); //Apply a lock on the item if (assigned != null && assigned.IsSuccessful && assigned.AppliedLocked) { //Real values to pass to the Microservice Dictionary <string, object> realAssignedValues = new Dictionary <string, object>(); //Inputs that have been converted to reference expression Dictionary <string, object> convertedInputs = new Dictionary <string, object>(); var template = await _entitiesRepository.GetFirstOrDefaultAsync <StepTemplate>(st => st.ReferenceId == unassignedStep.StepTemplateId); try { //This should not throw a error externally, the server should loop to the next one and log a error if (unassignedStep.Status != StepStatuses.Unassigned) { throw new InvalidStepQueueException("You cannot assign step " + unassignedStep.Id + " as it is not unassigned."); } bool inputsUpdated = false; foreach (var input in unassignedStep.Inputs) { string convertedValue = ""; bool isReferenceByValue = false; var isReference = InputDataUtility.IsInputReference(input, out convertedValue, out isReferenceByValue); if (input.Value is string && ((string)input.Value).Length > 1) { if (isReference) { //Copy by reference if (isReferenceByValue) { var foundGlobalValue = await _entitiesRepository.GetFirstOrDefaultAsync <GlobalValue>(gv => gv.Name == convertedValue); if (foundGlobalValue == null) { Logger.LogWarning("No global value was found for value " + input.Value); realAssignedValues.Add(input.Key, null); convertedInputs.Add(input.Key, input.Value + ":?"); } else if (foundGlobalValue.Type != template.InputDefinitions[input.Key].Type) { Logger.LogWarning("Global value was found for value " + input.Value + " however they are different types. " + template.InputDefinitions[input.Key].Type + " vs " + foundGlobalValue.Type); realAssignedValues.Add(input.Key, null); convertedInputs.Add(input.Key, input.Value + ":?"); } else { realAssignedValues.Add(input.Key, foundGlobalValue.Value); convertedInputs.Add(input.Key, input.Value + ":" + foundGlobalValue.Journal.GetCurrentChainId()); } } //copy by value else { var foundGlobalValue = await _entitiesRepository.GetFirstOrDefaultAsync <GlobalValue>(gv => gv.Name == convertedValue); if (foundGlobalValue == null) { Logger.LogWarning("No global value was found for value " + input.Value); realAssignedValues.Add(input.Key, null); convertedInputs.Add(input.Key, null); } else if (foundGlobalValue.Type != template.InputDefinitions[input.Key].Type) { Logger.LogWarning("Global value was found for value " + input.Value + " however they are different types. " + template.InputDefinitions[input.Key].Type + " vs " + foundGlobalValue.Type); realAssignedValues.Add(input.Key, null); convertedInputs.Add(input.Key, null); } else { realAssignedValues.Add(input.Key, foundGlobalValue.Value); convertedInputs.Add(input.Key, foundGlobalValue.Value); } } inputsUpdated = true; } else if (input.Value is string && ((string)input.Value).Length > 1 && ((string)input.Value).First() == '\\') { var escapedCommand = ((string)input.Value); //The $ is escaped realAssignedValues.Add(input.Key, ((string)input.Value).Substring(1, escapedCommand.Length - 1)); convertedInputs.Add(input.Key, input.Value); inputsUpdated = true; } else { realAssignedValues.Add(input.Key, input.Value); convertedInputs.Add(input.Key, input.Value); } } else { realAssignedValues.Add(input.Key, input.Value); convertedInputs.Add(input.Key, input.Value); } } //If a update was detected then add it to the journal updates if (inputsUpdated) { unassignedStep.UpdateJournal(new Domain.Entities.JournalEntries.JournalEntry() { CreatedBy = SystemUsers.QUEUE_MANAGER, CreatedOn = DateTime.UtcNow, Updates = new List <Update>() { new Update() { Type = UpdateType.Override, FieldName = "status", Value = StepStatuses.Assigned }, new Update() { FieldName = "inputs", Type = UpdateType.Override, Value = convertedInputs }, new Update() { FieldName = "assignedto", Type = UpdateType.Override, Value = request.BotId } } }); } else { unassignedStep.UpdateJournal(new Domain.Entities.JournalEntries.JournalEntry() { CreatedBy = SystemUsers.QUEUE_MANAGER, CreatedOn = DateTime.UtcNow, Updates = new List <Update>() { new Update() { Type = UpdateType.Override, FieldName = "status", Value = StepStatuses.Assigned } } }); } await _node.Handle(new AddShardWriteOperation() { Data = unassignedStep, WaitForSafeWrite = true, Operation = ConsensusCore.Domain.Enums.ShardOperationOptions.Update, RemoveLock = true, LockId = assigned.LockId.Value }); //await _entitiesRepository.UpdateStep(unassignedStep); if (inputsUpdated) { //Update the record with real values, this is not commited to DB unassignedStep.UpdateJournal(new Domain.Entities.JournalEntries.JournalEntry() { CreatedBy = SystemUsers.QUEUE_MANAGER, CreatedOn = DateTime.UtcNow, Updates = new List <Update>() { new Update() { FieldName = "inputs", Type = UpdateType.Override, Value = realAssignedValues } } }); } } catch (Exception e) { Console.WriteLine(e.Message); //throw e; } assignedStepSuccessfully = true; } else { ignoreUnassignedSteps.Add(unassignedStep.Id); assignedStepSuccessfully = false; } } //There were no unassigned steps to assign else { assignedStepSuccessfully = true; } }while (!assignedStepSuccessfully); if (unassignedStep != null) { var template = await _entitiesRepository.GetFirstOrDefaultAsync <StepTemplate>(st => st.ReferenceId == unassignedStep.StepTemplateId); //Decrypt the step unassignedStep.Inputs = DynamicDataUtility.DecryptDynamicData(template.InputDefinitions, unassignedStep.Inputs, EncryptionProtocol.AES256, ClusterStateService.GetEncryptionKey()); unassignedStep.RemoveDelimiters(); //Encrypt the step unassignedStep.Inputs = DynamicDataUtility.EncryptDynamicData(template.InputDefinitions, unassignedStep.Inputs, EncryptionProtocol.RSA, botkey.PublicEncryptionKey, true); } stopwatch.Stop(); return(new CommandResult <Step>() { ObjectRefId = unassignedStep != null?unassignedStep.Id.ToString() : "", ElapsedMs = stopwatch.ElapsedMilliseconds, Type = CommandResultTypes.Update, Result = unassignedStep != null ? unassignedStep : null }); } else { return(new CommandResult <Step>() { ObjectRefId = "", ElapsedMs = stopwatch.ElapsedMilliseconds, Type = CommandResultTypes.None }); } }
public Step GenerateStep(string stepTemplateId, string createdBy, string name = "", string description = "", Dictionary <string, object> inputs = null, List <string> stepTestTemplateIds = null, Guid?workflowId = null, string encryptionKey = "", Guid?executionTemplateId = null, Guid?executionScheduleId = null) { var verifiedInputs = new Dictionary <string, object>(); if (inputs != null) { if (inputs.Count() > InputDefinitions.Count() && !AllowDynamicInputs) { throw new InvalidStepInputException("Too many step inputs for step template " + Id + " were given, expected " + InputDefinitions.Count() + " got " + inputs.Count()); } if (InputDefinitions.Count() > inputs.Count()) { string missingInputs = ""; foreach (var id in InputDefinitions) { if (!inputs.Select(i => i.Key).Contains(id.Key)) { inputs.Add(id.Key, null); //missingInputs += id.Key + " "; } } //throw new InvalidStepInputException("Missing step inputs for step template " + Id + ", expected " + InputDefinitions.Count() + " got " + inputs.Count() + " missing "); } foreach (var input in inputs) { var foundTemplate = InputDefinitions.Where(tp => tp.Key == input.Key).FirstOrDefault(); if (!IsStepInputValid(input)) { throw new InvalidStepInputException("Step input " + input.Key + " is not found in the template definition."); } if ((AllowDynamicInputs && !InputDefinitions.ContainsKey(input.Key)) || InputDefinitions.ContainsKey(input.Key)) { if (InputDefinitions.ContainsKey(input.Key) && InputDefinitions[input.Key].Type == InputDataTypes.Secret && !InputDataUtility.IsInputReference(input, out _, out _)) { verifiedInputs.Add(input.Key.ToLower(), SecurityUtility.SymmetricallyEncrypt((string)input.Value, encryptionKey)); } else { verifiedInputs.Add(input.Key.ToLower(), input.Value); } } } } else if (inputs == null && InputDefinitions.Count() > 0) { throw new InvalidStepInputException("No inputs were specified however step template " + Id + " has " + InputDefinitions.Count() + " inputs."); } var newStep = new Step(Guid.NewGuid(), name, description, stepTemplateId, createdBy, verifiedInputs, encryptionKey, workflowId, executionTemplateId, executionScheduleId); return(newStep); }