/// <summary> /// Process method checks for presence of a session id and sequence /// number. If they do not exist then they are initialized in evidence. /// If they do exist in evidence then the sequence number is incremented /// and added back to the evidence. /// </summary> /// <param name="data"> /// The <see cref="IFlowData"/> instance to process. /// </param> /// <exception cref="ArgumentNullException"> /// Thrown if the supplied data instance is null /// </exception> protected override void ProcessInternal(IFlowData data) { if (data == null) { throw new ArgumentNullException(nameof(data)); } var evidence = data.GetEvidence().AsDictionary(); // If the evidence does not contain a session id then create a new one. if (evidence.ContainsKey(Constants.EVIDENCE_SESSIONID) == false) { data.AddEvidence(Constants.EVIDENCE_SESSIONID, GetNewSessionId()); } // If the evidence does not have a sequence then add one. Otherwise // increment it. if (evidence.ContainsKey(Constants.EVIDENCE_SEQUENCE) == false) { data.AddEvidence(Constants.EVIDENCE_SEQUENCE, 1); } else if (evidence.TryGetValue(Constants.EVIDENCE_SEQUENCE, out object sequence)) { if (sequence is int result || (sequence is string seq && int.TryParse(seq, out result))) { data.AddEvidence(Constants.EVIDENCE_SEQUENCE, result + 1); } else { data.AddError(new Exception(Messages.MessageFailSequenceNumberParse), this); Logger.LogError(Messages.MessageFailSequenceNumberIncrement); } }
/// <summary> /// Called by the Process method on the /// <see cref="FlowElementBase{T, TMeta}"/> base class. /// Executes all child elements in parallel. /// </summary> /// <param name="data"> /// The data to use when executing the flow elements. /// </param> protected override void ProcessInternal(IFlowData data) { List <Task> allTasks = new List <Task>(); foreach (var element in _flowElements) { allTasks.Add( // Run each element on a new thread. Task.Run(() => { element.Process(data); }).ContinueWith(t => { // If any exceptions occurred then add them to the // flow data. if (t.Exception != null) { foreach (var innerException in t.Exception.InnerExceptions) { data.AddError(innerException, element); } } }, TaskScheduler.Default)); } // Wait until all tasks have completed. Task.WhenAll(allTasks).Wait(); }
/// <summary> /// Process the given <see cref="IFlowData"/> using the /// <see cref="IFlowElement"/>s in the pipeline. /// </summary> /// <param name="data"> /// The <see cref="IFlowData"/> that contains the evidence and will /// allow the user to access the results. /// </param> public void Process(IFlowData data) { if (data == null) { throw new ArgumentNullException(nameof(data)); } _logger.LogDebug($"Pipeline '{GetHashCode()}' started processing."); foreach (var element in _flowElements) { try { element.Process(data); #pragma warning disable CS0618 // Type or member is obsolete // This usage will be replaced once the Cancellation Token // mechanism is available. if (data.Stop) { break; } #pragma warning restore CS0618 // Type or member is obsolete } #pragma warning disable CA1031 // Do not catch general exception types // We want to catch any exception here so that the // Pipeline can manage it. catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { // If an error occurs then store it in the // FlowData object. data.AddError(ex, element); } } // If any errors have occurred and exceptions are not // suppressed, then throw an aggregate exception. if (data.Errors != null && data.Errors.Count > 0 && _suppressProcessExceptions == false) { throw new AggregateException(data.Errors .Where(e => e.ShouldThrow == true) .Select(e => e.ExceptionData)); } _logger.LogDebug($"Pipeline '{GetHashCode()}' finished processing."); }
/// <summary> /// Create and populate a JSON string from the specified data. /// </summary> /// <param name="data"></param> /// <param name="config">The configuration to use</param> /// <returns> /// A string containing the data in JSON format. /// </returns> protected virtual string BuildJson(IFlowData data, PipelineConfig config) { int sequenceNumber = GetSequenceNumber(data); // Get property values from all the elements and add the ones that // are accessible to a dictionary. Dictionary <String, object> allProperties = GetAllProperties(data, config); // Only populate the JavaScript properties if the sequence // has not reached max iterations. if (sequenceNumber < Constants.MAX_JAVASCRIPT_ITERATIONS) { AddJavaScriptProperties(data, allProperties); } AddErrors(data, allProperties); try { return(BuildJson(allProperties)); } catch (JsonWriterException jsonEx) { StringBuilder msg = new StringBuilder(); msg.AppendLine("Error converting data to json string"); msg.AppendLine("["); foreach (var entry in allProperties) { msg.AppendLine($" '{entry.Key}' = {GetObjectAsString(entry.Value, 2)}"); } msg.AppendLine("]"); var exception = new PipelineDataException(msg.ToString(), jsonEx); data.AddError(exception, this); } return("{ \"error\": \"see flow data errors for more detail\" }"); }