/// <summary> /// Executes Plugin's ExecutePlugin method safely taking care maintaining the right execution context /// </summary> /// <param name="actualParams">Key-Value pairs representing Typed variable and their names</param> /// <returns>PluginOutput object</returns> private async Task <PluginData <T> > ExecuteSafely <T>(PluginInputs actualParams) where T : Schema, new() { var pluginSucceeded = false; PluginData <T> pluginData = null; try { EnsurePluginIsInitialized(); _executionContext.StartPluginExecution(PluginContext); if (AlreadyExecuted()) { pluginSucceeded = true; pluginData = _executionContext.GetPluginOutputFromId <T>(PluginContext.PluginOutputId); } else if (RequiredInputsNotNull(actualParams, ExecInfo.ParameterInfo)) { if (!IsCompatiblePlugin()) { throw new VersionMismatchException(PluginContext.PluginName); } _executionContext.UpdateInputConsumers(PluginContext); if (AllDependenciesFulFilled()) { if (ValidateParams(actualParams, ExecInfo.ParameterInfo, out object[] validatedParams))
/// <summary> /// Uses reflection to safely execute a Typed "ExecutePlugin" method that is defined in a Task/Workflow /// </summary> /// <param name="actualParams">Key-Value pairs representing Typed variable and their names</param> public virtual async Task <PluginData <T> > Execute <T>(PluginInputs actualParams) where T : Schema, new() { if (actualParams == null) { throw new WorkflowEngineException("ContainerInputs passed to a plugin cannot be null"); } PluginContext.PluginInputIds = actualParams.ToDictionary(kv => kv.Key, kv => kv.Value?.Id); return(await ExecuteSafely <T>(actualParams).ConfigureAwait(false)); }
/// <summary> /// Ensures that all non-null plugin parameters have been instantiated properly /// TODO: Replace this with a Compile Time Check using FxCop /// </summary> private void EnsureParamsContextIsSet(PluginInputs actualParams) { if (actualParams == null) { throw new WorkflowEngineException(nameof(actualParams)); } if (actualParams.Values.Any(param => param != null && param.ExecutionContext == null)) { throw new WorkflowEngineException("Workflow ExecutionContext not set for this plugin data objects. Make sure to use PluginServices to GetFromId all Plugin String"); } }
/// <summary> /// Returns true only if: /// 1. All single requird inputs are not null /// 2. All list required inputs are not null /// 3. Every item in non-null list inputs are not null /// </summary> private static bool RequiredInputsNotNull(PluginInputs actualParams, ParameterInfo[] expectedParams) { foreach (var ap in actualParams) { var pi = expectedParams.FirstOrDefault(ep => ep.Name == ap.Key); if (pi != null && pi.CustomAttributes.All(a => a.AttributeType != typeof(OptionalAttribute)) && ap.Value == null) { return(false); } } return(!actualParams.Values.Any(param => param != null && param.IsList() && param.GetDataList().Any(item => item == null))); }
public WorkflowContainerContext(IState persistentState) { var state = persistentState as WorkflowContextState; if (state == null) { throw new WorkflowEngineException("WorkflowContainerContext must be loaded with an object of type WorkflowContextState"); } ExecutionContext = new ExecutionContext(state.ExecutionContext); ContainerInputs = new PluginInputs(state.ContainerInputs, ExecutionContext); RootPluginContext = new PluginContext(state.RootPluginContext); // Sync Root Plugin Context with Execution Context (Pointer Swizzling) RootPluginContext = ExecutionContext.GetPluginContextFromId(RootPluginContext.PluginId); Executed = state.Executed; Variants = state.Variants; }
private static void UpdatePluginInputs(PluginInputs oldInputs, PluginInputs newInputs) { if (newInputs == null) { return; } foreach (var kv in newInputs) { var oldInput = oldInputs[kv.Key]; var newInput = kv.Value; if (!Equals(oldInput.Id, newInput.Id)) { throw new WorkflowEngineException("Attempting updating an input with a different one. Use GetWorkflowInput()"); } oldInputs[kv.Key] = kv.Value; } }
public async Task <WorkflowContainerExecutionResult> ReExecute(PluginInputs newInputs = null) { EnsureContainerIsInitialized(); if (!_containerContext.Executed) { throw new WorkflowEngineException("Cannot re-execute workfor container before executing it."); } var rootWorkflow = _pluginServices.LoadPlugin <TWorkflow>(_containerContext.RootPluginContext); UpdatePluginInputs(_containerContext.ContainerInputs, newInputs); try { _rootWorkflowOutput = await rootWorkflow.Execute <TOutput>(_containerContext.ContainerInputs).ConfigureAwait(false); } catch (VersionMismatchException) { return(WorkflowContainerExecutionResult.NotExecuted); } return(_rootWorkflowOutput == null ? WorkflowContainerExecutionResult.PartiallyCompleted : WorkflowContainerExecutionResult.Completed); }
async Task <WorkflowContainerExecutionResult> IWorkflowContainer <TWorkflow, TOutput> .Execute(PluginInputs workflowInputs) { EnsureContainerIsInitialized(); if (_containerContext.Executed) { throw new WorkflowEngineException("Cannot execute workflow container more than once."); } var rootWorkflow = _pluginServices.LoadPlugin <TWorkflow>(_containerContext.RootPluginContext); _containerContext.Executed = true; _containerContext.ContainerInputs = workflowInputs ?? throw new WorkflowEngineException("Cannot execute container with null inputs"); _containerContext.RootPluginContext = rootWorkflow.GetContext(); _containerContext.ExecutionContext = _pluginServices.GetExecutionContext(); _rootWorkflowOutput = await rootWorkflow.Execute <TOutput>(workflowInputs).ConfigureAwait(false); return(_rootWorkflowOutput == null ? WorkflowContainerExecutionResult.PartiallyCompleted : WorkflowContainerExecutionResult.Completed); }