/// <summary> /// Register an error that occurred while working with this /// instance. /// </summary> /// <param name="ex"> /// The exception that occurred. /// </param> /// <param name="flowElement"> /// The flow element that the exception occurred in. /// </param> /// <param name="shouldThrow"> /// Set whether the pipeline should throw the exception. /// </param> /// <param name="shouldLog"> /// Set whether the pipeline should log the exception as an error. /// </param> public void AddError(Exception ex, IFlowElement flowElement, bool shouldThrow, bool shouldLog) { if (_errors == null) { _errors = new List <IFlowError>(); } if (_errorsLock == null) { _errorsLock = new object(); } var error = new FlowError(ex, flowElement, shouldThrow); lock (_errorsLock) { _errors.Add(error); } if (_logger != null && _logger.IsEnabled(LogLevel.Error) && shouldLog) { string logMessage = "Error occurred during processing"; if (flowElement != null) { logMessage = logMessage + $" of {flowElement.GetType().Name}"; } _logger.LogError(ex, logMessage); } }
/// <summary> /// Constructor /// </summary> /// <param name="pipelinesAccessor"> /// A function that returns the list of pipelines associated /// with the parent engine. /// </param> /// <param name="currentElement"> /// The <see cref="IFlowElement"/> this instance of /// RequestEngineAccessor was created by. /// </param> public RequestEngineAccessor( Func <IReadOnlyList <IPipeline> > pipelinesAccessor, IFlowElement currentElement) { _currentElement = currentElement; _pipelinesAccessor = pipelinesAccessor; }
/// <summary> /// Constructor /// </summary> /// <param name="element"> /// The <see cref="IFlowElement"/> that this property is associated /// with. /// </param> /// <param name="name"> /// The name of the property. Must match the string key used to /// store the property value in the <see cref="IElementData"/> instance. /// </param> /// <param name="type"> /// The type of the property values. /// </param> /// <param name="category"> /// The category the property belongs to. /// </param> /// <param name="available"> /// True if the property is available. /// </param> /// <param name="itemProperties"> /// Where this meta-data instance relates to a list of complex objects, /// this parameter can contain a list of the property meta-data /// for items in that list. /// </param> /// <param name="delayExecution"> /// Only relevant if <see cref="Type"/> is <see cref="JavaScript"/>. /// Defaults to false. /// If set to true then the JavaScript in this property will /// not be executed automatically on the client device. /// </param> /// <param name="evidenceProperties"> /// The names of any <see cref="JavaScript"/> properties that, /// when executed, will obtain additional evidence that can help /// in determining the value of this property. /// Note that these names should include any parts after the /// element data key. /// I.e. if the complete property name is /// 'devices.profiles.screenwidthpixelsjavascript' then the /// name in this list must be 'profiles.screenwidthpixelsjavascript' /// </param> public ElementPropertyMetaData( IFlowElement element, string name, Type type, bool available, string category = "", IReadOnlyList <IElementPropertyMetaData> itemProperties = null, bool delayExecution = false, IReadOnlyList <string> evidenceProperties = null) { // WARNING: If you are adding properties to this constructor // that need to be exposed through the cloud service then you'll // also need to update: // - Pipeline.Core.Data.PropertyMetaData // - FiftyOne.Pipeline.CloudRequestEngine.FlowElements.CloudRequestEngineBase.LoadProperty() Element = element; Name = name; Type = type; Category = category; Available = available; ItemProperties = itemProperties; DelayExecution = delayExecution; EvidenceProperties = evidenceProperties; _itemPropertyDictionary = new Lazy <IReadOnlyDictionary <string, IElementPropertyMetaData> >(() => { return(ItemProperties?.ToDictionary(p => p.Name, p => p, StringComparer.OrdinalIgnoreCase)); }); }
private IFlowElement register <T>(IFlowElement element) { this.elements.Add(element); this.tasks.Add(((ITaskElement)element).MyTask); if (this.elements.Count > 1) { IFlowElement previous = this.elements[this.elements.Count - 2]; if (!(previous is IDone <T>)) { this.elements.Remove(this.elements.Last()); this.tasks.Remove(this.tasks.Last()); Type iDoneType = previous.GetIDoneType(); if (iDoneType == typeof(void)) { throw new FlowRegisterException(); } else { throw new FlowRegisterException(iDoneType); } } else { ((IDone <T>)previous).Done += ((IRun <T>) this.elements.Last()).Run; ((IFail)this.elements.Last()).Fail += callFailAndAlways; } } return(this.elements.Last()); }
/// <summary> /// Add the specified <see cref="IFlowElement"/> to the pipeline. /// Elements are typically executed sequentially in the order /// they are added. /// </summary> /// <param name="element"> /// The <see cref="IFlowElement"/> to add /// </param> /// <returns> /// This builder instance. /// </returns> /// <exception cref="ObjectDisposedException"> /// Thrown if the element has already been disposed. /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if the supplied element is null. /// </exception> public T AddFlowElement(IFlowElement element) { if (element == null) { throw new ArgumentNullException(nameof(element)); } if (element.IsDisposed) { throw new ObjectDisposedException(nameof(element)); } FlowElements.Add(element); return(this as T); }
/// <summary> /// Get the <see cref="IElementData"/> instance containing data /// populated by the specified element. /// </summary> /// <typeparam name="T"> /// The expected type of the data to be returned. /// </typeparam> /// <typeparam name="TMeta"> /// The type of meta data that the flow element will supply /// about the properties it populates. /// </typeparam> /// <param name="flowElement"> /// The <see cref="IFlowElement{T, TMeta}"/> that populated the /// desired data. /// </param> /// <returns> /// An instance of type T containing the data. /// </returns> /// <exception cref="ArgumentNullException"> /// Thrown if the supplied flow element is null /// </exception> /// <exception cref="PipelineException"> /// Thrown if this FlowData instance has not been processed yet. /// </exception> public T GetFromElement <T, TMeta>(IFlowElement <T, TMeta> flowElement) where T : IElementData where TMeta : IElementPropertyMetaData { if (_processed == false) { throw new PipelineException(Messages.ExceptionFlowDataNotYetProcessed); } if (flowElement == null) { throw new ArgumentNullException(nameof(flowElement)); } return(_data.Get(flowElement.ElementDataKeyTyped)); }
private void registerAdditionalEvents <TResult>(ref IFlowElement element, Action <TResult> onDone, Action <Exception> onFail, Action onAlways) { if (onDone != null) { ((IDone <TResult>)element).Done += onDone; } if (onFail != null) { ((IFail)element).Fail += onFail; } if (onAlways != null) { ((IAlways)element).Always += onAlways; } }
/// <summary> /// Constructor /// </summary> /// <param name="element"> /// The <see cref="IFlowElement"/> that this property is associated /// with. /// </param> /// <param name="name"> /// The name of the property. Must match the string key used to /// store the property value in the <see cref="IElementData"/> instance. /// </param> /// <param name="type"> /// The type of the property values. /// </param> /// <param name="category"> /// The category the property belongs to. /// </param> /// <param name="available"> /// True if the property is available. /// </param> /// <param name="itemProperties"> /// Where this meta-data instance relates to a list of complex objects, /// this parameter can contain a list of the property meta-data /// for items in that list. /// </param> public ElementPropertyMetaData( IFlowElement element, string name, Type type, bool available, string category = "", IReadOnlyList <IElementPropertyMetaData> itemProperties = null) { Element = element; Name = name; Type = type; Category = category; Available = available; ItemProperties = itemProperties; }
private static Type getGenericInterfaceType(IFlowElement element, string name) { Type result = getInterfaceType(element, name); if (result == default(Type)) { return(typeof(void)); } else if (!result.IsGenericType) { return(typeof(void)); } else { return(result.GetGenericArguments()[0]); } }
private void assertFlowElementIs(Type elementClass) { IBpmnModelInstance modelInstance = ModelExecutionContextExecutionListener.ModelInstance; Assert.NotNull(modelInstance); IModel model = modelInstance.Model; IEnumerable <IModelElementInstance> events = modelInstance.GetModelElementsByType(model.GetType(typeof(IEvent))); Assert.AreEqual(3, events.Count()); IEnumerable <IModelElementInstance> gateways = modelInstance.GetModelElementsByType(model.GetType(typeof(IGateway))); Assert.AreEqual(1, gateways.Count()); IEnumerable <IModelElementInstance> tasks = modelInstance.GetModelElementsByType(model.GetType(typeof(ITask))); Assert.AreEqual(1, tasks.Count()); IFlowElement flowElement = ModelExecutionContextExecutionListener.FlowElement; Assert.NotNull(flowElement); Assert.True(elementClass.IsAssignableFrom(flowElement.GetType())); }
private void MenuSelected(MenuModel menu) { if (Works.FirstOrDefault(x => x.Menu.Equals(menu)) is null) { IFlowElement content = null; switch (menu.IconType) { case GeoIcon.FolderOpenOutline: content = new Finder().UseViewModel(new FinderViewModel()); break; case GeoIcon.EyedropperVariant: content = new ColorSpoid().UseViewModel(new ColorSpoidViewModel()); break; case GeoIcon.Palette: content = new SwitchSkin().UseViewModel(Skin); break; case GeoIcon.Web: content = new Translator().UseViewModel(Translate); break; case GeoIcon.Close: Environment.Exit(0); break; default: content = new EmptyView(); break; } content.OnShow(menu); } }
public static void Clear() { ModelInstance = null; FlowElement = null; }
public void Notify(IBaseDelegateExecution execution) { ModelInstance = ((IDelegateExecution)execution).BpmnModelInstance; FlowElement = ((IDelegateExecution)execution).BpmnModelElementInstance; }
private static Type getInterfaceType(IFlowElement element, string name) { Type[] interfaces = element.GetType().GetInterfaces(); return(interfaces.FirstOrDefault((t) => { return t.Name.ToLower().StartsWith(name.ToLower()); })); }
internal static Type GetIDoneType(this IFlowElement element) { return(getGenericInterfaceType(element, "IDone")); }
/// <summary> /// Registers a method in the flow. /// </summary> /// <typeparam name="T">The type of the input parameter of the method.</typeparam> /// <typeparam name="TResult">The type of the return value of the method.</typeparam> /// <param name="method">The method to register.</param> /// <param name="onDone">Optional method that is executed after the method has finished successfully (e.g. logging or branching).</param> /// <param name="onFail">Optional method that is executed after the method has finished with an error (e.g. logging or branching).</param> /// <param name="onAlways">Optional method that is executed after the method has finished (e.g. logging or branching).</param> /// <exception cref="Simple.Messaging.FlowRegisterException">Occurs if the input type provided does not match the return type of the previous method.</exception> public void Register <T, TResult>(Func <T, TResult> method, Action <TResult> onDone = null, Action <Exception> onFail = null, Action onAlways = null) { IFlowElement f = register <T>(new Function <T, TResult>(method)); registerAdditionalEvents <TResult>(ref f, onDone, onFail, onAlways); }
/// <summary> /// Register an error that occurred while working with this /// instance. /// </summary> /// <param name="ex"> /// The exception that occurred. /// </param> /// <param name="flowElement"> /// The flow element that the exception occurred in. /// </param> public void AddError(Exception ex, IFlowElement flowElement) { AddError(ex, flowElement, true, true); }
/// <summary> /// Registers a method in the flow. /// </summary> /// <typeparam name="T">The type of the input parameter of the method.</typeparam> /// <param name="method">The method to register.</param> /// <param name="onDone">Optional method that is executed after the method has finished successfully (e.g. logging or branching).</param> /// <param name="onFail">Optional method that is executed after the method has finished with an error (e.g. logging or branching).</param> /// <param name="onAlways">Optional method that is executed after the method has finished (e.g. logging or branching).</param> /// <exception cref="Simple.Messaging.FlowRegisterException">Occurs if the type provided does not match the return type of the previous method.</exception> public void Register <T>(Action <T> method, Action onDone = null, Action <Exception> onFail = null, Action onAlways = null) { IFlowElement f = register <T>(new NoRetrunFunction <T>(method)); registerAdditionalEvents(ref f, onDone, onFail, onAlways); }
public static void Clear() { UserTask = null; ModelInstance = null; }
public virtual void Notify(IDelegateTask delegateTask) { ModelInstance = delegateTask.BpmnModelInstance; UserTask = delegateTask.BpmnModelElementInstance; }
/// <summary> /// Constructor /// </summary> /// <param name="ex"> /// The exception /// </param> /// <param name="flowElement"> /// The flow element that the exception occurred in or is related to. /// </param> /// <param name="shouldThrow"> /// Set whether the pipeline should throw this exception. /// </param> public FlowError(Exception ex, IFlowElement flowElement, bool shouldThrow = true) { ExceptionData = ex; FlowElement = flowElement; ShouldThrow = shouldThrow; }
/// <summary> /// Create a new <see cref="IFlowElement"/> using the specified /// <see cref="ElementOptions"/> and add it to the supplied list /// of elements. /// </summary> /// <param name="elements"> /// The list to add the new <see cref="IFlowElement"/> to. /// </param> /// <param name="elementOptions"> /// The <see cref="ElementOptions"/> instance to use when creating /// the <see cref="IFlowElement"/>. /// </param> /// <param name="elementLocation"> /// The string description of the element's location within the /// <see cref="PipelineOptions"/> instance. /// </param> private void AddElementToList( List <IFlowElement> elements, ElementOptions elementOptions, string elementLocation) { // Check that a builder name is set if (string.IsNullOrEmpty(elementOptions.BuilderName)) { throw new PipelineConfigurationException( $"A BuilderName must be specified for " + $"{elementLocation}."); } // Try to get the builder to use var builderType = GetBuilderType(elementOptions.BuilderName); if (builderType == null) { throw new PipelineConfigurationException( $"Could not find builder matching " + $"'{elementOptions.BuilderName}' for " + $"{elementLocation}. Available builders are: " + $"{string.Join(",", _elementBuilders.Select(t => t.Name))}"); } // Get the methods on the builder var buildMethods = builderType.GetRuntimeMethods() .Where(m => m.Name == "Build"); // If there are no 'Build' methods or if there is no default // constructor then throw an error. if (buildMethods.Any() == false) { throw new PipelineConfigurationException( $"Builder '{builderType.FullName}' for " + $"{elementLocation} has no 'Build' methods."); } object builderInstance = null; if (_services != null) { // Try to get a a builder instance from the service collection. builderInstance = _services.GetRequiredService(builderType); } else { // A service collection does not exist in the builder, so try // to construct a builder instance from the assemblies // currently loaded. builderInstance = GetBuilderFromAssemlies(builderType); if (builderInstance == null) { throw new PipelineConfigurationException( $"Builder '{builderType.FullName}' for " + $"{elementLocation} does not have a default constructor. " + $"i.e. One that takes no parameters. Or a constructor " + $"which takes an ILoggerFactory parameter."); } } // Holds a list of the names of parameters to pass to the // build method when we're ready. List <string> buildParameterList = new List <string>(); if (elementOptions.BuildParameters != null) { // Call any non-build methods on the builder to set optional // parameters. buildParameterList = ProcessBuildParameters( elementOptions.BuildParameters, builderType, builderInstance, elementLocation); } // At this point, all the optional methods on the builder // have been called and we're ready to call the Build method. // If there are no matching build methods or multiple possible // build methods based on our parameters then throw an exception. var possibleBuildMethods = buildMethods.Where(m => m.GetParameters().Length == buildParameterList.Count && m.GetParameters().All(p => buildParameterList.Contains(p.Name.ToUpperInvariant()))); StringBuilder buildSignatures = new StringBuilder(); buildSignatures.AppendLine(); foreach (var method in buildMethods) { buildSignatures.AppendLine(string.Join(",", method.GetParameters().Select(p => $"{p.Name} ({p.ParameterType.Name})"))); } if (possibleBuildMethods.Any() == false) { StringBuilder methodSignatures = new StringBuilder(); methodSignatures.AppendLine(); // Build a list of the 'set' methods on this builder // along with their parameter. foreach (var method in builderType.GetRuntimeMethods() // Include any methods that are: // 1. public // 2. have a single parameter .Where(m => m.IsPublic && m.GetParameters().Length == 1 && m.DeclaringType.Name.ToUpperInvariant().Contains("BUILDER"))) { methodSignatures.AppendLine($"{method.Name} ({method.GetParameters()[0].GetType().Name})"); } throw new PipelineConfigurationException( $"Builder '{builderType.FullName}' for " + $"{elementLocation} has no " + $"'Set' methods or 'Build' methods that match " + $"(case-insensitively) the following parameters: " + $"{string.Join(",", buildParameterList)}. " + $"The available configuration methods on this builder are: " + $"{methodSignatures.ToString()}" + $"The available 'Build' methods have the following signatures: " + $"{buildSignatures.ToString()}"); } else if (possibleBuildMethods.Count() > 1) { throw new PipelineConfigurationException( $"Builder '{builderType.FullName}' for " + $"{elementLocation} has multiple " + $"'Build' methods that match the following parameters: " + $"{string.Join(",", buildParameterList)}. " + $"Matching method signatures are: {buildSignatures.ToString()}"); } // Get the build method parameters and add the configured // values to the parameter list. List <object> parameters = new List <object>(); var buildMethod = possibleBuildMethods.Single(); var caseInsensitiveParameters = elementOptions.BuildParameters .ToDictionary(d => d.Key.ToUpperInvariant(), d => d.Value); foreach (var parameterInfo in buildMethod.GetParameters()) { var paramType = parameterInfo.ParameterType; object paramValue = caseInsensitiveParameters[parameterInfo.Name.ToUpperInvariant()]; if (paramType != typeof(string)) { paramValue = ParseToType(paramType, (string)paramValue, $"Method 'Build' on builder " + $"'{builderType.FullName}' for " + $"{elementLocation} expects a parameter of type " + $"'{paramType.Name}'"); } parameters.Add(paramValue); } // Call the build method with the parameters we set up above. object result = buildMethod.Invoke(builderInstance, parameters.ToArray()); if (result == null) { throw new PipelineConfigurationException( $"Failed to build {elementLocation} using " + $"'{builderType.FullName}', reason unknown."); } IFlowElement element = result as IFlowElement; if (element == null) { throw new PipelineConfigurationException( $"Failed to cast '{result.GetType().FullName}' to " + $"'IFlowElement' for {elementLocation}"); } // Add the element to the list. elements.Add(element); }