diff --git a/Flowsharp.sln b/Flowsharp.sln index b6d2ff0f8..f97302418 100644 --- a/Flowsharp.sln +++ b/Flowsharp.sln @@ -16,10 +16,9 @@ ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .gitignore = .gitignore README.md = README.md + Nuget.Config = Nuget.Config EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flowsharp.Fluid", "src\Flowsharp.Fluid\Flowsharp.Fluid.csproj", "{B20D6AF5-91B1-4455-8541-22C5B31288D0}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -38,10 +37,6 @@ Global {48EDB976-3227-4DFD-BBB3-3BC9AEA19A88}.Debug|Any CPU.Build.0 = Debug|Any CPU {48EDB976-3227-4DFD-BBB3-3BC9AEA19A88}.Release|Any CPU.ActiveCfg = Release|Any CPU {48EDB976-3227-4DFD-BBB3-3BC9AEA19A88}.Release|Any CPU.Build.0 = Release|Any CPU - {B20D6AF5-91B1-4455-8541-22C5B31288D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B20D6AF5-91B1-4455-8541-22C5B31288D0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B20D6AF5-91B1-4455-8541-22C5B31288D0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B20D6AF5-91B1-4455-8541-22C5B31288D0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -53,6 +48,5 @@ Global {300EE2D5-54C5-46F2-AD03-BB43589EA074} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925} {3B33AE9C-0465-4DA3-8C02-65E5766A7ED4} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925} {48EDB976-3227-4DFD-BBB3-3BC9AEA19A88} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925} - {B20D6AF5-91B1-4455-8541-22C5B31288D0} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925} EndGlobalSection EndGlobal diff --git a/Nuget.Config b/Nuget.Config new file mode 100644 index 000000000..de3a3397c --- /dev/null +++ b/Nuget.Config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/Flowsharp.Abstractions/Activities/Activity.cs b/src/Flowsharp.Abstractions/Activities/Activity.cs index 045cbc21a..205b10cae 100644 --- a/src/Flowsharp.Abstractions/Activities/Activity.cs +++ b/src/Flowsharp.Abstractions/Activities/Activity.cs @@ -1,26 +1,16 @@ using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Flowsharp.ActivityResults; using Flowsharp.Models; -using Microsoft.Extensions.Localization; namespace Flowsharp.Activities { public abstract class Activity : IActivity { - public virtual string Name => GetType().Name; - - public Task ProvideMetadataAsync(ActivityMetadataContext context, CancellationToken cancellationToken) - { - ProvideMetadata(context); - return Task.CompletedTask; - } - public virtual Task CanExecuteAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, CancellationToken cancellationToken) { - return Task.FromResult(true); + return Task.FromResult(CanExecute(workflowContext, activityContext)); } public virtual Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, CancellationToken cancellationToken) @@ -33,11 +23,6 @@ namespace Flowsharp.Activities return Task.FromResult(Resume(workflowContext, activityContext)); } - public virtual IEnumerable GetOutcomes(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext) - { - return Enumerable.Empty(); - } - public virtual Task OnActivityExecutedAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, CancellationToken cancellationToken) { OnActivityExecuted(workflowContext, activityContext); @@ -80,20 +65,17 @@ namespace Flowsharp.Activities return Task.CompletedTask; } - protected virtual void ProvideMetadata(ActivityMetadataContext context) - { - } - protected virtual ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext) { - return Noop(); + return ActivateEndpoint(); } protected virtual ActivityExecutionResult Resume(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext) { - return Noop(); + return ActivateEndpoint(); } - + + protected virtual bool CanExecute(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext) => true; protected virtual void OnActivityExecuted(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext) {} protected virtual void OnActivityExecuting(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext) {} protected virtual void ReceiveInput(WorkflowExecutionContext workflowContext, IDictionary input) {} @@ -102,32 +84,32 @@ namespace Flowsharp.Activities protected virtual void WorkflowStarted(WorkflowExecutionContext workflowContext) {} protected virtual void WorkflowStarting(WorkflowExecutionContext workflowContext) {} - protected IEnumerable Outcomes(params LocalizedString[] names) - { - return names.Select(x => new Outcome(x)); - } - - protected IEnumerable Outcomes(IEnumerable names) - { - return names.Select(x => new Outcome(x)); - } - - protected ActivityExecutionResult Outcomes(params string[] names) - { - return Outcomes((IEnumerable)names); - } - - protected ActivityExecutionResult Outcomes(IEnumerable names) - { - return new OutcomeResult(names); - } - - protected ActivityExecutionResult Halt() + protected HaltResult Halt() { return new HaltResult(); } - protected ActivityExecutionResult Noop() + protected ActivateEndpointResult ActivateEndpoint(string name = null) + { + return new ActivateEndpointResult(new SourceEndpoint(this, name)); + } + + protected ScheduleActivityResult ScheduleActivity(IActivity activity) + { + return new ScheduleActivityResult(activity); + } + + protected ReturnValueResult SetReturnValue(object value) + { + return new ReturnValueResult(value); + } + + protected FinishWorkflowResult Finish() + { + return new FinishWorkflowResult(); + } + + protected NoopResult Noop() { return new NoopResult(); } diff --git a/src/Flowsharp.Abstractions/Activities/IActivity.cs b/src/Flowsharp.Abstractions/Activities/IActivity.cs index f88645721..cb927c4a3 100644 --- a/src/Flowsharp.Abstractions/Activities/IActivity.cs +++ b/src/Flowsharp.Abstractions/Activities/IActivity.cs @@ -8,22 +8,6 @@ namespace Flowsharp.Activities { public interface IActivity { - /// - /// The system name of the activity. - /// - /// The Name is used to identify a given activity type and must be unique. - string Name { get; } - - /// - /// Provides metadata about the specified activity. - /// - Task ProvideMetadataAsync(ActivityMetadataContext context, CancellationToken cancellationToken); - - /// - /// Returns a list of possible outcomes when the activity is executed. - /// - IEnumerable GetOutcomes(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext); - /// /// Returns a value of whether the specified activity can execute. /// diff --git a/src/Flowsharp.Abstractions/Activities/Workflow.cs b/src/Flowsharp.Abstractions/Activities/Workflow.cs new file mode 100644 index 000000000..f37a2c27c --- /dev/null +++ b/src/Flowsharp.Abstractions/Activities/Workflow.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Linq; +using Flowsharp.ActivityResults; +using Flowsharp.Models; + +namespace Flowsharp.Activities +{ + public class Workflow + { + public Workflow() + { + } + + public Workflow(IEnumerable activities, IEnumerable connections) + { + Activities = activities.ToList(); + Connections = connections.ToList(); + } + + public IList Activities { get; set; } = new List(); + public IList Connections { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/src/Flowsharp.Abstractions/ActivityProviders/TypedActivityProvider.cs b/src/Flowsharp.Abstractions/ActivityProviders/TypedActivityProvider.cs deleted file mode 100644 index 152656c6b..000000000 --- a/src/Flowsharp.Abstractions/ActivityProviders/TypedActivityProvider.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Flowsharp.Activities; -using Flowsharp.Descriptors; -using Flowsharp.Services; - -namespace Flowsharp.ActivityProviders -{ - /// - /// Provides activities based on implementations that have been registered with the service container. - /// - public class TypedActivityProvider : IActivityProvider - { - private readonly Func> _activitiesFactory; - - public TypedActivityProvider(Func> activitiesFactory) - { - _activitiesFactory = activitiesFactory; - } - - public Task> GetActivityDescriptorsAsync(CancellationToken cancellationToken) - { - return Task.FromResult(_activitiesFactory().Select(ToDescriptor)); - } - - private ActivityDescriptor ToDescriptor(IActivity activity) - { - return new ActivityDescriptor - { - Name = activity.Name, - GetMetadataAsync = activity.ProvideMetadataAsync, - CanExecuteAsync = activity.CanExecuteAsync, - GetOutcomes = activity.GetOutcomes, - ExecuteActivityAsync = activity.ExecuteAsync, - ResumeActivityAsync = activity.ResumeAsync, - ReceiveInputAsync = activity.ReceiveInputAsync, - WorkflowResumedAsync = activity.WorkflowResumedAsync, - WorkflowResumingAsync = activity.WorkflowResumingAsync, - WorkflowStartedAsync = activity.WorkflowStartedAsync, - WorkflowStartingAsync = activity.WorkflowStartingAsync, - OnActivityExecutedAsync = activity.OnActivityExecutedAsync, - OnActivityExecutingAsync = activity.OnActivityExecutingAsync - }; - } - } -} diff --git a/src/Flowsharp.Abstractions/ActivityResults/ActivateEndpointResult.cs b/src/Flowsharp.Abstractions/ActivityResults/ActivateEndpointResult.cs new file mode 100644 index 000000000..9ddfbcade --- /dev/null +++ b/src/Flowsharp.Abstractions/ActivityResults/ActivateEndpointResult.cs @@ -0,0 +1,22 @@ +using Flowsharp.Models; + +namespace Flowsharp.ActivityResults +{ + /// + /// A result that carries information about the next activity to execute. + /// + public class ActivateEndpointResult : ActivityExecutionResult + { + public ActivateEndpointResult(SourceEndpoint endpoint) + { + Endpoint = endpoint; + } + + public SourceEndpoint Endpoint { get; } + + protected override void Execute(WorkflowExecutionContext workflowContext) + { + workflowContext.ScheduleNextActivities(workflowContext, Endpoint); + } + } +} \ No newline at end of file diff --git a/src/Flowsharp.Abstractions/ActivityResults/FinishWorkflowResult.cs b/src/Flowsharp.Abstractions/ActivityResults/FinishWorkflowResult.cs new file mode 100644 index 000000000..95fec5e70 --- /dev/null +++ b/src/Flowsharp.Abstractions/ActivityResults/FinishWorkflowResult.cs @@ -0,0 +1,12 @@ +using Flowsharp.Models; + +namespace Flowsharp.ActivityResults +{ + public class FinishWorkflowResult : ActivityExecutionResult + { + protected override void Execute(WorkflowExecutionContext workflowContext) + { + workflowContext.Finish(); + } + } +} \ No newline at end of file diff --git a/src/Flowsharp.Abstractions/ActivityResults/HaltResult.cs b/src/Flowsharp.Abstractions/ActivityResults/HaltResult.cs index 5a73e78f0..9b62aef97 100644 --- a/src/Flowsharp.Abstractions/ActivityResults/HaltResult.cs +++ b/src/Flowsharp.Abstractions/ActivityResults/HaltResult.cs @@ -11,20 +11,19 @@ namespace Flowsharp.ActivityResults { public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, CancellationToken cancellationToken) { - var currentActivity = workflowContext.CurrentExecutingActivity; + var currentActivity = workflowContext.CurrentActivity; if (workflowContext.IsFirstPass) { // Resume immediately when this is the first pass. - var result = await currentActivity.ActivityDescriptor.ResumeActivityAsync(workflowContext, currentActivity, cancellationToken); + var result = await currentActivity.ResumeAsync(workflowContext, new ActivityExecutionContext(currentActivity), cancellationToken); workflowContext.IsFirstPass = false; await result.ExecuteAsync(workflowContext, cancellationToken); } else { - // Block on this activity. - workflowContext.BlockingActivities.Add(currentActivity); + await workflowContext.HaltAsync(cancellationToken); } } } diff --git a/src/Flowsharp.Abstractions/ActivityResults/OutcomeResult.cs b/src/Flowsharp.Abstractions/ActivityResults/OutcomeResult.cs deleted file mode 100644 index c1f11050f..000000000 --- a/src/Flowsharp.Abstractions/ActivityResults/OutcomeResult.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Flowsharp.Models; - -namespace Flowsharp.ActivityResults -{ - /// - /// An activity execution result that sets the next outcomes to execute. - /// - public class OutcomeResult : ActivityExecutionResult - { - private readonly IEnumerable outcomeNames; - - public OutcomeResult(IEnumerable names) - { - outcomeNames = names.ToList(); - } - - protected override void Execute(WorkflowExecutionContext workflowContext) - { - var workflowType = workflowContext.WorkflowType; - var currentActivity = workflowContext.CurrentExecutingActivity; - - foreach (var outcome in outcomeNames) - { - // Look for next activity in the graph. - var transition = workflowType.Transitions.FirstOrDefault(x => x.From.ActivityId == currentActivity.ActivityType.Id && x.From.OutcomeName == outcome); - - if (transition != null) - { - var destinationActivity = workflowContext.Activities.Values.Single(x => x.ActivityType.Id == transition.To.ActivityId); - workflowContext.PushScheduledActivity(destinationActivity); - } - } - } - } -} diff --git a/src/Flowsharp.Abstractions/ActivityResults/ReturnValueResult.cs b/src/Flowsharp.Abstractions/ActivityResults/ReturnValueResult.cs new file mode 100644 index 000000000..a2d28e49f --- /dev/null +++ b/src/Flowsharp.Abstractions/ActivityResults/ReturnValueResult.cs @@ -0,0 +1,19 @@ +using Flowsharp.Models; + +namespace Flowsharp.ActivityResults +{ + public class ReturnValueResult : ActivityExecutionResult + { + private readonly object value; + + public ReturnValueResult(object value) + { + this.value = value; + } + + protected override void Execute(WorkflowExecutionContext workflowContext) + { + workflowContext.SetReturnValue(value); + } + } +} \ No newline at end of file diff --git a/src/Flowsharp.Abstractions/ActivityResults/ScheduleActivityResult.cs b/src/Flowsharp.Abstractions/ActivityResults/ScheduleActivityResult.cs new file mode 100644 index 000000000..23a7a2ea6 --- /dev/null +++ b/src/Flowsharp.Abstractions/ActivityResults/ScheduleActivityResult.cs @@ -0,0 +1,20 @@ +using Flowsharp.Activities; +using Flowsharp.Models; + +namespace Flowsharp.ActivityResults +{ + public class ScheduleActivityResult : ActivityExecutionResult + { + private readonly IActivity activity; + + public ScheduleActivityResult(IActivity activity) + { + this.activity = activity; + } + + protected override void Execute(WorkflowExecutionContext workflowContext) + { + workflowContext.ScheduleActivity(activity); + } + } +} \ No newline at end of file diff --git a/src/Flowsharp.Abstractions/Builders/ActivityWorkflowBuilder.cs b/src/Flowsharp.Abstractions/Builders/ActivityWorkflowBuilder.cs new file mode 100644 index 000000000..10f175f24 --- /dev/null +++ b/src/Flowsharp.Abstractions/Builders/ActivityWorkflowBuilder.cs @@ -0,0 +1,28 @@ +using System; +using Flowsharp.Activities; +using Flowsharp.Models; + +namespace Flowsharp.Builders +{ + public class ActivityWorkflowBuilder + { + private readonly IActivity activity; + private readonly WorkflowBuilder builder; + + public ActivityWorkflowBuilder(WorkflowBuilder builder, IActivity activity) + { + this.builder = builder; + this.activity = activity; + } + + public ActivityWorkflowBuilder Connect(IActivity target, Action activityBuilder = null) + { + var connection = new Connection(activity, target); + builder.Activities.Add(target); + builder.Connections.Add(connection); + + activityBuilder?.Invoke(new ActivityWorkflowBuilder(builder, target)); + return this; + } + } +} \ No newline at end of file diff --git a/src/Flowsharp.Abstractions/Builders/WorkflowBuilder.cs b/src/Flowsharp.Abstractions/Builders/WorkflowBuilder.cs new file mode 100644 index 000000000..410f85de4 --- /dev/null +++ b/src/Flowsharp.Abstractions/Builders/WorkflowBuilder.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Flowsharp.Activities; +using Flowsharp.Models; + +namespace Flowsharp.Builders +{ + public class WorkflowBuilder + { + public WorkflowBuilder() + { + Activities = new List(); + Connections = new List(); + } + + public IList Activities { get; } + public IList Connections { get; } + + public WorkflowBuilder AddActivity(IActivity activity, Action builder = null) + { + Activities.Add(activity); + builder?.Invoke(new ActivityWorkflowBuilder(this, activity)); + return this; + } + + public Workflow Build() + { + return new Workflow(Activities, Connections); + } + } +} \ No newline at end of file diff --git a/src/Flowsharp.Abstractions/Descriptors/ActivityDescriptor.cs b/src/Flowsharp.Abstractions/Descriptors/ActivityDescriptor.cs deleted file mode 100644 index 2d2c62428..000000000 --- a/src/Flowsharp.Abstractions/Descriptors/ActivityDescriptor.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Flowsharp.ActivityResults; -using Flowsharp.Models; - -namespace Flowsharp.Descriptors -{ - public class ActivityDescriptor - { - public string Name { get; set; } - - /// - /// Provides metadata about the specified activity. - /// - public Func GetMetadataAsync { get; set; } - - /// - /// Returns a list of possible outcomes when the activity is executed. - /// - public Func> GetOutcomes { get; set; } - - /// - /// Returns a value of whether the specified activity can execute. - /// - public Func> CanExecuteAsync { get; set; } - - /// - /// Executes the specified activity. - /// - public Func> ExecuteActivityAsync { get; set; } - - /// - /// Resumes the specified activity. - /// - public Func> ResumeActivityAsync { get; set; } - - /// - /// Executes before a workflow starts or resumes, giving activities an opportunity to read and store any values of interest. - /// - public Func, CancellationToken, Task> ReceiveInputAsync { get; set; } - - /// - /// Executes when a workflow is about to start. - /// - public Func WorkflowStartingAsync { get; set; } - - /// - /// Executes when a workflow has started. - /// - public Func WorkflowStartedAsync { get; set; } - - /// - /// Executes when a workflow is about to be resumed. - /// - public Func WorkflowResumingAsync { get; set; } - - /// - /// Executes when a workflow is resumed. - /// - public Func WorkflowResumedAsync { get; set; } - - /// - /// Executes when an activity is about to be executed. - /// - /// The activity for which the event is invoked. This is not necessarily the activity that is about to be executed. - /// The activity context containing the activity that is the subject of the event. - public Func OnActivityExecutingAsync { get; set; } - - /// - /// Called on each activity when an activity has been executed. - /// - /// The activity for which the event is invoked. This is not necessarily the activity that is about to be executed. - /// The activity context containing the activity that is the subject of the event. - public Func OnActivityExecutedAsync { get; set; } - } -} diff --git a/src/Flowsharp.Abstractions/Extensions/ConnectionActivityExtensions.cs b/src/Flowsharp.Abstractions/Extensions/ConnectionActivityExtensions.cs new file mode 100644 index 000000000..70db38456 --- /dev/null +++ b/src/Flowsharp.Abstractions/Extensions/ConnectionActivityExtensions.cs @@ -0,0 +1,9 @@ +using Flowsharp.Activities; +using Flowsharp.Models; + +namespace Flowsharp.Extensions +{ + public static class ConnectionActivityExtensions + { + } +} \ No newline at end of file diff --git a/src/Flowsharp.Abstractions/Extensions/InvokeExtensions.cs b/src/Flowsharp.Abstractions/Extensions/InvokeExtensions.cs index 38ed6091c..9fc0a08dd 100644 --- a/src/Flowsharp.Abstractions/Extensions/InvokeExtensions.cs +++ b/src/Flowsharp.Abstractions/Extensions/InvokeExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Flowsharp.Activities; using Microsoft.Extensions.Logging; namespace Flowsharp.Extensions @@ -10,7 +11,7 @@ namespace Flowsharp.Extensions /// /// Safely invoke methods by catching non fatal exceptions and logging them. /// - public static void Invoke(this IEnumerable events, Action dispatch, ILogger logger) + public static void Invoke(this IEnumerable events, Action dispatch, ILogger logger) { foreach (var sink in events) { @@ -20,7 +21,7 @@ namespace Flowsharp.Extensions } catch (Exception ex) { - HandleException(ex, logger, typeof(TEvents).Name, sink.GetType().FullName); + HandleException(ex, logger, typeof(T).Name, sink.GetType().FullName); } } } @@ -28,7 +29,7 @@ namespace Flowsharp.Extensions /// /// Safely invoke methods by catching non fatal exceptions and logging them. /// - public static async Task InvokeAsync(this IEnumerable events, Func dispatch, ILogger logger) + public static async Task InvokeAsync(this IEnumerable events, Func dispatch, ILogger logger) { foreach (var sink in events) { @@ -38,12 +39,12 @@ namespace Flowsharp.Extensions } catch (Exception ex) { - HandleException(ex, logger, typeof(TEvents).Name, sink.GetType().FullName); + HandleException(ex, logger, typeof(T).Name, sink.GetType().FullName); } } } - public static void HandleException(Exception ex, ILogger logger, string sourceType, string method) + private static void HandleException(Exception ex, ILogger logger, string sourceType, string method) { if (ex.IsFatal()) throw ex; diff --git a/src/Flowsharp.Abstractions/Flowsharp.Abstractions.csproj b/src/Flowsharp.Abstractions/Flowsharp.Abstractions.csproj index d7714b834..19ed3f430 100644 --- a/src/Flowsharp.Abstractions/Flowsharp.Abstractions.csproj +++ b/src/Flowsharp.Abstractions/Flowsharp.Abstractions.csproj @@ -3,6 +3,7 @@ netstandard2.0 Flowsharp + 7.1 diff --git a/src/Flowsharp.Abstractions/Models/ActivityExecutionContext.cs b/src/Flowsharp.Abstractions/Models/ActivityExecutionContext.cs index 9f1461f46..960e66f8b 100644 --- a/src/Flowsharp.Abstractions/Models/ActivityExecutionContext.cs +++ b/src/Flowsharp.Abstractions/Models/ActivityExecutionContext.cs @@ -1,19 +1,14 @@ -using Flowsharp.Descriptors; -using Newtonsoft.Json.Linq; +using Flowsharp.Activities; namespace Flowsharp.Models { public class ActivityExecutionContext { - public ActivityExecutionContext(ActivityType activityType, ActivityDescriptor activityDescriptor) + public ActivityExecutionContext(IActivity activity) { - ActivityType = activityType; - ActivityDescriptor = activityDescriptor; - State = new JObject(activityType.State); + Activity = activity; } - public ActivityType ActivityType { get; } - public ActivityDescriptor ActivityDescriptor { get; } - public JObject State { get; set; } + public IActivity Activity { get; private set; } } } diff --git a/src/Flowsharp.Abstractions/Models/ActivityMetadata.cs b/src/Flowsharp.Abstractions/Models/ActivityMetadata.cs deleted file mode 100644 index a4f9542ab..000000000 --- a/src/Flowsharp.Abstractions/Models/ActivityMetadata.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.Extensions.Localization; - -namespace Flowsharp.Abstractions.Models -{ - public class ActivityMetadata - { - public LocalizedString DisplayName { get; set; } - public LocalizedString Category { get; set; } - } -} diff --git a/src/Flowsharp.Abstractions/Models/ActivityMetadataContext.cs b/src/Flowsharp.Abstractions/Models/ActivityMetadataContext.cs deleted file mode 100644 index ae4c60bf3..000000000 --- a/src/Flowsharp.Abstractions/Models/ActivityMetadataContext.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Flowsharp.Abstractions.Models; - -namespace Flowsharp.Models -{ - public class ActivityMetadataContext - { - public ActivityMetadata Metadata { get; set; } - } -} diff --git a/src/Flowsharp.Abstractions/Models/ActivityType.cs b/src/Flowsharp.Abstractions/Models/ActivityType.cs deleted file mode 100644 index ad359611e..000000000 --- a/src/Flowsharp.Abstractions/Models/ActivityType.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Newtonsoft.Json.Linq; - -namespace Flowsharp.Models -{ - public class ActivityType - { - public ActivityType(string id, string name, JObject state = null) - { - Id = id; - Name = name; - State = state ?? new JObject(); - } - - public string Id { get; } - public string Name { get; } - public JObject State { get; } - } -} diff --git a/src/Flowsharp.Abstractions/Models/Connection.cs b/src/Flowsharp.Abstractions/Models/Connection.cs new file mode 100644 index 000000000..6b6f414c4 --- /dev/null +++ b/src/Flowsharp.Abstractions/Models/Connection.cs @@ -0,0 +1,28 @@ +using Flowsharp.Activities; + +namespace Flowsharp.Models +{ + public class Connection + { + public Connection() + { + } + + public Connection(IActivity source, IActivity target) : this(new SourceEndpoint(source), new TargetEndpoint(target)) + { + } + + public Connection(IActivity source, string sourceEndpointName, IActivity target) : this(new SourceEndpoint(source, sourceEndpointName), new TargetEndpoint(target)) + { + } + + public Connection(SourceEndpoint source, TargetEndpoint target) + { + Source = source; + Target = target; + } + + public SourceEndpoint Source { get; set; } + public TargetEndpoint Target { get; set; } + } +} \ No newline at end of file diff --git a/src/Flowsharp.Abstractions/Models/DestinationEndpoint.cs b/src/Flowsharp.Abstractions/Models/DestinationEndpoint.cs deleted file mode 100644 index be332d605..000000000 --- a/src/Flowsharp.Abstractions/Models/DestinationEndpoint.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Flowsharp.Models -{ - public class DestinationEndpoint : Endpoint - { - } -} diff --git a/src/Flowsharp.Abstractions/Models/Endpoint.cs b/src/Flowsharp.Abstractions/Models/Endpoint.cs index 96daadac6..5a10b2369 100644 --- a/src/Flowsharp.Abstractions/Models/Endpoint.cs +++ b/src/Flowsharp.Abstractions/Models/Endpoint.cs @@ -1,7 +1,18 @@ -namespace Flowsharp.Models +using Flowsharp.Activities; + +namespace Flowsharp.Models { - public class Endpoint + public abstract class Endpoint { - public string ActivityId { get; set; } + protected Endpoint() + { + } + + protected Endpoint(IActivity activity) + { + Activity = activity; + } + + public IActivity Activity { get; set; } } -} +} \ No newline at end of file diff --git a/src/Flowsharp.Abstractions/Models/Outcome.cs b/src/Flowsharp.Abstractions/Models/Outcome.cs deleted file mode 100644 index c3b7c9c78..000000000 --- a/src/Flowsharp.Abstractions/Models/Outcome.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Flowsharp.Json; -using Microsoft.Extensions.Localization; -using Newtonsoft.Json; - -namespace Flowsharp.Models -{ - public class Outcome - { - public Outcome(LocalizedString displayName) : this(displayName.Name, displayName) - { - } - - public Outcome(string name, LocalizedString displayName) - { - Name = name; - DisplayName = displayName; - } - - public string Name { get; } - - [JsonConverter(typeof(LocalizedStringConverter))] - public LocalizedString DisplayName { get; } - } -} diff --git a/src/Flowsharp.Abstractions/Models/SourceEndpoint.cs b/src/Flowsharp.Abstractions/Models/SourceEndpoint.cs index 164d05524..378aa0180 100644 --- a/src/Flowsharp.Abstractions/Models/SourceEndpoint.cs +++ b/src/Flowsharp.Abstractions/Models/SourceEndpoint.cs @@ -1,7 +1,18 @@ -namespace Flowsharp.Models +using Flowsharp.Activities; + +namespace Flowsharp.Models { public class SourceEndpoint : Endpoint { - public string OutcomeName { get; set; } + public SourceEndpoint() + { + } + + public SourceEndpoint(IActivity activity, string name = null) : base(activity) + { + Name = name; + } + + public string Name { get; set; } } -} +} \ No newline at end of file diff --git a/src/Flowsharp.Abstractions/Models/TargetEndpoint.cs b/src/Flowsharp.Abstractions/Models/TargetEndpoint.cs new file mode 100644 index 000000000..593f4b355 --- /dev/null +++ b/src/Flowsharp.Abstractions/Models/TargetEndpoint.cs @@ -0,0 +1,15 @@ +using Flowsharp.Activities; + +namespace Flowsharp.Models +{ + public class TargetEndpoint : Endpoint + { + public TargetEndpoint() + { + } + + public TargetEndpoint(IActivity activity) : base(activity) + { + } + } +} \ No newline at end of file diff --git a/src/Flowsharp.Abstractions/Models/Transition.cs b/src/Flowsharp.Abstractions/Models/Transition.cs deleted file mode 100644 index e99d8abe6..000000000 --- a/src/Flowsharp.Abstractions/Models/Transition.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Flowsharp.Models -{ - public class Transition - { - public SourceEndpoint From { get; set; } - public DestinationEndpoint To { get; set; } - } -} diff --git a/src/Flowsharp.Abstractions/Models/WorkflowExecutionContext.cs b/src/Flowsharp.Abstractions/Models/WorkflowExecutionContext.cs index e610255ef..040af39bb 100644 --- a/src/Flowsharp.Abstractions/Models/WorkflowExecutionContext.cs +++ b/src/Flowsharp.Abstractions/Models/WorkflowExecutionContext.cs @@ -1,46 +1,87 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; -using Flowsharp.Descriptors; +using System.Threading; +using System.Threading.Tasks; +using Flowsharp.Activities; namespace Flowsharp.Models { public class WorkflowExecutionContext { - public WorkflowExecutionContext(IDictionary activityDescriptorDictionary, WorkflowType workflowType, WorkflowStatus status) + public WorkflowExecutionContext(Workflow workflow, WorkflowStatus status = WorkflowStatus.Idle) { - WorkflowType = workflowType; - Activities = workflowType.Activities.ToDictionary(x => x.Id, x => new ActivityExecutionContext(x, activityDescriptorDictionary[x.Name])); - BlockingActivities = new List(); + Workflow = workflow; Status = status; - IsFirstPass = true; + IsFirstPass = true; + scheduledActivities = new Stack(); + scopes = new Stack(); - scheduledActivities = new Stack(); + BeginScope(); } - private readonly Stack scheduledActivities; + private readonly Stack scheduledActivities; + private readonly Stack scopes; - public WorkflowType WorkflowType { get; } - public IDictionary Activities { get; } - public ICollection BlockingActivities { get; } + public Workflow Workflow { get; } public WorkflowStatus Status { get; set; } public bool HasScheduledActivities => scheduledActivities.Any(); public bool IsFirstPass { get; set; } - public ActivityExecutionContext CurrentExecutingActivity { get; private set; } + public IActivity CurrentActivity { get; private set; } + public WorkflowExecutionScope CurrentScope { get; private set; } - public void PushScheduledActivity(ActivityExecutionContext activityExecutionContext) + public void BeginScope() + { + scopes.Push(CurrentScope = new WorkflowExecutionScope()); + } + + public void EndScope() { - scheduledActivities.Push(activityExecutionContext); + scopes.Pop(); + CurrentScope = scopes.Peek(); + } + + public void ScheduleActivity(IActivity activity) + { + scheduledActivities.Push(activity); } - public ActivityExecutionContext PopScheduledActivity() + public IActivity PopScheduledActivity() { - return CurrentExecutingActivity = scheduledActivities.Pop(); + CurrentActivity = scheduledActivities.Pop(); + return CurrentActivity; + } + + public void SetReturnValue(object value) + { + CurrentScope.ReturnValue = value; } - public void Fault(Exception exception, ActivityExecutionContext activity) + public void Fault(Exception exception, IActivity activity) { throw new NotImplementedException(); } + + public Task HaltAsync(CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public void Finish() + { + Status = WorkflowStatus.Finished; + } + + public virtual void ScheduleNextActivities(WorkflowExecutionContext workflowContext, SourceEndpoint endpoint) + { + var completedActivity = workflowContext.CurrentActivity; + var connections = Workflow.Connections.Where(x => x.Source.Activity == completedActivity && x.Source.Name == endpoint.Name); + + foreach (var connection in connections) + { + workflowContext.ScheduleActivity(connection.Target.Activity); + } + } } } diff --git a/src/Flowsharp.Abstractions/Models/WorkflowExecutionScope.cs b/src/Flowsharp.Abstractions/Models/WorkflowExecutionScope.cs new file mode 100644 index 000000000..7c505981e --- /dev/null +++ b/src/Flowsharp.Abstractions/Models/WorkflowExecutionScope.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace Flowsharp.Models +{ + public class WorkflowExecutionScope + { + public WorkflowExecutionScope() + { + Variables = new Dictionary(); + } + + public object ReturnValue { get; set; } + public IDictionary Variables { get; } + + public void SetVariable(string variableName, object value) + { + Variables[variableName] = value; + } + + public T GetVariable(string name) + { + return Variables.ContainsKey(name) ? (T)Variables[name] : default(T); + } + } +} \ No newline at end of file diff --git a/src/Flowsharp.Abstractions/Models/WorkflowType.cs b/src/Flowsharp.Abstractions/Models/WorkflowType.cs deleted file mode 100644 index b268e1695..000000000 --- a/src/Flowsharp.Abstractions/Models/WorkflowType.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; - -namespace Flowsharp.Models -{ - public class WorkflowType - { - public string Name { get; set; } - public ICollection Activities { get; set; } - public ICollection Transitions { get; set; } - } -} diff --git a/src/Flowsharp.Abstractions/Serialization/IWorkflowSerializer.cs b/src/Flowsharp.Abstractions/Serialization/IWorkflowSerializer.cs new file mode 100644 index 000000000..8d26a01dc --- /dev/null +++ b/src/Flowsharp.Abstractions/Serialization/IWorkflowSerializer.cs @@ -0,0 +1,11 @@ +using System.Threading; +using System.Threading.Tasks; +using Flowsharp.Activities; + +namespace Flowsharp.Services +{ + public interface IWorkflowSerializer + { + Task SerializeAsync(Workflow workflow, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Flowsharp.Abstractions/Services/IActivityLibrary.cs b/src/Flowsharp.Abstractions/Services/IActivityLibrary.cs deleted file mode 100644 index f6d85dea7..000000000 --- a/src/Flowsharp.Abstractions/Services/IActivityLibrary.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Flowsharp.Descriptors; - -namespace Flowsharp.Services -{ - public interface IActivityLibrary - { - Task> GetActivityDescriptorsAsync(CancellationToken cancellationToken); - } -} diff --git a/src/Flowsharp.Abstractions/Services/IActivityProvider.cs b/src/Flowsharp.Abstractions/Services/IActivityProvider.cs deleted file mode 100644 index 4c2e59831..000000000 --- a/src/Flowsharp.Abstractions/Services/IActivityProvider.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Flowsharp.Descriptors; - -namespace Flowsharp.Services -{ - /// - /// Implementors provide available activity descriptors. - /// - public interface IActivityProvider - { - Task> GetActivityDescriptorsAsync(CancellationToken cancellationToken); - } -} diff --git a/src/Flowsharp.Abstractions/Services/IWorkflowInvoker.cs b/src/Flowsharp.Abstractions/Services/IWorkflowInvoker.cs index 444cdaf46..f24c522a1 100644 --- a/src/Flowsharp.Abstractions/Services/IWorkflowInvoker.cs +++ b/src/Flowsharp.Abstractions/Services/IWorkflowInvoker.cs @@ -1,11 +1,12 @@ using System.Threading; using System.Threading.Tasks; +using Flowsharp.Activities; using Flowsharp.Models; namespace Flowsharp.Services { public interface IWorkflowInvoker { - Task InvokeAsync(WorkflowExecutionContext workflowContext, string startActivityId, CancellationToken cancellationToken); + Task InvokeAsync(Workflow workflow, IActivity startActivity = default, CancellationToken cancellationToken = default); } } diff --git a/src/Flowsharp.Core/Activities/IfElse.cs b/src/Flowsharp.Core/Activities/IfElse.cs new file mode 100644 index 000000000..cdedcdbb9 --- /dev/null +++ b/src/Flowsharp.Core/Activities/IfElse.cs @@ -0,0 +1,37 @@ +using System; +using Flowsharp.ActivityResults; +using Flowsharp.Models; + +namespace Flowsharp.Activities +{ + public class IfElse : Activity + { + private readonly Func condition; + + public IfElse() + { + } + + public IfElse(Func condition, IActivity trueBranch, IActivity falseBranch) + { + this.condition = condition; + TrueBranch = trueBranch; + FalseBranch = falseBranch; + } + + public IActivity TrueBranch { get; set; } + public IActivity FalseBranch { get; set;} + + protected override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext) + { + var result = condition(workflowContext, activityContext); + if (TrueBranch != null && result) + return ScheduleActivity(TrueBranch); + + if(FalseBranch != null && !result) + return ScheduleActivity(FalseBranch); + + return Noop(); + } + } +} \ No newline at end of file diff --git a/src/Flowsharp.Core/Activities/ReadLine.cs b/src/Flowsharp.Core/Activities/ReadLine.cs new file mode 100644 index 000000000..620e75514 --- /dev/null +++ b/src/Flowsharp.Core/Activities/ReadLine.cs @@ -0,0 +1,30 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Flowsharp.ActivityResults; +using Flowsharp.Models; + +namespace Flowsharp.Activities +{ + public class ReadLine : Activity + { + private readonly TextReader input; + + public ReadLine() : this(Console.In) + { + } + + public ReadLine(TextReader input) + { + this.input = input; + } + + public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, CancellationToken cancellationToken) + { + var value = await input.ReadLineAsync(); + workflowContext.SetReturnValue(value); + return ActivateEndpoint(); + } + } +} \ No newline at end of file diff --git a/src/Flowsharp.Core/Activities/SetVariable.cs b/src/Flowsharp.Core/Activities/SetVariable.cs new file mode 100644 index 000000000..670f5b460 --- /dev/null +++ b/src/Flowsharp.Core/Activities/SetVariable.cs @@ -0,0 +1,37 @@ +using System; +using Flowsharp.ActivityResults; +using Flowsharp.Models; + +namespace Flowsharp.Activities +{ + public class SetVariable : Activity + { + private readonly Func valueProvider; + + public SetVariable() + { + } + + public SetVariable(string name, Func valueProvider) : this(name, default(object)) + { + VariableName = name; + this.valueProvider = valueProvider; + } + + public SetVariable(string name, object value) + { + VariableName = name; + Value = value; + } + + public string VariableName { get; set; } + public object Value { get; set; } + + protected override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext) + { + var value = valueProvider(workflowContext, activityContext); + workflowContext.CurrentScope.SetVariable(VariableName, value); + return ActivateEndpoint(); + } + } +} \ No newline at end of file diff --git a/src/Flowsharp.Core/Activities/WriteLine.cs b/src/Flowsharp.Core/Activities/WriteLine.cs index 081d5b647..ed5e7fb59 100644 --- a/src/Flowsharp.Core/Activities/WriteLine.cs +++ b/src/Flowsharp.Core/Activities/WriteLine.cs @@ -1,19 +1,54 @@ using System; -using Flowsharp.Activities; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; using Flowsharp.ActivityResults; using Flowsharp.Models; -namespace Flowsharp.ActivityProviders +namespace Flowsharp.Activities { /// - /// Provides activities based on implementations that have been registered with the service container. + /// Writes a text string to the specified stream. /// public class WriteLine : Activity { - protected override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext) + private readonly TextWriter output; + private readonly Func textProvider; + + public WriteLine() : this(Console.Out, null) { - Console.WriteLine("Hello World!"); - return Outcomes("Done"); + } + + public WriteLine(string text) : this(Console.Out, text) + { + } + + public WriteLine(Func textProvider) : this(Console.Out, null) + { + this.textProvider = textProvider; + } + + public WriteLine(TextWriter output, string text) + { + this.output = output; + Text = text; + textProvider = (w, a) => Text; + } + + public string Text { get; set; } + + protected override bool CanExecute(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext) + { + var text = textProvider(workflowContext, activityContext); + return text != null; + } + + public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, CancellationToken cancellationToken) + { + var text = textProvider(workflowContext, activityContext); + await output.WriteLineAsync(text); + return ActivateEndpoint(); } } } diff --git a/src/Flowsharp.Core/Flowsharp.Core.csproj b/src/Flowsharp.Core/Flowsharp.Core.csproj index c7919e206..5a0e596aa 100644 --- a/src/Flowsharp.Core/Flowsharp.Core.csproj +++ b/src/Flowsharp.Core/Flowsharp.Core.csproj @@ -3,6 +3,7 @@ netstandard2.0 Flowsharp + 7.1 @@ -14,4 +15,8 @@ + + + + diff --git a/src/Flowsharp.Core/Serialization/JsonWorkflowSerializer.cs b/src/Flowsharp.Core/Serialization/JsonWorkflowSerializer.cs new file mode 100644 index 000000000..02db3c014 --- /dev/null +++ b/src/Flowsharp.Core/Serialization/JsonWorkflowSerializer.cs @@ -0,0 +1,22 @@ +using System.Threading; +using System.Threading.Tasks; +using Flowsharp.Activities; +using Flowsharp.Services; +using Newtonsoft.Json; + +namespace Flowsharp.Serialization +{ + public class JsonWorkflowSerializer : IWorkflowSerializer + { + public Task SerializeAsync(Workflow workflow, CancellationToken cancellationToken) + { + var settings = new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.Objects, + TypeNameHandling = TypeNameHandling.Objects + }; + var json = JsonConvert.SerializeObject(workflow, settings); + return Task.FromResult(json); + } + } +} \ No newline at end of file diff --git a/src/Flowsharp.Core/Services/ActivityLibrary.cs b/src/Flowsharp.Core/Services/ActivityLibrary.cs deleted file mode 100644 index 227b37952..000000000 --- a/src/Flowsharp.Core/Services/ActivityLibrary.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Flowsharp.Descriptors; - -namespace Flowsharp.Services -{ - public class ActivityLibrary : IActivityLibrary - { - private readonly IEnumerable providers; - - public ActivityLibrary(IEnumerable providers) - { - this.providers = providers; - } - - public async Task> GetActivityDescriptorsAsync(CancellationToken cancellationToken) - { - var tasks = providers.Select(x => x.GetActivityDescriptorsAsync(cancellationToken)).ToList(); - var results = await Task.WhenAll(tasks); - - return results.SelectMany(x => x); - } - } - - public static class ActivityLibraryExtensions - { - public static async Task> GetActivityDescriptorDictionaryAsync(this IActivityLibrary activityLibrary, CancellationToken cancellationToken) - { - var activityDescriptors = await activityLibrary.GetActivityDescriptorsAsync(cancellationToken); - return activityDescriptors.ToDictionary(x => x.Name); - } - } -} diff --git a/src/Flowsharp.Core/Services/WorkflowInvoker.cs b/src/Flowsharp.Core/Services/WorkflowInvoker.cs index 6447c1bb1..1ed478cbb 100644 --- a/src/Flowsharp.Core/Services/WorkflowInvoker.cs +++ b/src/Flowsharp.Core/Services/WorkflowInvoker.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Flowsharp.Activities; using Flowsharp.ActivityResults; using Flowsharp.Extensions; using Flowsharp.Models; @@ -18,82 +20,87 @@ namespace Flowsharp.Services private readonly ILogger logger; - public async Task InvokeAsync(WorkflowExecutionContext workflowContext, string startActivityId, CancellationToken cancellationToken) + public async Task InvokeAsync(Workflow workflow, IActivity startActivity = default, CancellationToken cancellationToken = default) { - var isResuming = workflowContext.Status == WorkflowStatus.Resuming; - var startActivity = workflowContext.Activities[startActivityId]; + var workflowExecutionContext = new WorkflowExecutionContext(workflow); + var isResuming = workflowExecutionContext.Status == WorkflowStatus.Resuming; - workflowContext.Status = WorkflowStatus.Executing; - workflowContext.PushScheduledActivity(startActivity); - - while (workflowContext.HasScheduledActivities) + if (startActivity == null) + startActivity = workflow.Activities.First(); + + workflowExecutionContext.Status = WorkflowStatus.Executing; + workflowExecutionContext.ScheduleActivity(startActivity); + await InvokeActivitiesAsync(workflowExecutionContext, x => x.WorkflowStartingAsync(workflowExecutionContext, cancellationToken)); + + while (workflowExecutionContext.HasScheduledActivities) { - var currentActivity = workflowContext.PopScheduledActivity(); + var currentActivity = workflowExecutionContext.PopScheduledActivity(); + var result = await ExecuteActivityAsync(workflowExecutionContext, currentActivity, isResuming, cancellationToken); - if (!await ExecuteActivityAsync(workflowContext, currentActivity, isResuming, cancellationToken)) + if(result == null) break; + + await result.ExecuteAsync(workflowExecutionContext, cancellationToken); - workflowContext.IsFirstPass = false; + workflowExecutionContext.IsFirstPass = false; isResuming = false; } - workflowContext.Status = workflowContext.BlockingActivities.Any() ? WorkflowStatus.Halted : WorkflowStatus.Finished; + workflowExecutionContext.Status = WorkflowStatus.Finished; + + return workflowExecutionContext; } - private async Task ExecuteActivityAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, bool isResuming, CancellationToken cancellationToken) + private async Task ExecuteActivityAsync(WorkflowExecutionContext workflowContext, IActivity activity, bool isResuming, CancellationToken cancellationToken) { try { - await InvokeActivitiesAsync(workflowContext, x => x.ActivityDescriptor.OnActivityExecutingAsync(workflowContext, activityContext, cancellationToken)); + //await InvokeActivitiesAsync(workflowContext, x => x.ActivityDescriptor.OnActivityExecutingAsync(workflowContext, activity, cancellationToken)); if (cancellationToken.IsCancellationRequested) { workflowContext.Status = WorkflowStatus.Aborted; - return false; + return null; } - var result = await ExecuteOrResumeActivityAsync(workflowContext, activityContext, isResuming, cancellationToken); - await InvokeActivitiesAsync(workflowContext, x => x.ActivityDescriptor.OnActivityExecutedAsync(workflowContext, activityContext, cancellationToken)); - await result.ExecuteAsync(workflowContext, cancellationToken); + return await ExecuteOrResumeActivityAsync(workflowContext, activity, isResuming, cancellationToken); + //await InvokeActivitiesAsync(workflowContext, x => x.ActivityDescriptor.OnActivityExecutedAsync(workflowContext, activity, cancellationToken)); } catch (Exception ex) { - FaultWorkflow(workflowContext, activityContext, ex); + FaultWorkflow(workflowContext, activity, ex); } - return true; + return null; } - private void FaultWorkflow(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, Exception ex) + private void FaultWorkflow(WorkflowExecutionContext workflowContext, IActivity activity, Exception ex) { logger.LogError( ex, - "An unhandled error occurred while executing an activity. Workflow ID: '{WorkflowTypeId}'. Activity: '{ActivityId}', '{ActivityName}'. Putting the workflow in the faulted state.", - workflowContext.WorkflowType.Name, - activityContext.ActivityType.Id, - activityContext.ActivityType.Name + "An unhandled error occurred while executing an activity. Putting the workflow in the faulted state." ); - workflowContext.Fault(ex, activityContext); + workflowContext.Fault(ex, activity); } - private async Task ExecuteOrResumeActivityAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activity, bool isResuming, CancellationToken cancellationToken) + private async Task ExecuteOrResumeActivityAsync(WorkflowExecutionContext workflowContext, IActivity activity, bool isResuming, CancellationToken cancellationToken) { if (!isResuming) { // Execute the current activity. - return await activity.ActivityDescriptor.ExecuteActivityAsync(workflowContext, activity, cancellationToken); + return await activity.ExecuteAsync(workflowContext, new ActivityExecutionContext(activity), cancellationToken); } else { // Resume the current activity. - return await activity.ActivityDescriptor.ResumeActivityAsync(workflowContext, activity, cancellationToken); + return await activity.ResumeAsync(workflowContext, new ActivityExecutionContext(activity), cancellationToken); } } - private async Task InvokeActivitiesAsync(WorkflowExecutionContext workflowContext, Func action) + private async Task InvokeActivitiesAsync(WorkflowExecutionContext workflowContext, Func action) { - await workflowContext.Activities.Values.InvokeAsync(action, logger); + await workflowContext.Workflow.Activities.InvokeAsync(action, logger); } } } diff --git a/src/Flowsharp.Fluid/Flowsharp.Fluid.csproj b/src/Flowsharp.Fluid/Flowsharp.Fluid.csproj deleted file mode 100644 index 633d246d2..000000000 --- a/src/Flowsharp.Fluid/Flowsharp.Fluid.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - netstandard2.0 - - - - - - - diff --git a/src/Flowsharp.Fluid/WorkflowBuilder.cs b/src/Flowsharp.Fluid/WorkflowBuilder.cs deleted file mode 100644 index a4e310cb2..000000000 --- a/src/Flowsharp.Fluid/WorkflowBuilder.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using Flowsharp.Models; - -namespace Flowsharp.Fluid -{ - public class WorkflowBuilder - { - public WorkflowBuilder() - { - activityTypes = new List(); - } - - private IList activityTypes; - - } -} \ No newline at end of file diff --git a/src/Flowsharp.Samples.Console/Flowchart1.yaml b/src/Flowsharp.Samples.Console/Flowchart1.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/src/Flowsharp.Samples.Console/Program.cs b/src/Flowsharp.Samples.Console/Program.cs index c7afb65c4..dc36693db 100644 --- a/src/Flowsharp.Samples.Console/Program.cs +++ b/src/Flowsharp.Samples.Console/Program.cs @@ -1,8 +1,9 @@ -using System.Threading; +using System; +using System.Threading; using System.Threading.Tasks; using Flowsharp.Activities; -using Flowsharp.ActivityProviders; -using Flowsharp.Models; +using Flowsharp.Builders; +using Flowsharp.Serialization; using Flowsharp.Services; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -13,22 +14,30 @@ namespace Flowsharp.Samples.Console { static async Task Main() { - var workflowType = new WorkflowType - { - Activities = new[] - { - new ActivityType("1", "WriteLine") - }, - Transitions = new Transition[0] - }; - - var typedActivityProvider = new TypedActivityProvider(() => new IActivity[] { new WriteLine() } ); - var activityLibrary = new ActivityLibrary(new[]{ typedActivityProvider }); - var dictionary = await activityLibrary.GetActivityDescriptorDictionaryAsync(CancellationToken.None); - var workflowContext = new WorkflowExecutionContext(dictionary, workflowType, WorkflowStatus.Idle); var invoker = new WorkflowInvoker(new Logger(new NullLoggerFactory())); + + var workflow = new WorkflowBuilder() + .AddActivity(new WriteLine("You have now transitioned into a networked workflow."), helloWorld => + helloWorld.Connect(new WriteLine("Let's run a program."), runProgram => + runProgram.Connect(new WriteLine("Enter first value:"), firstValue => + firstValue.Connect(new ReadLine(), value1 => + value1.Connect(new SetVariable("x", (w, a) => int.Parse((string)w.CurrentScope.ReturnValue)), setX => + setX.Connect(new WriteLine("Enter second value:"), secondValue => + secondValue.Connect(new ReadLine(), value2 => + value2.Connect(new SetVariable("y", (w, a) => int.Parse((string)w.CurrentScope.ReturnValue)), setY => + setY.Connect(new WriteLine((w, a) => + { + var x = w.CurrentScope.GetVariable("x"); + var y = w.CurrentScope.GetVariable("y"); + var z = x + y; + return $"{x} + {y} = {z}"; + })))))))))) + .Build(); - await invoker.InvokeAsync(workflowContext, "1", CancellationToken.None); + await invoker.InvokeAsync(workflow); + + var serializer = new JsonWorkflowSerializer(); + var json = await serializer.SerializeAsync(workflow, CancellationToken.None); } } }