diff --git a/ActionFlow.Tests/ActionFlow.Tests.csproj b/ActionFlow.Tests/ActionFlow.Tests.csproj index aaf7cd2..b193972 100644 --- a/ActionFlow.Tests/ActionFlow.Tests.csproj +++ b/ActionFlow.Tests/ActionFlow.Tests.csproj @@ -10,6 +10,7 @@ + @@ -24,4 +25,10 @@ + + + Always + + + diff --git a/ActionFlow.Tests/Extensions/ServiceCollectionExtensionsTests.cs b/ActionFlow.Tests/Extensions/ServiceCollectionExtensionsTests.cs index 5538f9d..604dff3 100644 --- a/ActionFlow.Tests/Extensions/ServiceCollectionExtensionsTests.cs +++ b/ActionFlow.Tests/Extensions/ServiceCollectionExtensionsTests.cs @@ -23,10 +23,10 @@ public void When_using_action_flow_engine_it_should_register_all_required_servic //Assert Assert.IsNotNull(provider.GetRequiredService()); - Assert.IsNotNull(provider.GetRequiredService()); Assert.IsNotNull(provider.GetRequiredService()); Assert.IsNotNull(provider.GetRequiredService()); Assert.IsNotNull(provider.GetRequiredService()); + Assert.IsNotNull(provider.GetRequiredService()); Assert.IsNotNull(provider.GetRequiredService>()); var stepActionFactory = provider.GetRequiredService(); diff --git a/ActionFlow.Tests/Providers/WorkflowProviderBuiltinExtensionsTests.cs b/ActionFlow.Tests/Providers/WorkflowProviderBuiltinExtensionsTests.cs new file mode 100644 index 0000000..0ac97d3 --- /dev/null +++ b/ActionFlow.Tests/Providers/WorkflowProviderBuiltinExtensionsTests.cs @@ -0,0 +1,72 @@ +using ActionFlow.Domain.Engine; +using ActionFlow.Engine.Providers; +using ActionFlow.Engine.Providers.Extensions; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using System.Text.Json; + +namespace ActionFlow.Tests.Providers; + +[TestClass] +public class WorkflowProviderBuiltinExtensionsTests +{ + [TestMethod] + public void When_using_from_json_it_should_add_workflow() + { + //Arrange + var workflows = new List + { + new("Test", + [ + new("initialize", "Variable", new Dictionary + { + { "age", "1" }, + { "canWalk", "true" }, + }), + new("test variable value", "Variable", [], "age == 1 && canWalk == true") + ]) + }; + + var workflowJson = JsonSerializer.SerializeToNode(workflows); + var services = new ServiceCollection(); + services.AddJsonWorkflowProvider(workflowJson!.AsArray()); + + //Act + var provider = services.BuildServiceProvider(); + + //Assert + var workflowProvider = provider.GetRequiredService(); + var result = workflowProvider.GetAllWorkflows(); + result.Should().BeEquivalentTo(workflows); + } + + [TestMethod] + public void When_using_from_json_file_it_should_add_workflow() + { + //Arrange + var expected = new List + { + new("Test", + [ + new("initialize", "Variable", new Dictionary + { + { "age", "1" }, + { "canWalk", "true" }, + }), + new("test variable value", "Variable", [], "age == 1 && canWalk == true") + ]) + }; + + var services = new ServiceCollection(); + var path = Path.Combine(Environment.CurrentDirectory, @"Providers\workflows.json"); + services.AddJsonWorkflowProvider(path); + + //Act + var provider = services.BuildServiceProvider(); + + //Assert + var workflowProvider = provider.GetRequiredService(); + var result = workflowProvider.GetAllWorkflows(); + result.Should().BeEquivalentTo(expected); + } +} diff --git a/ActionFlow.Tests/Providers/WorkflowProviderTests.cs b/ActionFlow.Tests/Providers/WorkflowProviderTests.cs new file mode 100644 index 0000000..d38b811 --- /dev/null +++ b/ActionFlow.Tests/Providers/WorkflowProviderTests.cs @@ -0,0 +1,26 @@ +using ActionFlow.Domain.Engine; +using ActionFlow.Engine.Providers; + +namespace ActionFlow.Tests.Providers; + +[TestClass] +public class WorkflowProviderTests +{ + [TestMethod] + public void When_getting_all_workflows_it_should_return_a_list() + { + //Arrange + var testWorkflows = new List + { + new("test", []) + }; + var sut = new WorkflowProvider(testWorkflows); + + //Act + var result = sut.GetAllWorkflows(); + + //Assert + Assert.IsInstanceOfType(result, typeof(List)); + Assert.IsTrue(result.Contains(testWorkflows[0])); + } +} diff --git a/ActionFlow.Tests/Providers/workflows.json b/ActionFlow.Tests/Providers/workflows.json new file mode 100644 index 0000000..30994cc --- /dev/null +++ b/ActionFlow.Tests/Providers/workflows.json @@ -0,0 +1,23 @@ +[ + { + "WorkflowName": "Test", + "Steps": [ + { + "Name": "initialize", + "ActionType": "Variable", + "ConditionExpression": null, + "Properties": { + "age": "1", + "canWalk": "true" + } + }, + { + "Name": "test variable value", + "ActionType": "Variable", + "ConditionExpression": "age == 1 \u0026\u0026 canWalk == true", + "Properties": {} + } + ], + "OutputParameters": null + } +] \ No newline at end of file diff --git a/ActionFlow/Domain/Engine/Step.cs b/ActionFlow/Domain/Engine/Step.cs index 5a80826..047e747 100644 --- a/ActionFlow/Domain/Engine/Step.cs +++ b/ActionFlow/Domain/Engine/Step.cs @@ -1,18 +1,19 @@ namespace ActionFlow.Domain.Engine { - public class Step - { - public Step(string name, string actionType, Dictionary? properties = null, string? conditionExpression = null) - { - Name = name; - ActionType = actionType; - Properties = properties ?? []; - ConditionExpression = conditionExpression; - } + public class Step + { + public Step(string name, string actionType, Dictionary? properties = null, string? conditionExpression = null) + { + Name = name; + ActionType = actionType; + Properties = properties ?? []; + ConditionExpression = conditionExpression; + } - public string Name { get; } - public string ActionType { get; } - public string? ConditionExpression { get; } - public Dictionary? Properties { get; } - } + public string Name { get; } + public string ActionType { get; } + public string? ConditionExpression { get; } + + public Dictionary? Properties { get; } + } } diff --git a/ActionFlow/Engine/Providers/Extensions/WorkflowProviderBuiltinExtensions.cs b/ActionFlow/Engine/Providers/Extensions/WorkflowProviderBuiltinExtensions.cs index 2086481..22f9dc2 100644 --- a/ActionFlow/Engine/Providers/Extensions/WorkflowProviderBuiltinExtensions.cs +++ b/ActionFlow/Engine/Providers/Extensions/WorkflowProviderBuiltinExtensions.cs @@ -1,28 +1,59 @@ using ActionFlow.Domain.Engine; +using Microsoft.Extensions.DependencyInjection; using System.Text.Json; using System.Text.Json.Nodes; +using System.Text.Json.Serialization; namespace ActionFlow.Engine.Providers.Extensions; public static class WorkflowProviderBuiltinExtensions { - public static void UseFromJson(this IWorkflowProvider workflowProvider, JsonArray workflowsJson) + public static void AddJsonWorkflowProvider(this IServiceCollection services, string jsonPath) { - var workflows = workflowsJson.Deserialize(); + var jsonString = File.ReadAllText(jsonPath); + var workflowsJson = JsonArray.Parse(jsonString)?.AsArray(); + + services.AddJsonWorkflowProvider(workflowsJson!); + } + + public static void AddJsonWorkflowProvider(this IServiceCollection services, JsonArray workflowsJson) + { + var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + options.Converters.Add(new ObjectToStringConverter()); + + var workflows = workflowsJson.Deserialize>(options); if (workflows != null) { - foreach (var workflow in workflows) + services.AddScoped(x => { - workflowProvider.AddWorkflow(workflow); - } + return new WorkflowProvider([.. workflows]); + }); } } - public static void UseFromJson(this IWorkflowProvider workflowProvider, string jsonPath) + public class ObjectToStringConverter : JsonConverter { - var jsonBytes = File.ReadAllBytes(jsonPath); - var workflowsJson = JsonArray.Parse(jsonBytes)?.AsArray(); + public override bool CanConvert(Type typeToConvert) => typeof(object) == typeToConvert; - workflowProvider.UseFromJson(workflowsJson!); + public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Number) + { + return reader.TryGetInt64(out long l) ? l.ToString() : reader.GetDouble().ToString(); + } + if (reader.TokenType == JsonTokenType.String) + { + return reader.GetString(); + } + using (JsonDocument document = JsonDocument.ParseValue(ref reader)) + { + return document.RootElement.Clone().ToString(); + } + } + + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); + } } } diff --git a/ActionFlow/Engine/Providers/IWorkflowProvider.cs b/ActionFlow/Engine/Providers/IWorkflowProvider.cs index b2897b1..884116d 100644 --- a/ActionFlow/Engine/Providers/IWorkflowProvider.cs +++ b/ActionFlow/Engine/Providers/IWorkflowProvider.cs @@ -5,6 +5,5 @@ namespace ActionFlow.Engine.Providers public interface IWorkflowProvider { List GetAllWorkflows(); - void AddWorkflow(Workflow workflow); } } \ No newline at end of file diff --git a/ActionFlow/Engine/Providers/WorkflowProvider.cs b/ActionFlow/Engine/Providers/WorkflowProvider.cs index de661d3..d6c5deb 100644 --- a/ActionFlow/Engine/Providers/WorkflowProvider.cs +++ b/ActionFlow/Engine/Providers/WorkflowProvider.cs @@ -2,34 +2,30 @@ namespace ActionFlow.Engine.Providers { - public class WorkflowProvider : IWorkflowProvider + public class BlankWorkflowProvider() : WorkflowProvider(new List()) { - protected List _workflows = []; - public void AddWorkflow(Workflow workflow) - { - _workflows.Add(workflow); - } + } + public class WorkflowProvider(List workflows) : IWorkflowProvider + { public List GetAllWorkflows() { - List workflows = []; - //Test //Todo: Load from DB, Json, or other source - var steps = new List - { - new("initialize", "Variable", new Dictionary - { - { "age", "1" }, - { "canWalk", "true" }, - }), - new("test variable value", "Variable", [], "age == 1 && canWalk == true") - }; + //var steps = new List + //{ + // new("initialize", "Variable", new Dictionary + // { + // { "age", "1" }, + // { "canWalk", "true" }, + // }), + // new("test variable value", "Variable", [], "age == 1 && canWalk == true") + //}; - Workflow workflow = new Workflow("Test Workflow Rule 1", steps); - workflows.Add(workflow); - //Test End + //Workflow workflow = new Workflow("Test Workflow Rule 1", steps); + //workflows.Add(workflow); + ////Test End return workflows; } diff --git a/ActionFlow/Extensions/ServiceCollectionExtensions.cs b/ActionFlow/Extensions/ServiceCollectionExtensions.cs index 2deb1a9..95c46a5 100644 --- a/ActionFlow/Extensions/ServiceCollectionExtensions.cs +++ b/ActionFlow/Extensions/ServiceCollectionExtensions.cs @@ -27,7 +27,7 @@ public static void AddDefaultActions(this IServiceCollection services) public static void UseActionFlowEngine(this IServiceCollection services) { services.AddScoped(); - services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped();