From 4e06192e8e16b55b7dc0adc9a63992d41602b6d6 Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 14 Apr 2023 16:56:46 +0100 Subject: [PATCH 01/10] Update dotnet.yml --- .github/workflows/dotnet.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 25f43e7..7b53e0e 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -5,9 +5,9 @@ name: .NET on: push: - branches: [ "main" ] + branches: [ "main", "develop" ] pull_request: - branches: [ "main" ] + branches: [ "main", "develop" ] jobs: build: From 39d10bf09297620941425ba8c9542edda17d19dd Mon Sep 17 00:00:00 2001 From: Thomas Wright Date: Fri, 14 Apr 2023 18:04:34 +0100 Subject: [PATCH 02/10] added snowflake still broken :( --- Coflo.sln | 21 +++++ .../Coflo.Core.Snowflake.csproj | 14 ++++ .../Generators/IIdGenerator.cs | 6 ++ .../Generators/IdGenerator.cs | 84 +++++++++++++++++++ .../Coflo.Hosting.Orchestrator.csproj | 9 ++ .../Coflo.Core.Snowflake.Tests.csproj | 32 +++++++ .../IdGeneratorTests.cs | 38 +++++++++ .../core/Coflo.Core.Snowflake.Tests/Usings.cs | 1 + 8 files changed, 205 insertions(+) create mode 100644 source/hosting/Coflo.Core.Snowflake/Coflo.Core.Snowflake.csproj create mode 100644 source/hosting/Coflo.Core.Snowflake/Generators/IIdGenerator.cs create mode 100644 source/hosting/Coflo.Core.Snowflake/Generators/IdGenerator.cs create mode 100644 source/hosting/Coflo.Hosting.Orchestrator/Coflo.Hosting.Orchestrator.csproj create mode 100644 tests/core/Coflo.Core.Snowflake.Tests/Coflo.Core.Snowflake.Tests.csproj create mode 100644 tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs create mode 100644 tests/core/Coflo.Core.Snowflake.Tests/Usings.cs diff --git a/Coflo.sln b/Coflo.sln index bda815c..847b1e8 100644 --- a/Coflo.sln +++ b/Coflo.sln @@ -44,6 +44,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{FA4C5E4B-B EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.SDK.Tests", "tests\core\Coflo.SDK.Tests\Coflo.SDK.Tests.csproj", "{7FE22FFD-CB25-446A-9192-76127270C3E1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Hosting.Orchestrator", "source\hosting\Coflo.Hosting.Orchestrator\Coflo.Hosting.Orchestrator.csproj", "{3009ACF8-F2DC-492C-8E41-7E36AB3A7A6C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Core.Snowflake", "source\hosting\Coflo.Core.Snowflake\Coflo.Core.Snowflake.csproj", "{2FF4D33E-2964-41B3-B0B2-3C9D70616FBA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Core.Snowflake.Tests", "tests\core\Coflo.Core.Snowflake.Tests\Coflo.Core.Snowflake.Tests.csproj", "{75110834-EBD6-450A-9092-B619CE02FEAC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -66,6 +72,9 @@ Global {2569CD34-41EF-42A3-AE63-BE01A0124D44} = {C46B2D26-B277-4EE0-9497-0473D52EE7A9} {FA4C5E4B-B42D-4BC5-9F26-FD32CD88F75F} = {1230DC9F-E226-4466-B0B1-99B52B66232C} {7FE22FFD-CB25-446A-9192-76127270C3E1} = {FA4C5E4B-B42D-4BC5-9F26-FD32CD88F75F} + {3009ACF8-F2DC-492C-8E41-7E36AB3A7A6C} = {DEAF9660-31F2-4E6B-9A77-93BA08D7D122} + {2FF4D33E-2964-41B3-B0B2-3C9D70616FBA} = {C46B2D26-B277-4EE0-9497-0473D52EE7A9} + {75110834-EBD6-450A-9092-B619CE02FEAC} = {FA4C5E4B-B42D-4BC5-9F26-FD32CD88F75F} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {28DE14D7-4F2E-40D9-B847-69866240023D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -104,5 +113,17 @@ Global {7FE22FFD-CB25-446A-9192-76127270C3E1}.Debug|Any CPU.Build.0 = Debug|Any CPU {7FE22FFD-CB25-446A-9192-76127270C3E1}.Release|Any CPU.ActiveCfg = Release|Any CPU {7FE22FFD-CB25-446A-9192-76127270C3E1}.Release|Any CPU.Build.0 = Release|Any CPU + {3009ACF8-F2DC-492C-8E41-7E36AB3A7A6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3009ACF8-F2DC-492C-8E41-7E36AB3A7A6C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3009ACF8-F2DC-492C-8E41-7E36AB3A7A6C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3009ACF8-F2DC-492C-8E41-7E36AB3A7A6C}.Release|Any CPU.Build.0 = Release|Any CPU + {2FF4D33E-2964-41B3-B0B2-3C9D70616FBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2FF4D33E-2964-41B3-B0B2-3C9D70616FBA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2FF4D33E-2964-41B3-B0B2-3C9D70616FBA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2FF4D33E-2964-41B3-B0B2-3C9D70616FBA}.Release|Any CPU.Build.0 = Release|Any CPU + {75110834-EBD6-450A-9092-B619CE02FEAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75110834-EBD6-450A-9092-B619CE02FEAC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75110834-EBD6-450A-9092-B619CE02FEAC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75110834-EBD6-450A-9092-B619CE02FEAC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/source/hosting/Coflo.Core.Snowflake/Coflo.Core.Snowflake.csproj b/source/hosting/Coflo.Core.Snowflake/Coflo.Core.Snowflake.csproj new file mode 100644 index 0000000..da7176b --- /dev/null +++ b/source/hosting/Coflo.Core.Snowflake/Coflo.Core.Snowflake.csproj @@ -0,0 +1,14 @@ + + + + net7.0 + enable + enable + + + + + + + + diff --git a/source/hosting/Coflo.Core.Snowflake/Generators/IIdGenerator.cs b/source/hosting/Coflo.Core.Snowflake/Generators/IIdGenerator.cs new file mode 100644 index 0000000..130b277 --- /dev/null +++ b/source/hosting/Coflo.Core.Snowflake/Generators/IIdGenerator.cs @@ -0,0 +1,6 @@ +namespace Coflo.Core.Snowflake.Generators; + +public interface IIdGenerator +{ + Task NextId(); +} \ No newline at end of file diff --git a/source/hosting/Coflo.Core.Snowflake/Generators/IdGenerator.cs b/source/hosting/Coflo.Core.Snowflake/Generators/IdGenerator.cs new file mode 100644 index 0000000..a1a5594 --- /dev/null +++ b/source/hosting/Coflo.Core.Snowflake/Generators/IdGenerator.cs @@ -0,0 +1,84 @@ +using Microsoft.Extensions.Configuration; +using NodaTime; + +namespace Coflo.Core.Snowflake.Generators; + +public class IdGenerator : IIdGenerator +{ + private const int _nodeIdBits = 10; + private const int _sequenceBits = 12; + internal const int _epochBits = 41; + internal const int _unusedBits = 1; + + private readonly int _maxMachineId = (int)(Math.Pow(2, _nodeIdBits) - 1); + private readonly int _maxSequence = (int)(Math.Pow(2, _sequenceBits) - 1); + + private const long _epoch = 1675209600L; + + private readonly int _machineId; + private readonly IClock _clock; + + private long _lastTimeStamp = -1L; + private long _sequence = 0L; + + private readonly Mutex _mutex; + + public IdGenerator(IConfiguration configuration, IClock clock) + { + var machineId = configuration.GetValue("MachineId"); + + if(machineId < 0 || machineId > _maxMachineId) + { + throw new ArgumentException($"Node ID must be between 0 and {_maxMachineId}"); + } + + _mutex = new Mutex(); + _machineId = machineId; + _clock = clock; + } + + internal long GetTimestamp() + { + var now = _clock.GetCurrentInstant(); + var epoch = Instant.FromUnixTimeSeconds(_epoch); + var duration = now.ToUnixTimeMilliseconds()- epoch.ToUnixTimeMilliseconds(); + + return duration; + } + + public async Task NextId() + { + _mutex.WaitOne(); + + var currentTimeStamp = GetTimestamp(); + + if (currentTimeStamp < _lastTimeStamp && _sequence >= 1) throw new SystemException("System Clock Invalid"); + + if (currentTimeStamp == _lastTimeStamp) + { + _sequence = _sequence++ & _maxSequence; + + if (_sequence == 0) + { + await Task.Delay(1); + + currentTimeStamp = GetTimestamp(); + } + } + else + { + _sequence = 0; + } + + _lastTimeStamp = currentTimeStamp; + + var id = + (currentTimeStamp << _epochBits) | + (uint)(_machineId << _nodeIdBits) | (_sequence << _sequenceBits) + | 1 << _unusedBits; + + _mutex.ReleaseMutex(); + + return id; + } +} \ No newline at end of file diff --git a/source/hosting/Coflo.Hosting.Orchestrator/Coflo.Hosting.Orchestrator.csproj b/source/hosting/Coflo.Hosting.Orchestrator/Coflo.Hosting.Orchestrator.csproj new file mode 100644 index 0000000..6836c68 --- /dev/null +++ b/source/hosting/Coflo.Hosting.Orchestrator/Coflo.Hosting.Orchestrator.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/tests/core/Coflo.Core.Snowflake.Tests/Coflo.Core.Snowflake.Tests.csproj b/tests/core/Coflo.Core.Snowflake.Tests/Coflo.Core.Snowflake.Tests.csproj new file mode 100644 index 0000000..21d9ac4 --- /dev/null +++ b/tests/core/Coflo.Core.Snowflake.Tests/Coflo.Core.Snowflake.Tests.csproj @@ -0,0 +1,32 @@ + + + + net7.0 + enable + enable + + false + true + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs b/tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs new file mode 100644 index 0000000..b9e55a0 --- /dev/null +++ b/tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs @@ -0,0 +1,38 @@ +using Coflo.Core.Snowflake.Generators; +using FluentAssertions.Extensions; +using Microsoft.Extensions.Configuration; +using Moq; +using NodaTime; +using NodaTime.Testing; + +namespace Coflo.Core.Snowflake.Tests; + +public class IdGeneratorTests +{ + private readonly IConfiguration _mockConfiguration; + private readonly IdGenerator _idGenerator; + private FakeClock _fakeClock; + + public IdGeneratorTests() + { + _fakeClock = new FakeClock(Instant.FromDateTimeUtc(DateTime.Parse("2021-04-01 00:00:00").AsUtc())); + + var inMemorySettings = new Dictionary { + {"MachineId", "1"}, + }; + + _mockConfiguration = new ConfigurationBuilder() + .AddInMemoryCollection(inMemorySettings) + .Build(); + + _idGenerator = new IdGenerator(_mockConfiguration, _fakeClock); + } + + [Fact] + public async Task Assert_NextId_Returns_Correct_Id() + { + var result = await _idGenerator.NextId(); + + Assert.Equal(1, result); + } +} \ No newline at end of file diff --git a/tests/core/Coflo.Core.Snowflake.Tests/Usings.cs b/tests/core/Coflo.Core.Snowflake.Tests/Usings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/tests/core/Coflo.Core.Snowflake.Tests/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file From cb176e2fe8df0709f3a0c0e7d5c6ed6eb0c8eed9 Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 15 Apr 2023 02:43:55 +0100 Subject: [PATCH 03/10] id generator finished --- .../Generators/IIdGenerator.cs | 4 +- .../Generators/IdGenerator.cs | 108 +++++++++--------- .../Models/SnowflakeId.cs | 22 ++++ .../IdGeneratorTests.cs | 69 +++++++++-- 4 files changed, 139 insertions(+), 64 deletions(-) create mode 100644 source/hosting/Coflo.Core.Snowflake/Models/SnowflakeId.cs diff --git a/source/hosting/Coflo.Core.Snowflake/Generators/IIdGenerator.cs b/source/hosting/Coflo.Core.Snowflake/Generators/IIdGenerator.cs index 130b277..2bcdf17 100644 --- a/source/hosting/Coflo.Core.Snowflake/Generators/IIdGenerator.cs +++ b/source/hosting/Coflo.Core.Snowflake/Generators/IIdGenerator.cs @@ -1,4 +1,6 @@ -namespace Coflo.Core.Snowflake.Generators; +using Coflo.Core.Snowflake.Models; + +namespace Coflo.Core.Snowflake.Generators; public interface IIdGenerator { diff --git a/source/hosting/Coflo.Core.Snowflake/Generators/IdGenerator.cs b/source/hosting/Coflo.Core.Snowflake/Generators/IdGenerator.cs index a1a5594..ab89416 100644 --- a/source/hosting/Coflo.Core.Snowflake/Generators/IdGenerator.cs +++ b/source/hosting/Coflo.Core.Snowflake/Generators/IdGenerator.cs @@ -1,84 +1,90 @@ -using Microsoft.Extensions.Configuration; +using System.Data; +using Coflo.Core.Snowflake.Models; +using Microsoft.Extensions.Configuration; using NodaTime; namespace Coflo.Core.Snowflake.Generators; public class IdGenerator : IIdGenerator { - private const int _nodeIdBits = 10; - private const int _sequenceBits = 12; - internal const int _epochBits = 41; - internal const int _unusedBits = 1; - - private readonly int _maxMachineId = (int)(Math.Pow(2, _nodeIdBits) - 1); - private readonly int _maxSequence = (int)(Math.Pow(2, _sequenceBits) - 1); - - private const long _epoch = 1675209600L; - - private readonly int _machineId; private readonly IClock _clock; - - private long _lastTimeStamp = -1L; + private readonly long _machineId; + + private const long TimestampBits = 41; + private const long SequenceBits = 8; + private const long MachineIdBits = 63 - TimestampBits - SequenceBits; + private const long Epoch = 1_638_400_000_000L; + private long _lastTimestamp = -1L; private long _sequence = 0L; - + private readonly Mutex _mutex; - public IdGenerator(IConfiguration configuration, IClock clock) + public IdGenerator(IClock clock, IConfiguration configuration) { - var machineId = configuration.GetValue("MachineId"); + _clock = clock; + _machineId = configuration.GetValue("MachineId"); - if(machineId < 0 || machineId > _maxMachineId) - { - throw new ArgumentException($"Node ID must be between 0 and {_maxMachineId}"); - } - _mutex = new Mutex(); - _machineId = machineId; - _clock = clock; } - - internal long GetTimestamp() + + public Task NextId() { - var now = _clock.GetCurrentInstant(); - var epoch = Instant.FromUnixTimeSeconds(_epoch); - var duration = now.ToUnixTimeMilliseconds()- epoch.ToUnixTimeMilliseconds(); + var timestamp = GetTimestamp(); - return duration; - } - - public async Task NextId() - { _mutex.WaitOne(); - var currentTimeStamp = GetTimestamp(); - - if (currentTimeStamp < _lastTimeStamp && _sequence >= 1) throw new SystemException("System Clock Invalid"); - - if (currentTimeStamp == _lastTimeStamp) + if (timestamp == _lastTimestamp) { - _sequence = _sequence++ & _maxSequence; - + _sequence = (_sequence + 1) & ((1 << (int) SequenceBits) - 1); + if (_sequence == 0) { - await Task.Delay(1); - - currentTimeStamp = GetTimestamp(); + timestamp = GetNextTimestamp(); } } else { _sequence = 0; } - - _lastTimeStamp = currentTimeStamp; - - var id = - (currentTimeStamp << _epochBits) | - (uint)(_machineId << _nodeIdBits) | (_sequence << _sequenceBits) - | 1 << _unusedBits; + + _lastTimestamp = timestamp; _mutex.ReleaseMutex(); + return Task.FromResult(EncodeId(_clock.GetCurrentInstant(), _machineId, _sequence)); + } + + private long GetTimestamp() + { + return _clock.GetCurrentInstant().ToUnixTimeMilliseconds() - Epoch; + } + + private long GetNextTimestamp() + { + var timestamp = GetTimestamp(); + + while (timestamp <= _lastTimestamp) + { + timestamp = GetTimestamp(); + } + + return timestamp; + } + + internal static long EncodeId(Instant timestamp, long machineId, long sequence) + { + var timestampDelta = timestamp.ToUnixTimeMilliseconds() - Epoch; + var id = (timestampDelta << (int) (SequenceBits + MachineIdBits)) | (machineId << (int) SequenceBits) | sequence; + return id; } + + public static SnowflakeId DecodeId(long id) + { + var sequence = id & ((1 << (int) SequenceBits) - 1); + var machineId = (id >> (int) SequenceBits) & ((1 << (int) MachineIdBits) - 1); + var timestamp = (id >> (int) (SequenceBits + MachineIdBits)) + Epoch; + + return SnowflakeId.Create(id, Instant.FromUnixTimeMilliseconds(timestamp), machineId, sequence); + } } \ No newline at end of file diff --git a/source/hosting/Coflo.Core.Snowflake/Models/SnowflakeId.cs b/source/hosting/Coflo.Core.Snowflake/Models/SnowflakeId.cs new file mode 100644 index 0000000..1c5cf53 --- /dev/null +++ b/source/hosting/Coflo.Core.Snowflake/Models/SnowflakeId.cs @@ -0,0 +1,22 @@ +using NodaTime; + +namespace Coflo.Core.Snowflake.Models; + +public class SnowflakeId +{ + public long Id { get; private set; } + public Instant Timestamp { get; private set; } + public long MachineId { get; private set; } + public long Sequence { get; private set; } + + internal static SnowflakeId Create(long id, Instant timestamp, long machineId, long sequence) + { + return new SnowflakeId + { + Id = id, + Timestamp = timestamp, + MachineId = machineId, + Sequence = sequence + }; + } +} \ No newline at end of file diff --git a/tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs b/tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs index b9e55a0..6fcb8de 100644 --- a/tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs +++ b/tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs @@ -1,38 +1,83 @@ +using System.Diagnostics; using Coflo.Core.Snowflake.Generators; +using FluentAssertions; using FluentAssertions.Extensions; using Microsoft.Extensions.Configuration; using Moq; using NodaTime; using NodaTime.Testing; +using Xunit.Abstractions; namespace Coflo.Core.Snowflake.Tests; public class IdGeneratorTests { - private readonly IConfiguration _mockConfiguration; + private readonly ITestOutputHelper _testOutputHelper; private readonly IdGenerator _idGenerator; - private FakeClock _fakeClock; + private readonly FakeClock _fakeClock; - public IdGeneratorTests() + public IdGeneratorTests(ITestOutputHelper testOutputHelper) { - _fakeClock = new FakeClock(Instant.FromDateTimeUtc(DateTime.Parse("2021-04-01 00:00:00").AsUtc())); + _testOutputHelper = testOutputHelper; + _fakeClock = new FakeClock(Instant.FromDateTimeUtc(DateTime.Parse("14/04/2023 00:00:00").AsUtc())); - var inMemorySettings = new Dictionary { - {"MachineId", "1"}, + var inMemorySettings = new Dictionary + { + { "MachineId", "1" }, }; - _mockConfiguration = new ConfigurationBuilder() + var mockConfiguration = new ConfigurationBuilder() .AddInMemoryCollection(inMemorySettings) .Build(); - - _idGenerator = new IdGenerator(_mockConfiguration, _fakeClock); + + _idGenerator = new IdGenerator(_fakeClock, mockConfiguration); } - + [Fact] public async Task Assert_NextId_Returns_Correct_Id() { var result = await _idGenerator.NextId(); - - Assert.Equal(1, result); + + var decodedId = IdGenerator.DecodeId(result); + + decodedId.MachineId.Should().Be(1); + decodedId.Sequence.Should().Be(0); + decodedId.Timestamp.Should().Be(_fakeClock.GetCurrentInstant()); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [InlineData(4)] + [InlineData(5)] + public async Task Assert_NextId_Returns_Correct_Id_When_Sequence_Overflows(int sequence) + { + var expectedInstant = + Instant.FromDateTimeUtc(DateTime.Parse("14/04/2023 00:00:00").AsUtc() + TimeSpan.FromSeconds(sequence)); + _fakeClock.Reset(expectedInstant); + var result = await _idGenerator.NextId(); + + var decodedId = IdGenerator.DecodeId(result); + _testOutputHelper.WriteLine(decodedId.Id.ToString()); + decodedId.MachineId.Should().Be(1); + decodedId.Sequence.Should().Be(0); + decodedId.Timestamp.Should().Be(expectedInstant); + } + + [Fact] + public async Task Assert_NextId_Returns_Correct_Id_When_Sequence_Overflows_Then_Resets() + { + var expectedInstant = + Instant.FromDateTimeUtc(DateTime.Parse("14/04/2023 00:00:00").AsUtc() + TimeSpan.FromSeconds(5)); + _fakeClock.Reset(expectedInstant); + var result = await _idGenerator.NextId(); + + var decodedId = IdGenerator.DecodeId(result); + _testOutputHelper.WriteLine(decodedId.Id.ToString()); + decodedId.MachineId.Should().Be(1); + decodedId.Sequence.Should().Be(0); + decodedId.Timestamp.Should().Be(expectedInstant); } } \ No newline at end of file From 9f034cd83e37882649519a2e64a5c43c6b78e0cd Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 15 Apr 2023 02:56:20 +0100 Subject: [PATCH 04/10] added culture info --- .../core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs b/tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs index 6fcb8de..b7bd78e 100644 --- a/tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs +++ b/tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using System.Globalization; using Coflo.Core.Snowflake.Generators; using FluentAssertions; using FluentAssertions.Extensions; @@ -15,11 +16,13 @@ public class IdGeneratorTests private readonly ITestOutputHelper _testOutputHelper; private readonly IdGenerator _idGenerator; private readonly FakeClock _fakeClock; + private readonly DateTimeFormatInfo _dateTimeFormat; public IdGeneratorTests(ITestOutputHelper testOutputHelper) { _testOutputHelper = testOutputHelper; - _fakeClock = new FakeClock(Instant.FromDateTimeUtc(DateTime.Parse("14/04/2023 00:00:00").AsUtc())); + _dateTimeFormat = new CultureInfo("en-GB").DateTimeFormat; + _fakeClock = new FakeClock(Instant.FromDateTimeUtc(DateTime.Parse("14/04/2023 00:00:00", _dateTimeFormat).AsUtc())); var inMemorySettings = new Dictionary { @@ -55,7 +58,7 @@ public async Task Assert_NextId_Returns_Correct_Id() public async Task Assert_NextId_Returns_Correct_Id_When_Sequence_Overflows(int sequence) { var expectedInstant = - Instant.FromDateTimeUtc(DateTime.Parse("14/04/2023 00:00:00").AsUtc() + TimeSpan.FromSeconds(sequence)); + Instant.FromDateTimeUtc(DateTime.Parse("14/04/2023 00:00:00", _dateTimeFormat).AsUtc() + TimeSpan.FromSeconds(sequence)); _fakeClock.Reset(expectedInstant); var result = await _idGenerator.NextId(); @@ -70,7 +73,7 @@ public async Task Assert_NextId_Returns_Correct_Id_When_Sequence_Overflows(int s public async Task Assert_NextId_Returns_Correct_Id_When_Sequence_Overflows_Then_Resets() { var expectedInstant = - Instant.FromDateTimeUtc(DateTime.Parse("14/04/2023 00:00:00").AsUtc() + TimeSpan.FromSeconds(5)); + Instant.FromDateTimeUtc(DateTime.Parse("14/04/2023 00:00:00", _dateTimeFormat).AsUtc() + TimeSpan.FromSeconds(5)); _fakeClock.Reset(expectedInstant); var result = await _idGenerator.NextId(); From a1e82310fcf8a5929d66b11308b09d4db6223024 Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 15 Apr 2023 03:31:00 +0100 Subject: [PATCH 05/10] basic workflow control --- .../Coflo.Abstractions/Coflo.Abstractions.csproj | 5 ----- .../Coflo.Abstractions/Enums/VariableType.cs | 11 +++++++++++ .../Models/Activity/ActivityDefinition.cs | 13 +++++++++++++ .../Models/Activity/ActivityExecutionContext.cs | 15 +++++++++++++++ .../Models/Activity/ActivityResult.cs | 15 +++++++++++++++ .../Models/Step/WorkflowStep.cs | 12 ++++++++++++ .../Models/Step/WorkflowStepTrigger.cs | 6 ++++++ .../Models/Variable/VariableDefinition.cs | 16 ++++++++++++++++ .../Models/Variable/VariableValue.cs | 6 ++++++ .../Models/Workflow/WorkflowDefinition.cs | 2 +- .../Models/Workflow/WorkflowDefinitionVersion.cs | 4 +++- .../Models/Workflow/WorkflowInstance.cs | 6 ++++++ source/core/Coflo.SDK/Activities/ActivityBase.cs | 8 ++++++++ source/core/Coflo.SDK/Class1.cs | 5 ----- source/core/Coflo.SDK/Coflo.SDK.csproj | 5 +++++ 15 files changed, 117 insertions(+), 12 deletions(-) create mode 100644 source/core/Coflo.Abstractions/Enums/VariableType.cs create mode 100644 source/core/Coflo.Abstractions/Models/Activity/ActivityDefinition.cs create mode 100644 source/core/Coflo.Abstractions/Models/Activity/ActivityExecutionContext.cs create mode 100644 source/core/Coflo.Abstractions/Models/Activity/ActivityResult.cs create mode 100644 source/core/Coflo.Abstractions/Models/Step/WorkflowStep.cs create mode 100644 source/core/Coflo.Abstractions/Models/Step/WorkflowStepTrigger.cs create mode 100644 source/core/Coflo.Abstractions/Models/Variable/VariableDefinition.cs create mode 100644 source/core/Coflo.Abstractions/Models/Variable/VariableValue.cs create mode 100644 source/core/Coflo.Abstractions/Models/Workflow/WorkflowInstance.cs create mode 100644 source/core/Coflo.SDK/Activities/ActivityBase.cs delete mode 100644 source/core/Coflo.SDK/Class1.cs diff --git a/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj b/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj index 4b43e18..a4318a6 100644 --- a/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj +++ b/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj @@ -11,9 +11,4 @@ - - - - - diff --git a/source/core/Coflo.Abstractions/Enums/VariableType.cs b/source/core/Coflo.Abstractions/Enums/VariableType.cs new file mode 100644 index 0000000..73cbe2d --- /dev/null +++ b/source/core/Coflo.Abstractions/Enums/VariableType.cs @@ -0,0 +1,11 @@ +namespace Coflo.Abstractions.Enums; + +public enum VariableType +{ + DateTime, + Decimal, + Integer, + String, + Boolean, + JObject, +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Activity/ActivityDefinition.cs b/source/core/Coflo.Abstractions/Models/Activity/ActivityDefinition.cs new file mode 100644 index 0000000..5fb3b21 --- /dev/null +++ b/source/core/Coflo.Abstractions/Models/Activity/ActivityDefinition.cs @@ -0,0 +1,13 @@ +namespace Coflo.Abstractions.Models.Activity; + +public class ActivityDefinition +{ + public virtual string DisplayName { get; set; } = default!; + public virtual string? Description { get; set; } + + public virtual List Outcomes { get; set; } = new() + { + "Success", + "Failure" + }; +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Activity/ActivityExecutionContext.cs b/source/core/Coflo.Abstractions/Models/Activity/ActivityExecutionContext.cs new file mode 100644 index 0000000..18f91bb --- /dev/null +++ b/source/core/Coflo.Abstractions/Models/Activity/ActivityExecutionContext.cs @@ -0,0 +1,15 @@ +using Coflo.Abstractions.Models.Variable; + +namespace Coflo.Abstractions.Models.Activity; + +public class ActivityExecutionContext +{ + public List Variables { get; set; } = new(); + public List PersistDefinitions { get; set; } = new(); + + public string Name { get; set; } = default!; + public string ActivityName { get; set; } = default!; + public string CorrelationId { get; set; } = default!; + public long WorkflowInstanceId { get; set; } + public long WorkflowDefinitionId { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Activity/ActivityResult.cs b/source/core/Coflo.Abstractions/Models/Activity/ActivityResult.cs new file mode 100644 index 0000000..1407027 --- /dev/null +++ b/source/core/Coflo.Abstractions/Models/Activity/ActivityResult.cs @@ -0,0 +1,15 @@ +namespace Coflo.Abstractions.Models.Activity; + +public class ActivityResult +{ + public List ActivityLog { get; set; } = new(); + public bool IsSuccess { get; set; } + public string? Outcome { get; set; } + + public ActivityResult(bool isSuccess = false, List activityLog = null, string? outcome = null) + { + IsSuccess = isSuccess; + ActivityLog = activityLog ?? new List(); + Outcome = outcome; + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Step/WorkflowStep.cs b/source/core/Coflo.Abstractions/Models/Step/WorkflowStep.cs new file mode 100644 index 0000000..5734926 --- /dev/null +++ b/source/core/Coflo.Abstractions/Models/Step/WorkflowStep.cs @@ -0,0 +1,12 @@ +namespace Coflo.Abstractions.Models.Step; + +public class WorkflowStep +{ + public string Name { get; set; } = default!; + public string DisplayName { get; set; } = default!; + + public string ActivityName { get; set; } = default!; + + public string? Condition { get; set; } + public ICollection NextSteps { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Step/WorkflowStepTrigger.cs b/source/core/Coflo.Abstractions/Models/Step/WorkflowStepTrigger.cs new file mode 100644 index 0000000..59563e2 --- /dev/null +++ b/source/core/Coflo.Abstractions/Models/Step/WorkflowStepTrigger.cs @@ -0,0 +1,6 @@ +namespace Coflo.Abstractions.Models.Step; + +public class WorkflowStepTrigger +{ + public List Steps { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Variable/VariableDefinition.cs b/source/core/Coflo.Abstractions/Models/Variable/VariableDefinition.cs new file mode 100644 index 0000000..9f96913 --- /dev/null +++ b/source/core/Coflo.Abstractions/Models/Variable/VariableDefinition.cs @@ -0,0 +1,16 @@ +using Coflo.Abstractions.Enums; + +namespace Coflo.Abstractions.Models.Variable; + +public class VariableDefinition +{ + public string Name { get; set; } = default!; + public string DisplayName { get; set; } = default!; + public VariableType Type { get; set; } = default!; + public bool IsArray { get; set; } + public object? DefaultValue { get; set; } + public bool IsRequired { get; set; } + public bool IsSecret { get; set; } + public string? Description { get; set; } + public string? Validation { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Variable/VariableValue.cs b/source/core/Coflo.Abstractions/Models/Variable/VariableValue.cs new file mode 100644 index 0000000..b8281d3 --- /dev/null +++ b/source/core/Coflo.Abstractions/Models/Variable/VariableValue.cs @@ -0,0 +1,6 @@ +namespace Coflo.Abstractions.Models.Variable; + +public class VariableValue : VariableDefinition +{ + public object? Value { get; set; } = default!; +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinition.cs b/source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinition.cs index afbbd94..4dbb88c 100644 --- a/source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinition.cs +++ b/source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinition.cs @@ -5,7 +5,7 @@ namespace Coflo.Abstractions.Models.Workflow; public class WorkflowDefinition : ITenantScope { - public Guid Id { get; set; } + public long Id { get; set; } public string Name { get; set; } public Guid? TenantId { get; set; } diff --git a/source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinitionVersion.cs b/source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinitionVersion.cs index 00c89d3..4836fe0 100644 --- a/source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinitionVersion.cs +++ b/source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinitionVersion.cs @@ -5,10 +5,12 @@ namespace Coflo.Abstractions.Models.Workflow; public class WorkflowDefinitionVersion : ITenantScope { - public Guid WorkflowId { get; set; } + public long WorkflowId { get; set; } public long VersionId { get; set; } public Guid? TenantId { get; set; } + + public Instant CreatedAt { get; set; } public Instant UpdatedAt { get; set; } } \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Workflow/WorkflowInstance.cs b/source/core/Coflo.Abstractions/Models/Workflow/WorkflowInstance.cs new file mode 100644 index 0000000..d1ea896 --- /dev/null +++ b/source/core/Coflo.Abstractions/Models/Workflow/WorkflowInstance.cs @@ -0,0 +1,6 @@ +namespace Coflo.Abstractions.Models.Workflow; + +public class WorkflowInstance +{ + +} \ No newline at end of file diff --git a/source/core/Coflo.SDK/Activities/ActivityBase.cs b/source/core/Coflo.SDK/Activities/ActivityBase.cs new file mode 100644 index 0000000..c7c9abb --- /dev/null +++ b/source/core/Coflo.SDK/Activities/ActivityBase.cs @@ -0,0 +1,8 @@ +using Coflo.Abstractions.Models.Activity; + +namespace Coflo.SDK.Activities; + +public abstract class ActivityBase : ActivityDefinition +{ + public abstract ActivityResult ExecuteActivityAsync(ActivityExecutionContext context); +} \ No newline at end of file diff --git a/source/core/Coflo.SDK/Class1.cs b/source/core/Coflo.SDK/Class1.cs deleted file mode 100644 index 150f87b..0000000 --- a/source/core/Coflo.SDK/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Coflo.SDK; - -public class Class1 -{ -} \ No newline at end of file diff --git a/source/core/Coflo.SDK/Coflo.SDK.csproj b/source/core/Coflo.SDK/Coflo.SDK.csproj index 6836c68..c9d3cfe 100644 --- a/source/core/Coflo.SDK/Coflo.SDK.csproj +++ b/source/core/Coflo.SDK/Coflo.SDK.csproj @@ -6,4 +6,9 @@ enable + + + + + From 8bb65221811c6c2b5905b6d8ca0dfff996c52414 Mon Sep 17 00:00:00 2001 From: Thomas Wright Date: Sun, 16 Apr 2023 12:16:37 +0100 Subject: [PATCH 06/10] Abstraction Assembly --- .../Coflo.Abstractions.csproj | 5 + .../Coflo.Abstractions.csproj.DotSettings | 2 + .../Contracts/Tenant/IApplicationTenant.cs | 7 - .../Contracts/Tenant/ITenantScope.cs | 6 - .../Coflo.Abstractions/Enums/VariableType.cs | 11 - .../Coflo.Abstractions/Events/Models/Event.cs | 18 ++ .../Events/Models/EventSubscription.cs | 26 +++ .../Events/Models/ScheduledCommand.cs | 13 ++ .../Execution/Models/ExecutionError.cs | 12 ++ .../Execution/Models/ExecutionResult.cs | 104 +++++++++ .../Expr/Contracts/IStepOutcome.cs | 14 ++ .../Expr/Models/ExpressionOutcome.cs | 32 +++ .../Contracts/IDistributedLockProvider.cs | 22 ++ .../Models/Activity/ActivityDefinition.cs | 13 -- .../Activity/ActivityExecutionContext.cs | 15 -- .../Models/Activity/ActivityResult.cs | 15 -- .../Models/Step/WorkflowStep.cs | 12 -- .../Models/Step/WorkflowStepTrigger.cs | 6 - .../Models/Tenant/ApplicationTenant.cs | 9 - .../Models/Variable/VariableDefinition.cs | 16 -- .../Models/Variable/VariableValue.cs | 6 - .../Models/Workflow/WorkflowDefinition.cs | 17 -- .../Workflow/WorkflowDefinitionVersion.cs | 16 -- .../Models/Workflow/WorkflowInstance.cs | 6 - .../Persistence/Contracts/IEventRepository.cs | 19 ++ .../Contracts/IPersistenceProvider.cs | 11 + .../Contracts/IScheduledCommandRepository.cs | 12 ++ .../Contracts/ISubscriptionRepository.cs | 21 ++ .../Contracts/IWorkflowRepository.cs | 24 +++ .../CurruptPersistenceDataException.cs | 5 + .../Exceptions/NotFoundException.cs | 15 ++ .../Models/ControlPersistenceData.cs | 6 + .../Models/IteratorPersistenceData.cs | 6 + .../Models/SchedulePersistenceData.cs | 6 + .../Contracts/IContainerStepBuilder.cs | 16 ++ .../Contracts/IParallelStepBuilder.cs | 12 ++ .../Implementation/ActionStepBody.cs | 16 ++ .../Primitives/Implementation/Activity.cs | 53 +++++ .../Implementation/ContainerStepBody.cs | 8 + .../Implementation/Control/Decide.cs | 15 ++ .../Implementation/Control/Delay.cs | 20 ++ .../Implementation/Control/EndStep.cs | 15 ++ .../Primitives/Implementation/Control/If.cs | 36 ++++ .../Implementation/Control/OutcomeSwitch.cs | 41 ++++ .../Implementation/Control/WaitFor.cs | 34 +++ .../Primitives/Implementation/Control/When.cs | 47 +++++ .../Implementation/InlineStepBody.cs | 21 ++ .../Implementation/Iteration/Foreach.cs | 53 +++++ .../Implementation/Iteration/Sequence.cs | 23 ++ .../Implementation/Iteration/While.cs | 31 +++ .../Implementation/Recurring/Recur.cs | 26 +++ .../Implementation/Recurring/Schedule.cs | 30 +++ .../Implementation/SagaContainer.cs | 17 ++ .../Implementation/SubWorkflowStepBody.cs | 14 ++ .../Implementation/WorkflowStepInline.cs | 15 ++ .../Primitives/Models/ActivityResult.cs | 9 + .../Steps/Attributes/StepAttribute.cs | 7 + .../Steps/Contracts/IActivityController.cs | 11 + .../Steps/Contracts/IStepBody.cs | 9 + .../Steps/Contracts/IStepBuilder.cs | 153 ++++++++++++++ .../Steps/Contracts/IStepExecutionContext.cs | 18 ++ .../Steps/Contracts/IStepParameter.cs | 7 + .../Exceptions/ActivityFailedException.cs | 8 + .../Steps/Models/ActionStepBody.cs | 16 ++ .../Steps/Models/ActivityToken.cs | 36 ++++ .../Steps/Models/InlineStepBody.cs | 20 ++ .../Steps/Models/PendingActivity.cs | 9 + .../Steps/Models/StepBody.cs | 42 ++++ .../Steps/Models/StepBodyAsync.cs | 9 + .../Steps/Models/WorkflowStepDelegate.cs | 8 + .../Workflow/Contracts/IWorkflow.cs | 14 ++ .../Workflow/Contracts/IWorkflowBuilder.cs | 37 ++++ .../Workflow/Contracts/IWorkflowController.cs | 38 ++++ .../Workflow/Contracts/IWorkflowModifier.cs | 197 ++++++++++++++++++ .../Enums/ExecutionPipelineDirective.cs | 8 + .../Workflow/Enums/PointerStatus.cs | 15 ++ .../Workflow/Enums/WorkflowErrorHandling.cs | 9 + .../Workflow/Enums/WorkflowStatus.cs | 9 + .../WorkflowDefinitionLoadException.cs | 9 + .../Exceptions/WorkflowLockedException.cs | 9 + .../WorkflowNotRegisteredException.cs | 6 + .../Workflow/Models/ExecutionPointer.cs | 52 +++++ .../Models/ExecutionPointerCollection.cs | 105 ++++++++++ .../Workflow/Models/WorkflowDefinition.cs | 16 ++ .../Workflow/Models/WorkflowExecutorResult.cs | 10 + .../Workflow/Models/WorkflowInstance.cs | 35 ++++ .../Workflow/Models/WorkflowResult.cs | 10 + .../Workflow/Models/WorkflowStep.cs | 94 +++++++++ .../Workflow/Models/WorkflowStepCollection.cs | 76 +++++++ 89 files changed, 2027 insertions(+), 155 deletions(-) create mode 100644 source/core/Coflo.Abstractions/Coflo.Abstractions.csproj.DotSettings delete mode 100644 source/core/Coflo.Abstractions/Contracts/Tenant/IApplicationTenant.cs delete mode 100644 source/core/Coflo.Abstractions/Contracts/Tenant/ITenantScope.cs delete mode 100644 source/core/Coflo.Abstractions/Enums/VariableType.cs create mode 100644 source/core/Coflo.Abstractions/Events/Models/Event.cs create mode 100644 source/core/Coflo.Abstractions/Events/Models/EventSubscription.cs create mode 100644 source/core/Coflo.Abstractions/Events/Models/ScheduledCommand.cs create mode 100644 source/core/Coflo.Abstractions/Execution/Models/ExecutionError.cs create mode 100644 source/core/Coflo.Abstractions/Execution/Models/ExecutionResult.cs create mode 100644 source/core/Coflo.Abstractions/Expr/Contracts/IStepOutcome.cs create mode 100644 source/core/Coflo.Abstractions/Expr/Models/ExpressionOutcome.cs create mode 100644 source/core/Coflo.Abstractions/Locking/Contracts/IDistributedLockProvider.cs delete mode 100644 source/core/Coflo.Abstractions/Models/Activity/ActivityDefinition.cs delete mode 100644 source/core/Coflo.Abstractions/Models/Activity/ActivityExecutionContext.cs delete mode 100644 source/core/Coflo.Abstractions/Models/Activity/ActivityResult.cs delete mode 100644 source/core/Coflo.Abstractions/Models/Step/WorkflowStep.cs delete mode 100644 source/core/Coflo.Abstractions/Models/Step/WorkflowStepTrigger.cs delete mode 100644 source/core/Coflo.Abstractions/Models/Tenant/ApplicationTenant.cs delete mode 100644 source/core/Coflo.Abstractions/Models/Variable/VariableDefinition.cs delete mode 100644 source/core/Coflo.Abstractions/Models/Variable/VariableValue.cs delete mode 100644 source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinition.cs delete mode 100644 source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinitionVersion.cs delete mode 100644 source/core/Coflo.Abstractions/Models/Workflow/WorkflowInstance.cs create mode 100644 source/core/Coflo.Abstractions/Persistence/Contracts/IEventRepository.cs create mode 100644 source/core/Coflo.Abstractions/Persistence/Contracts/IPersistenceProvider.cs create mode 100644 source/core/Coflo.Abstractions/Persistence/Contracts/IScheduledCommandRepository.cs create mode 100644 source/core/Coflo.Abstractions/Persistence/Contracts/ISubscriptionRepository.cs create mode 100644 source/core/Coflo.Abstractions/Persistence/Contracts/IWorkflowRepository.cs create mode 100644 source/core/Coflo.Abstractions/Persistence/Exceptions/CurruptPersistenceDataException.cs create mode 100644 source/core/Coflo.Abstractions/Persistence/Exceptions/NotFoundException.cs create mode 100644 source/core/Coflo.Abstractions/Persistence/Models/ControlPersistenceData.cs create mode 100644 source/core/Coflo.Abstractions/Persistence/Models/IteratorPersistenceData.cs create mode 100644 source/core/Coflo.Abstractions/Persistence/Models/SchedulePersistenceData.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Contracts/IContainerStepBuilder.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Contracts/IParallelStepBuilder.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/ActionStepBody.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Activity.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/ContainerStepBody.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Control/Decide.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Control/Delay.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Control/EndStep.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Control/If.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Control/OutcomeSwitch.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Control/WaitFor.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Control/When.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/InlineStepBody.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/Foreach.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/Sequence.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/While.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Recurring/Recur.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Recurring/Schedule.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/SagaContainer.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/SubWorkflowStepBody.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/WorkflowStepInline.cs create mode 100644 source/core/Coflo.Abstractions/Primitives/Models/ActivityResult.cs create mode 100644 source/core/Coflo.Abstractions/Steps/Attributes/StepAttribute.cs create mode 100644 source/core/Coflo.Abstractions/Steps/Contracts/IActivityController.cs create mode 100644 source/core/Coflo.Abstractions/Steps/Contracts/IStepBody.cs create mode 100644 source/core/Coflo.Abstractions/Steps/Contracts/IStepBuilder.cs create mode 100644 source/core/Coflo.Abstractions/Steps/Contracts/IStepExecutionContext.cs create mode 100644 source/core/Coflo.Abstractions/Steps/Contracts/IStepParameter.cs create mode 100644 source/core/Coflo.Abstractions/Steps/Exceptions/ActivityFailedException.cs create mode 100644 source/core/Coflo.Abstractions/Steps/Models/ActionStepBody.cs create mode 100644 source/core/Coflo.Abstractions/Steps/Models/ActivityToken.cs create mode 100644 source/core/Coflo.Abstractions/Steps/Models/InlineStepBody.cs create mode 100644 source/core/Coflo.Abstractions/Steps/Models/PendingActivity.cs create mode 100644 source/core/Coflo.Abstractions/Steps/Models/StepBody.cs create mode 100644 source/core/Coflo.Abstractions/Steps/Models/StepBodyAsync.cs create mode 100644 source/core/Coflo.Abstractions/Steps/Models/WorkflowStepDelegate.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflow.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowBuilder.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowController.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowModifier.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Enums/ExecutionPipelineDirective.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Enums/PointerStatus.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Enums/WorkflowErrorHandling.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Enums/WorkflowStatus.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowDefinitionLoadException.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowLockedException.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowNotRegisteredException.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Models/ExecutionPointer.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Models/ExecutionPointerCollection.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Models/WorkflowDefinition.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Models/WorkflowExecutorResult.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Models/WorkflowInstance.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Models/WorkflowResult.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Models/WorkflowStep.cs create mode 100644 source/core/Coflo.Abstractions/Workflow/Models/WorkflowStepCollection.cs diff --git a/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj b/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj index a4318a6..77bf108 100644 --- a/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj +++ b/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj @@ -7,8 +7,13 @@ + + + + + diff --git a/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj.DotSettings b/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj.DotSettings new file mode 100644 index 0000000..9163818 --- /dev/null +++ b/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Contracts/Tenant/IApplicationTenant.cs b/source/core/Coflo.Abstractions/Contracts/Tenant/IApplicationTenant.cs deleted file mode 100644 index efc58d4..0000000 --- a/source/core/Coflo.Abstractions/Contracts/Tenant/IApplicationTenant.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Coflo.Abstractions.Contracts.Tenant; - -public interface IApplicationTenant -{ - public Guid TenantId { get; set; } - public string Name { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Contracts/Tenant/ITenantScope.cs b/source/core/Coflo.Abstractions/Contracts/Tenant/ITenantScope.cs deleted file mode 100644 index 7928df0..0000000 --- a/source/core/Coflo.Abstractions/Contracts/Tenant/ITenantScope.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Coflo.Abstractions.Contracts.Tenant; - -public interface ITenantScope -{ - public Guid? TenantId { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Enums/VariableType.cs b/source/core/Coflo.Abstractions/Enums/VariableType.cs deleted file mode 100644 index 73cbe2d..0000000 --- a/source/core/Coflo.Abstractions/Enums/VariableType.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Coflo.Abstractions.Enums; - -public enum VariableType -{ - DateTime, - Decimal, - Integer, - String, - Boolean, - JObject, -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Events/Models/Event.cs b/source/core/Coflo.Abstractions/Events/Models/Event.cs new file mode 100644 index 0000000..0a7834e --- /dev/null +++ b/source/core/Coflo.Abstractions/Events/Models/Event.cs @@ -0,0 +1,18 @@ +namespace Coflo.Abstractions.Events.Models; + +public class Event +{ + public long Id { get; set; } + + public string EventName { get; set; } + + public string EventKey { get; set; } + + public object EventData { get; set; } + + public DateTime EventTime { get; set; } + + public bool IsProcessed { get; set; } + + public const string EventTypeActivity = "WorkflowCore.Activity"; +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Events/Models/EventSubscription.cs b/source/core/Coflo.Abstractions/Events/Models/EventSubscription.cs new file mode 100644 index 0000000..816e048 --- /dev/null +++ b/source/core/Coflo.Abstractions/Events/Models/EventSubscription.cs @@ -0,0 +1,26 @@ +namespace Coflo.Abstractions.Events.Models; + +public class EventSubscription +{ + public long Id { get; set; } + + public long WorkflowId { get; set; } + + public int StepId { get; set; } + + public string ExecutionPointerId { get; set; } + + public string EventName { get; set; } + + public string EventKey { get; set; } + + public DateTime SubscribeAsOf { get; set; } + + public object SubscriptionData { get; set; } + + public string ExternalToken { get; set; } + + public string ExternalWorkerId { get; set; } + + public DateTime? ExternalTokenExpiry { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Events/Models/ScheduledCommand.cs b/source/core/Coflo.Abstractions/Events/Models/ScheduledCommand.cs new file mode 100644 index 0000000..f86ea8e --- /dev/null +++ b/source/core/Coflo.Abstractions/Events/Models/ScheduledCommand.cs @@ -0,0 +1,13 @@ +using Mediator; + +namespace Coflo.Abstractions.Events.Models; + +public class ScheduledCommand : ICommand +{ + public const string ProcessWorkflow = "ProcessWorkflow"; + public const string ProcessEvent = "ProcessEvent"; + + public string CommandName { get; set; } + public string Data { get; set; } + public long ExecuteTime { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Execution/Models/ExecutionError.cs b/source/core/Coflo.Abstractions/Execution/Models/ExecutionError.cs new file mode 100644 index 0000000..029e3ad --- /dev/null +++ b/source/core/Coflo.Abstractions/Execution/Models/ExecutionError.cs @@ -0,0 +1,12 @@ +namespace Coflo.Abstractions.Execution.Models; + +public class ExecutionError +{ + public DateTime ErrorTime { get; set; } + + public long WorkflowId { get; set; } + + public long ExecutionPointerId { get; set; } + + public string Message { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Execution/Models/ExecutionResult.cs b/source/core/Coflo.Abstractions/Execution/Models/ExecutionResult.cs new file mode 100644 index 0000000..0e20c32 --- /dev/null +++ b/source/core/Coflo.Abstractions/Execution/Models/ExecutionResult.cs @@ -0,0 +1,104 @@ +using Coflo.Abstractions.Events.Models; + +namespace Coflo.Abstractions.Execution.Models; + +public class ExecutionResult +{ + public bool Proceed { get; set; } + + public object OutcomeValue { get; set; } + + public TimeSpan? SleepFor { get; set; } + + public object PersistenceData { get; set; } + + public string EventName { get; set; } + + public string EventKey { get; set; } + + public DateTime EventAsOf { get; set; } + + public object SubscriptionData { get; set; } + + public List BranchValues { get; set; } = new(); + + public ExecutionResult() + { + } + + public ExecutionResult(object outcome) + { + Proceed = true; + OutcomeValue = outcome; + } + + public static ExecutionResult Outcome(object value) + { + return new ExecutionResult + { + Proceed = true, + OutcomeValue = value + }; + } + + public static ExecutionResult Next() + { + return new ExecutionResult + { + Proceed = true, + OutcomeValue = null + }; + } + + public static ExecutionResult Persist(object persistenceData) + { + return new ExecutionResult + { + Proceed = false, + PersistenceData = persistenceData + }; + } + + public static ExecutionResult Branch(List branches, object persistenceData) + { + return new ExecutionResult + { + Proceed = false, + PersistenceData = persistenceData, + BranchValues = branches + }; + } + + public static ExecutionResult Sleep(TimeSpan duration, object persistenceData) + { + return new ExecutionResult + { + Proceed = false, + SleepFor = duration, + PersistenceData = persistenceData + }; + } + + public static ExecutionResult WaitForEvent(string eventName, string eventKey, DateTime effectiveDate) + { + return new ExecutionResult + { + Proceed = false, + EventName = eventName, + EventKey = eventKey, + EventAsOf = effectiveDate.ToUniversalTime() + }; + } + + public static ExecutionResult WaitForActivity(string activityName, object subscriptionData, DateTime effectiveDate) + { + return new ExecutionResult + { + Proceed = false, + EventName = Event.EventTypeActivity, + EventKey = activityName, + SubscriptionData = subscriptionData, + EventAsOf = effectiveDate.ToUniversalTime() + }; + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Expr/Contracts/IStepOutcome.cs b/source/core/Coflo.Abstractions/Expr/Contracts/IStepOutcome.cs new file mode 100644 index 0000000..28adebb --- /dev/null +++ b/source/core/Coflo.Abstractions/Expr/Contracts/IStepOutcome.cs @@ -0,0 +1,14 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Steps.Models; + +namespace Coflo.Abstractions.Expr.Contracts; + +public interface IStepOutcome +{ + string ExternalNextStepId { get; set; } + string Label { get; set; } + int NextStep { get; set; } + + bool Matches(object data); + bool Matches(ExecutionResult executionResult, object data); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Expr/Models/ExpressionOutcome.cs b/source/core/Coflo.Abstractions/Expr/Models/ExpressionOutcome.cs new file mode 100644 index 0000000..3843b1b --- /dev/null +++ b/source/core/Coflo.Abstractions/Expr/Models/ExpressionOutcome.cs @@ -0,0 +1,32 @@ +using System.Linq.Expressions; +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Expr.Contracts; +using Coflo.Abstractions.Steps.Models; + +namespace Coflo.Abstractions.Expr.Models; + +public class ExpressionOutcome : IStepOutcome +{ + private readonly Func _func; + + public int NextStep { get; set; } + + public string Label { get; set; } + + public string ExternalNextStepId { get; set; } + + public ExpressionOutcome(Expression> expression) + { + _func = expression.Compile(); + } + + public bool Matches(ExecutionResult executionResult, object data) + { + return _func((TData)data, executionResult.OutcomeValue); + } + + public bool Matches(object data) + { + return _func((TData)data, null); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Locking/Contracts/IDistributedLockProvider.cs b/source/core/Coflo.Abstractions/Locking/Contracts/IDistributedLockProvider.cs new file mode 100644 index 0000000..83c696c --- /dev/null +++ b/source/core/Coflo.Abstractions/Locking/Contracts/IDistributedLockProvider.cs @@ -0,0 +1,22 @@ +namespace Coflo.Abstractions.Locking.Contracts; + +/// +/// The implemention of this interface will be responsible for +/// providing a (distributed) locking mechanism to manage in flight workflows +/// +public interface IDistributedLockProvider +{ + /// + /// Acquire a lock on the specified resource. + /// + /// Resource ID to lock. + /// + /// `true`, if the lock was acquired. + Task AcquireLock(string Id, CancellationToken cancellationToken); + + Task ReleaseLock(string Id); + + Task Start(); + + Task Stop(); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Activity/ActivityDefinition.cs b/source/core/Coflo.Abstractions/Models/Activity/ActivityDefinition.cs deleted file mode 100644 index 5fb3b21..0000000 --- a/source/core/Coflo.Abstractions/Models/Activity/ActivityDefinition.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Coflo.Abstractions.Models.Activity; - -public class ActivityDefinition -{ - public virtual string DisplayName { get; set; } = default!; - public virtual string? Description { get; set; } - - public virtual List Outcomes { get; set; } = new() - { - "Success", - "Failure" - }; -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Activity/ActivityExecutionContext.cs b/source/core/Coflo.Abstractions/Models/Activity/ActivityExecutionContext.cs deleted file mode 100644 index 18f91bb..0000000 --- a/source/core/Coflo.Abstractions/Models/Activity/ActivityExecutionContext.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Coflo.Abstractions.Models.Variable; - -namespace Coflo.Abstractions.Models.Activity; - -public class ActivityExecutionContext -{ - public List Variables { get; set; } = new(); - public List PersistDefinitions { get; set; } = new(); - - public string Name { get; set; } = default!; - public string ActivityName { get; set; } = default!; - public string CorrelationId { get; set; } = default!; - public long WorkflowInstanceId { get; set; } - public long WorkflowDefinitionId { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Activity/ActivityResult.cs b/source/core/Coflo.Abstractions/Models/Activity/ActivityResult.cs deleted file mode 100644 index 1407027..0000000 --- a/source/core/Coflo.Abstractions/Models/Activity/ActivityResult.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Coflo.Abstractions.Models.Activity; - -public class ActivityResult -{ - public List ActivityLog { get; set; } = new(); - public bool IsSuccess { get; set; } - public string? Outcome { get; set; } - - public ActivityResult(bool isSuccess = false, List activityLog = null, string? outcome = null) - { - IsSuccess = isSuccess; - ActivityLog = activityLog ?? new List(); - Outcome = outcome; - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Step/WorkflowStep.cs b/source/core/Coflo.Abstractions/Models/Step/WorkflowStep.cs deleted file mode 100644 index 5734926..0000000 --- a/source/core/Coflo.Abstractions/Models/Step/WorkflowStep.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Coflo.Abstractions.Models.Step; - -public class WorkflowStep -{ - public string Name { get; set; } = default!; - public string DisplayName { get; set; } = default!; - - public string ActivityName { get; set; } = default!; - - public string? Condition { get; set; } - public ICollection NextSteps { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Step/WorkflowStepTrigger.cs b/source/core/Coflo.Abstractions/Models/Step/WorkflowStepTrigger.cs deleted file mode 100644 index 59563e2..0000000 --- a/source/core/Coflo.Abstractions/Models/Step/WorkflowStepTrigger.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Coflo.Abstractions.Models.Step; - -public class WorkflowStepTrigger -{ - public List Steps { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Tenant/ApplicationTenant.cs b/source/core/Coflo.Abstractions/Models/Tenant/ApplicationTenant.cs deleted file mode 100644 index 28889c5..0000000 --- a/source/core/Coflo.Abstractions/Models/Tenant/ApplicationTenant.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Coflo.Abstractions.Contracts.Tenant; - -namespace Coflo.Abstractions.Models.Tenant; - -public class ApplicationTenant : IApplicationTenant -{ - public Guid TenantId { get; set; } - public string Name { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Variable/VariableDefinition.cs b/source/core/Coflo.Abstractions/Models/Variable/VariableDefinition.cs deleted file mode 100644 index 9f96913..0000000 --- a/source/core/Coflo.Abstractions/Models/Variable/VariableDefinition.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Coflo.Abstractions.Enums; - -namespace Coflo.Abstractions.Models.Variable; - -public class VariableDefinition -{ - public string Name { get; set; } = default!; - public string DisplayName { get; set; } = default!; - public VariableType Type { get; set; } = default!; - public bool IsArray { get; set; } - public object? DefaultValue { get; set; } - public bool IsRequired { get; set; } - public bool IsSecret { get; set; } - public string? Description { get; set; } - public string? Validation { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Variable/VariableValue.cs b/source/core/Coflo.Abstractions/Models/Variable/VariableValue.cs deleted file mode 100644 index b8281d3..0000000 --- a/source/core/Coflo.Abstractions/Models/Variable/VariableValue.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Coflo.Abstractions.Models.Variable; - -public class VariableValue : VariableDefinition -{ - public object? Value { get; set; } = default!; -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinition.cs b/source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinition.cs deleted file mode 100644 index 4dbb88c..0000000 --- a/source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinition.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Coflo.Abstractions.Contracts.Tenant; -using NodaTime; - -namespace Coflo.Abstractions.Models.Workflow; - -public class WorkflowDefinition : ITenantScope -{ - public long Id { get; set; } - public string Name { get; set; } - - public Guid? TenantId { get; set; } - - public ICollection Versions { get; set; } - - public Instant CreatedAt { get; set; } - public Instant UpdatedAt { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinitionVersion.cs b/source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinitionVersion.cs deleted file mode 100644 index 4836fe0..0000000 --- a/source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinitionVersion.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Coflo.Abstractions.Contracts.Tenant; -using NodaTime; - -namespace Coflo.Abstractions.Models.Workflow; - -public class WorkflowDefinitionVersion : ITenantScope -{ - public long WorkflowId { get; set; } - public long VersionId { get; set; } - public Guid? TenantId { get; set; } - - - - public Instant CreatedAt { get; set; } - public Instant UpdatedAt { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Models/Workflow/WorkflowInstance.cs b/source/core/Coflo.Abstractions/Models/Workflow/WorkflowInstance.cs deleted file mode 100644 index d1ea896..0000000 --- a/source/core/Coflo.Abstractions/Models/Workflow/WorkflowInstance.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Coflo.Abstractions.Models.Workflow; - -public class WorkflowInstance -{ - -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Contracts/IEventRepository.cs b/source/core/Coflo.Abstractions/Persistence/Contracts/IEventRepository.cs new file mode 100644 index 0000000..214d5b1 --- /dev/null +++ b/source/core/Coflo.Abstractions/Persistence/Contracts/IEventRepository.cs @@ -0,0 +1,19 @@ +using Coflo.Abstractions.Events.Models; + +namespace Coflo.Abstractions.Persistence.Contracts; + +public interface IEventRepository +{ + Task CreateEvent(Event newEvent, CancellationToken cancellationToken = default); + + Task GetEvent(string id, CancellationToken cancellationToken = default); + + Task> GetRunnableEvents(DateTime asAt, CancellationToken cancellationToken = default); + + Task> GetEvents(string eventName, string eventKey, DateTime asOf, + CancellationToken cancellationToken = default); + + Task MarkEventProcessed(string id, CancellationToken cancellationToken = default); + + Task MarkEventUnprocessed(string id, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Contracts/IPersistenceProvider.cs b/source/core/Coflo.Abstractions/Persistence/Contracts/IPersistenceProvider.cs new file mode 100644 index 0000000..6c3b8ff --- /dev/null +++ b/source/core/Coflo.Abstractions/Persistence/Contracts/IPersistenceProvider.cs @@ -0,0 +1,11 @@ +using Coflo.Abstractions.Execution.Models; + +namespace Coflo.Abstractions.Persistence.Contracts; + +public interface IPersistenceProvider : IWorkflowRepository, ISubscriptionRepository, IEventRepository, + IScheduledCommandRepository +{ + Task PersistErrors(IEnumerable errors, CancellationToken cancellationToken = default); + + void EnsureStoreExists(); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Contracts/IScheduledCommandRepository.cs b/source/core/Coflo.Abstractions/Persistence/Contracts/IScheduledCommandRepository.cs new file mode 100644 index 0000000..a84cda2 --- /dev/null +++ b/source/core/Coflo.Abstractions/Persistence/Contracts/IScheduledCommandRepository.cs @@ -0,0 +1,12 @@ +using Coflo.Abstractions.Events.Models; + +namespace Coflo.Abstractions.Persistence.Contracts; + +public interface IScheduledCommandRepository +{ + bool SupportsScheduledCommands { get; } + + Task ScheduleCommand(ScheduledCommand command); + + Task ProcessCommands(DateTimeOffset asOf, Func action, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Contracts/ISubscriptionRepository.cs b/source/core/Coflo.Abstractions/Persistence/Contracts/ISubscriptionRepository.cs new file mode 100644 index 0000000..42c17c0 --- /dev/null +++ b/source/core/Coflo.Abstractions/Persistence/Contracts/ISubscriptionRepository.cs @@ -0,0 +1,21 @@ +using Coflo.Abstractions.Events.Models; + +namespace Coflo.Abstractions.Persistence.Contracts; + +public interface ISubscriptionRepository +{ + Task CreateEventSubscription(EventSubscription subscription, CancellationToken cancellationToken = default); + + Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default); + + Task TerminateSubscription(long eventSubscriptionId, CancellationToken cancellationToken = default); + + Task GetSubscription(long eventSubscriptionId, CancellationToken cancellationToken = default); + + Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default); + + Task SetSubscriptionToken(long eventSubscriptionId, string token, long workerId, DateTime expiry, CancellationToken cancellationToken = default); + + Task ClearSubscriptionToken(long eventSubscriptionId, string token, CancellationToken cancellationToken = default); + +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Contracts/IWorkflowRepository.cs b/source/core/Coflo.Abstractions/Persistence/Contracts/IWorkflowRepository.cs new file mode 100644 index 0000000..e0a9e18 --- /dev/null +++ b/source/core/Coflo.Abstractions/Persistence/Contracts/IWorkflowRepository.cs @@ -0,0 +1,24 @@ +using Coflo.Abstractions.Events.Models; +using Coflo.Abstractions.Workflow.Enums; +using Coflo.Abstractions.Workflow.Models; + +namespace Coflo.Abstractions.Persistence.Contracts; + +public interface IWorkflowRepository +{ + Task CreateNewWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default); + + Task PersistWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default); + + Task PersistWorkflow(WorkflowInstance workflow, List subscriptions, CancellationToken cancellationToken = default); + + Task> GetRunnableInstances(DateTime asAt, CancellationToken cancellationToken = default); + + [Obsolete] + Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take); + + Task GetWorkflowInstance(string Id, CancellationToken cancellationToken = default); + + Task> GetWorkflowInstances(IEnumerable ids, CancellationToken cancellationToken = default); + +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Exceptions/CurruptPersistenceDataException.cs b/source/core/Coflo.Abstractions/Persistence/Exceptions/CurruptPersistenceDataException.cs new file mode 100644 index 0000000..7edd3e1 --- /dev/null +++ b/source/core/Coflo.Abstractions/Persistence/Exceptions/CurruptPersistenceDataException.cs @@ -0,0 +1,5 @@ +namespace Coflo.Abstractions.Persistence.Exceptions; + +public class CorruptPersistenceDataException : Exception +{ +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Exceptions/NotFoundException.cs b/source/core/Coflo.Abstractions/Persistence/Exceptions/NotFoundException.cs new file mode 100644 index 0000000..98ab89a --- /dev/null +++ b/source/core/Coflo.Abstractions/Persistence/Exceptions/NotFoundException.cs @@ -0,0 +1,15 @@ +namespace Coflo.Abstractions.Persistence.Exceptions; + +public class NotFoundException : Exception +{ + + public NotFoundException() : base() + { + + } + + public NotFoundException(string message) : base(message) + { + + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Models/ControlPersistenceData.cs b/source/core/Coflo.Abstractions/Persistence/Models/ControlPersistenceData.cs new file mode 100644 index 0000000..4eddd64 --- /dev/null +++ b/source/core/Coflo.Abstractions/Persistence/Models/ControlPersistenceData.cs @@ -0,0 +1,6 @@ +namespace Coflo.Abstractions.Persistence.Models; + +public class ControlPersistenceData +{ + public bool ChildrenActive { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Models/IteratorPersistenceData.cs b/source/core/Coflo.Abstractions/Persistence/Models/IteratorPersistenceData.cs new file mode 100644 index 0000000..1478cf2 --- /dev/null +++ b/source/core/Coflo.Abstractions/Persistence/Models/IteratorPersistenceData.cs @@ -0,0 +1,6 @@ +namespace Coflo.Abstractions.Persistence.Models; + +public class IteratorPersistenceData : ControlPersistenceData +{ + public int Index { get; set; } = 0; +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Models/SchedulePersistenceData.cs b/source/core/Coflo.Abstractions/Persistence/Models/SchedulePersistenceData.cs new file mode 100644 index 0000000..60b45ad --- /dev/null +++ b/source/core/Coflo.Abstractions/Persistence/Models/SchedulePersistenceData.cs @@ -0,0 +1,6 @@ +namespace Coflo.Abstractions.Persistence.Models; + +public class SchedulePersistenceData +{ + public bool Elapsed { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Contracts/IContainerStepBuilder.cs b/source/core/Coflo.Abstractions/Primitives/Contracts/IContainerStepBuilder.cs new file mode 100644 index 0000000..8d0f6d7 --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Contracts/IContainerStepBuilder.cs @@ -0,0 +1,16 @@ +using Coflo.Abstractions.Steps.Contracts; +using Coflo.Abstractions.Workflow.Contracts; + +namespace Coflo.Abstractions.Primitives.Contracts; + +public interface IContainerStepBuilder + where TStepBody : IStepBody + where TReturnStep : IStepBody +{ + /// + /// The block of steps to execute + /// + /// + /// + IStepBuilder Do(Action> builder); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Contracts/IParallelStepBuilder.cs b/source/core/Coflo.Abstractions/Primitives/Contracts/IParallelStepBuilder.cs new file mode 100644 index 0000000..7d5f417 --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Contracts/IParallelStepBuilder.cs @@ -0,0 +1,12 @@ +using Coflo.Abstractions.Primitives.Iteration; +using Coflo.Abstractions.Steps.Contracts; +using Coflo.Abstractions.Workflow.Contracts; + +namespace Coflo.Abstractions.Primitives.Contracts; + +public interface IParallelStepBuilder + where TStepBody : IStepBody +{ + IParallelStepBuilder Do(Action> builder); + IStepBuilder Join(); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/ActionStepBody.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/ActionStepBody.cs new file mode 100644 index 0000000..eb631fe --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/ActionStepBody.cs @@ -0,0 +1,16 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Steps.Contracts; +using Coflo.Abstractions.Steps.Models; + +namespace Coflo.Abstractions.Primitives; + +public class ActionStepBody : StepBody +{ + public Action Body { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + Body(context); + return ExecutionResult.Next(); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Activity.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Activity.cs new file mode 100644 index 0000000..57e4033 --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/Activity.cs @@ -0,0 +1,53 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Primitives.Models; +using Coflo.Abstractions.Steps.Contracts; +using Coflo.Abstractions.Steps.Exceptions; +using Coflo.Abstractions.Steps.Models; + +namespace Coflo.Abstractions.Primitives; + +public class Activity : StepBody +{ + public string ActivityName { get; set; } + + public DateTime EffectiveDate { get; set; } + + public object Parameters { get; set; } + + public object Result { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + if (!context.ExecutionPointer.EventPublished) + { + DateTime effectiveDate = DateTime.MinValue; + + if (EffectiveDate != null) + { + effectiveDate = EffectiveDate; + } + + return ExecutionResult.WaitForActivity(ActivityName, Parameters, effectiveDate); + } + + if (context.ExecutionPointer.EventData is ActivityResult result) + { + var actResult = result; + + if (actResult.Status == ActivityResult.StatusType.Success) + { + Result = actResult.Data; + } + else + { + throw new ActivityFailedException(actResult.Data); + } + } + else + { + Result = context.ExecutionPointer.EventData; + } + + return ExecutionResult.Next(); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/ContainerStepBody.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/ContainerStepBody.cs new file mode 100644 index 0000000..c74dab3 --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/ContainerStepBody.cs @@ -0,0 +1,8 @@ +using Coflo.Abstractions.Steps.Models; + +namespace Coflo.Abstractions.Primitives; + +public abstract class ContainerStepBody : StepBody +{ + +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/Decide.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/Decide.cs new file mode 100644 index 0000000..81ba220 --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/Decide.cs @@ -0,0 +1,15 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Steps.Contracts; +using Coflo.Abstractions.Steps.Models; + +namespace Coflo.Abstractions.Primitives.Control; + +public class Decide : StepBody +{ + public object Expression { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + return ExecutionResult.Outcome(Expression); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/Delay.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/Delay.cs new file mode 100644 index 0000000..755357d --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/Delay.cs @@ -0,0 +1,20 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Steps.Contracts; +using Coflo.Abstractions.Steps.Models; + +namespace Coflo.Abstractions.Primitives.Control; + +public class Delay : StepBody +{ + public TimeSpan Period { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + if (context.PersistenceData != null) + { + return ExecutionResult.Next(); + } + + return ExecutionResult.Sleep(Period, true); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/EndStep.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/EndStep.cs new file mode 100644 index 0000000..b9fb208 --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/EndStep.cs @@ -0,0 +1,15 @@ +using Coflo.Abstractions.Workflow.Enums; +using Coflo.Abstractions.Workflow.Models; + +namespace Coflo.Abstractions.Primitives.Control; + +public class EndStep : WorkflowStep +{ + public override Type BodyType => null!; + + public override ExecutionPipelineDirective InitForExecution(WorkflowResult result, WorkflowDefinition definition, + WorkflowInstance workflow, ExecutionPointer executionPointer) + { + return ExecutionPipelineDirective.EndWorkflow; + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/If.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/If.cs new file mode 100644 index 0000000..f36dca5 --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/If.cs @@ -0,0 +1,36 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Persistence.Exceptions; +using Coflo.Abstractions.Persistence.Models; +using Coflo.Abstractions.Steps.Contracts; + +namespace Coflo.Abstractions.Primitives.Control; + +public class If : ContainerStepBody +{ + public bool Condition { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + if (context.PersistenceData == null) + { + if (Condition) + { + return ExecutionResult.Branch(new List { context.Item }, new ControlPersistenceData { ChildrenActive = true }); + } + + return ExecutionResult.Next(); + } + + if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) + { + if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) + { + return ExecutionResult.Next(); + } + + return ExecutionResult.Persist(context.PersistenceData); + } + + throw new CorruptPersistenceDataException(); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/OutcomeSwitch.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/OutcomeSwitch.cs new file mode 100644 index 0000000..0fb8ad4 --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/OutcomeSwitch.cs @@ -0,0 +1,41 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Persistence.Exceptions; +using Coflo.Abstractions.Persistence.Models; +using Coflo.Abstractions.Steps.Contracts; + +namespace Coflo.Abstractions.Primitives.Control; + +public class OutcomeSwitch : ContainerStepBody +{ + public override ExecutionResult Run(IStepExecutionContext context) + { + if (context.PersistenceData == null) + { + var result = ExecutionResult.Branch(new List { context.Item }, new ControlPersistenceData { ChildrenActive = true }); + result.OutcomeValue = GetPreviousOutcome(context); + return result; + } + + if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) + { + if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) + { + return ExecutionResult.Next(); + } + else + { + var result = ExecutionResult.Persist(context.PersistenceData); + result.OutcomeValue = GetPreviousOutcome(context); + return result; + } + } + + throw new CorruptPersistenceDataException(); + } + + private object GetPreviousOutcome(IStepExecutionContext context) + { + var prevPointer = context.Workflow.ExecutionPointers.FindById(context.ExecutionPointer.PredecessorId); + return prevPointer.Outcome; + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/WaitFor.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/WaitFor.cs new file mode 100644 index 0000000..c6a9994 --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/WaitFor.cs @@ -0,0 +1,34 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Steps.Contracts; +using Coflo.Abstractions.Steps.Models; + +namespace Coflo.Abstractions.Primitives.Control; + +public class WaitFor : StepBody +{ + public string EventKey { get; set; } + + public string EventName { get; set; } + + public DateTime EffectiveDate { get; set; } + + public object EventData { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + if (!context.ExecutionPointer.EventPublished) + { + DateTime effectiveDate = DateTime.MinValue; + + if (EffectiveDate != null) + { + effectiveDate = EffectiveDate; + } + + return ExecutionResult.WaitForEvent(EventName, EventKey, effectiveDate); + } + + EventData = context.ExecutionPointer.EventData; + return ExecutionResult.Next(); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/When.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/When.cs new file mode 100644 index 0000000..37c93cf --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/When.cs @@ -0,0 +1,47 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Persistence.Exceptions; +using Coflo.Abstractions.Persistence.Models; +using Coflo.Abstractions.Steps.Contracts; + +namespace Coflo.Abstractions.Primitives.Control; + +public class When : ContainerStepBody +{ + public object ExpectedOutcome { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + var switchOutcome = GetSwitchOutcome(context); + + if (ExpectedOutcome != switchOutcome) + { + if (Convert.ToString(ExpectedOutcome) != Convert.ToString(switchOutcome)) + { + return ExecutionResult.Next(); + } + } + + if (context.PersistenceData == null) + { + return ExecutionResult.Branch(new List { context.Item }, new ControlPersistenceData { ChildrenActive = true }); + } + + if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) + { + if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) + { + return ExecutionResult.Next(); + } + + return ExecutionResult.Persist(context.PersistenceData); + } + + throw new CorruptPersistenceDataException(); + } + + private object GetSwitchOutcome(IStepExecutionContext context) + { + var switchPointer = context.Workflow.ExecutionPointers.First(x => x.Children.Contains(context.ExecutionPointer.Id)); + return switchPointer.Outcome; + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/InlineStepBody.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/InlineStepBody.cs new file mode 100644 index 0000000..09bfe6a --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/InlineStepBody.cs @@ -0,0 +1,21 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Steps.Contracts; +using Coflo.Abstractions.Steps.Models; + +namespace Coflo.Abstractions.Primitives; + +public class InlineStepBody : StepBody +{ + + private readonly Func _body; + + public InlineStepBody(Func body) + { + _body = body; + } + + public override ExecutionResult Run(IStepExecutionContext context) + { + return _body.Invoke(context); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/Foreach.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/Foreach.cs new file mode 100644 index 0000000..c9a75cd --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/Foreach.cs @@ -0,0 +1,53 @@ +using System.Collections; +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Persistence.Models; +using Coflo.Abstractions.Steps.Contracts; + +namespace Coflo.Abstractions.Primitives.Iteration; + +public class Foreach : ContainerStepBody +{ + public IEnumerable Collection { get; set; } + public bool RunParallel { get; set; } = true; + + public override ExecutionResult Run(IStepExecutionContext context) + { + switch (context.PersistenceData) + { + case null: + { + var values = Collection.Cast().ToList(); + + if (!values.Any()) + return ExecutionResult.Next(); + + return ExecutionResult.Branch( + RunParallel ? new List(values) : new List(new object[] { values.ElementAt(0) }), + new IteratorPersistenceData { ChildrenActive = true }); + } + case IteratorPersistenceData { ChildrenActive: true } persistenceData: + { + if (!context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) + return ExecutionResult.Persist(persistenceData); + + if (RunParallel) return ExecutionResult.Next(); + + var values = Collection.Cast(); + + persistenceData.Index++; + + return persistenceData.Index < values.Count() + ? ExecutionResult.Branch(new List(new object[] { values.ElementAt(persistenceData.Index) }), + persistenceData) + : ExecutionResult.Next(); + } + } + + if (context.PersistenceData is not ControlPersistenceData { ChildrenActive: true }) + return ExecutionResult.Persist(context.PersistenceData); + + return context.Workflow.IsBranchComplete(context.ExecutionPointer.Id) + ? ExecutionResult.Next() + : ExecutionResult.Persist(context.PersistenceData); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/Sequence.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/Sequence.cs new file mode 100644 index 0000000..9489634 --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/Sequence.cs @@ -0,0 +1,23 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Persistence.Exceptions; +using Coflo.Abstractions.Persistence.Models; +using Coflo.Abstractions.Steps.Contracts; + +namespace Coflo.Abstractions.Primitives.Iteration; + +public class Sequence : ContainerStepBody +{ + public override ExecutionResult Run(IStepExecutionContext context) + { + return context.PersistenceData switch + { + null => ExecutionResult.Branch(new List { context.Item }, + new ControlPersistenceData { ChildrenActive = true }), + ControlPersistenceData { ChildrenActive: true } => + context.Workflow.IsBranchComplete(context.ExecutionPointer.Id) + ? ExecutionResult.Next() + : ExecutionResult.Persist(context.PersistenceData), + _ => throw new CorruptPersistenceDataException() + }; + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/While.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/While.cs new file mode 100644 index 0000000..d868158 --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/While.cs @@ -0,0 +1,31 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Persistence.Exceptions; +using Coflo.Abstractions.Persistence.Models; +using Coflo.Abstractions.Steps.Contracts; + +namespace Coflo.Abstractions.Primitives.Iteration; + +public class While : ContainerStepBody +{ + public bool Condition { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + switch (context.PersistenceData) + { + case null when Condition: + return ExecutionResult.Branch(new List { context.Item }, + new ControlPersistenceData { ChildrenActive = true }); + case null: + return ExecutionResult.Next(); + case ControlPersistenceData data when (data.ChildrenActive): + { + return ExecutionResult.Persist(!context.Workflow.IsBranchComplete(context.ExecutionPointer.Id) + ? context.PersistenceData + : null); //re-evaluate condition on next pass + } + default: + throw new CorruptPersistenceDataException(); + } + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Recurring/Recur.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Recurring/Recur.cs new file mode 100644 index 0000000..59776cf --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/Recurring/Recur.cs @@ -0,0 +1,26 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Steps.Contracts; + +namespace Coflo.Abstractions.Primitives.Recurring; + +public class Recur : ContainerStepBody +{ + public TimeSpan Interval { get; set; } + + public bool StopCondition { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + if (StopCondition) + { + return ExecutionResult.Next(); + } + + return new ExecutionResult + { + Proceed = false, + BranchValues = new List { null }, + SleepFor = Interval + }; + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Recurring/Schedule.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Recurring/Schedule.cs new file mode 100644 index 0000000..e34560e --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/Recurring/Schedule.cs @@ -0,0 +1,30 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Persistence.Models; +using Coflo.Abstractions.Steps.Contracts; + +namespace Coflo.Abstractions.Primitives.Recurring; + +public class Schedule : ContainerStepBody +{ + public TimeSpan Interval { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (context.PersistenceData == null) + { + return ExecutionResult.Sleep(Interval, new SchedulePersistenceData { Elapsed = false }); + } + + if (context.PersistenceData is not SchedulePersistenceData data) throw new ArgumentException(); + + if (!data.Elapsed) + return ExecutionResult.Branch(new List { context.Item }, + new SchedulePersistenceData { Elapsed = true }); + + + return context.Workflow.IsBranchComplete(context.ExecutionPointer.Id) + ? ExecutionResult.Next() + : ExecutionResult.Persist(context.PersistenceData); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/SagaContainer.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/SagaContainer.cs new file mode 100644 index 0000000..22c830d --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/SagaContainer.cs @@ -0,0 +1,17 @@ +using Coflo.Abstractions.Steps.Contracts; +using Coflo.Abstractions.Workflow.Models; + +namespace Coflo.Abstractions.Primitives; + +public class SagaContainer : WorkflowStep + where TStepBody : IStepBody +{ + public override bool ResumeChildrenAfterCompensation => false; + public override bool RevertChildrenAfterCompensation => true; + + public override void PrimeForRetry(ExecutionPointer pointer) + { + base.PrimeForRetry(pointer); + pointer.PersistenceData = null; + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/SubWorkflowStepBody.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/SubWorkflowStepBody.cs new file mode 100644 index 0000000..e8ee42c --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/SubWorkflowStepBody.cs @@ -0,0 +1,14 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Steps.Contracts; +using Coflo.Abstractions.Steps.Models; + +namespace Coflo.Abstractions.Primitives; + +public class SubWorkflowStepBody : StepBody +{ + public override ExecutionResult Run(IStepExecutionContext context) + { + // TODO: What is this supposed to do? + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/WorkflowStepInline.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/WorkflowStepInline.cs new file mode 100644 index 0000000..1a107a6 --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Implementation/WorkflowStepInline.cs @@ -0,0 +1,15 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Steps.Contracts; +using Coflo.Abstractions.Workflow.Models; + +namespace Coflo.Abstractions.Primitives; + +public class WorkflowStepInline : WorkflowStep +{ + public Func Body { get; set; } + + public override IStepBody ConstructBody(IServiceProvider serviceProvider) + { + return new InlineStepBody(Body); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Models/ActivityResult.cs b/source/core/Coflo.Abstractions/Primitives/Models/ActivityResult.cs new file mode 100644 index 0000000..0dd9acd --- /dev/null +++ b/source/core/Coflo.Abstractions/Primitives/Models/ActivityResult.cs @@ -0,0 +1,9 @@ +namespace Coflo.Abstractions.Primitives.Models; + +public class ActivityResult +{ + public enum StatusType { Success, Fail } + public StatusType Status { get; set; } + public long SubscriptionId { get; set; } + public object Data { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Attributes/StepAttribute.cs b/source/core/Coflo.Abstractions/Steps/Attributes/StepAttribute.cs new file mode 100644 index 0000000..971a2d4 --- /dev/null +++ b/source/core/Coflo.Abstractions/Steps/Attributes/StepAttribute.cs @@ -0,0 +1,7 @@ +namespace Coflo.Abstractions.Steps.Attributes; + +public class StepAttribute +{ + public string Name { get; set; } + public string DisplayName { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Contracts/IActivityController.cs b/source/core/Coflo.Abstractions/Steps/Contracts/IActivityController.cs new file mode 100644 index 0000000..24f60f5 --- /dev/null +++ b/source/core/Coflo.Abstractions/Steps/Contracts/IActivityController.cs @@ -0,0 +1,11 @@ +using Coflo.Abstractions.Steps.Models; + +namespace Coflo.Abstractions.Steps.Contracts; + +public interface IActivityController +{ + Task GetPendingActivity(string activityName, long workerId, TimeSpan? timeout = null); + Task ReleaseActivityToken(string token); + Task SubmitActivitySuccess(string token, object result); + Task SubmitActivityFailure(string token, object result); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Contracts/IStepBody.cs b/source/core/Coflo.Abstractions/Steps/Contracts/IStepBody.cs new file mode 100644 index 0000000..340958c --- /dev/null +++ b/source/core/Coflo.Abstractions/Steps/Contracts/IStepBody.cs @@ -0,0 +1,9 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Steps.Models; + +namespace Coflo.Abstractions.Steps.Contracts; + +public interface IStepBody +{ + Task RunAsync(IStepExecutionContext context); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Contracts/IStepBuilder.cs b/source/core/Coflo.Abstractions/Steps/Contracts/IStepBuilder.cs new file mode 100644 index 0000000..443c02a --- /dev/null +++ b/source/core/Coflo.Abstractions/Steps/Contracts/IStepBuilder.cs @@ -0,0 +1,153 @@ +using System.Linq.Expressions; +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Steps.Models; +using Coflo.Abstractions.Workflow.Contracts; +using Coflo.Abstractions.Workflow.Enums; +using Coflo.Abstractions.Workflow.Models; + +namespace Coflo.Abstractions.Steps.Contracts; + +public interface IStepBuilder : IWorkflowModifier + where TStepBody : IStepBody +{ + IWorkflowBuilder WorkflowBuilder { get; } + + WorkflowStep Step { get; set; } + + /// + /// Specifies a display name for the step + /// + /// A display name for the step for easy identification in logs, etc... + /// + IStepBuilder Name(string name); + + /// + /// Specifies a custom Id to reference this step + /// + /// A custom Id to reference this step + /// + IStepBuilder Id(string id); + + /// + /// Specify the next step in the workflow by Id + /// + /// + /// + IStepBuilder Attach(string id); + + /// + /// Configure an outcome branch for this step, then wire it to another step + /// + /// + /// + IStepBuilder Branch(object outcomeValue, IStepBuilder branch) + where TStep : IStepBody; + + /// + /// Configure an outcome branch for this step, then wire it to another step + /// + /// + /// + IStepBuilder Branch(Expression> outcomeExpression, + IStepBuilder branch) where TStep : IStepBody; + + /// + /// Map properties on the step to properties on the workflow data object before the step executes + /// + /// + /// Property on the step + /// + /// + IStepBuilder Input(Expression> stepProperty, + Expression> value); + + /// + /// Map properties on the step to properties on the workflow data object before the step executes + /// + /// + /// The property on the step + /// + /// + IStepBuilder Input(Expression> stepProperty, + Expression> value); + + /// + /// Manipulate properties on the step before its executed. + /// + /// + /// + IStepBuilder Input(Action action); + + IStepBuilder Input(Action action); + + /// + /// Map properties on the workflow data object to properties on the step after the step executes + /// + /// + /// Property on the data object + /// + /// + IStepBuilder Output(Expression> dataProperty, + Expression> value); + + /// + /// Manipulate properties on the data object after the step executes + /// + /// + /// + IStepBuilder Output(Action action); + + IStepBuilder End(string name) where TStep : IStepBody; + + /// + /// Configure the behavior when this step throws an unhandled exception + /// + /// What action to take when this step throws an unhandled exception + /// If the behavior is retry, how often + /// + IStepBuilder OnError(WorkflowErrorHandling behavior, TimeSpan? retryInterval = null); + + /// + /// Ends the workflow and marks it as complete + /// + /// + IStepBuilder EndWorkflow(); + + /// + /// Undo step if unhandled exception is thrown by this step + /// + /// The type of the step to execute + /// Configure additional parameters for this step + /// + IStepBuilder CompensateWith(Action> stepSetup = null) + where TStep : IStepBody; + + /// + /// Undo step if unhandled exception is thrown by this step + /// + /// + /// + IStepBuilder CompensateWith(Func body); + + /// + /// Undo step if unhandled exception is thrown by this step + /// + /// + /// + IStepBuilder CompensateWith(Action body); + + /// + /// Undo step if unhandled exception is thrown by this step + /// + /// + /// + IStepBuilder CompensateWithSequence(Action> builder); + + /// + /// Prematurely cancel the execution of this step on a condition + /// + /// + /// + IStepBuilder CancelCondition(Expression> cancelCondition, + bool proceedAfterCancel = false); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Contracts/IStepExecutionContext.cs b/source/core/Coflo.Abstractions/Steps/Contracts/IStepExecutionContext.cs new file mode 100644 index 0000000..d47abe5 --- /dev/null +++ b/source/core/Coflo.Abstractions/Steps/Contracts/IStepExecutionContext.cs @@ -0,0 +1,18 @@ +using Coflo.Abstractions.Workflow.Models; + +namespace Coflo.Abstractions.Steps.Contracts; + +public interface IStepExecutionContext +{ + object Item { get; set; } + + ExecutionPointer ExecutionPointer { get; set; } + + object PersistenceData { get; set; } + + WorkflowStep Step { get; set; } + + WorkflowInstance Workflow { get; set; } + + CancellationToken CancellationToken { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Contracts/IStepParameter.cs b/source/core/Coflo.Abstractions/Steps/Contracts/IStepParameter.cs new file mode 100644 index 0000000..3a0f342 --- /dev/null +++ b/source/core/Coflo.Abstractions/Steps/Contracts/IStepParameter.cs @@ -0,0 +1,7 @@ +namespace Coflo.Abstractions.Steps.Contracts; + +public interface IStepParameter +{ + void AssignInput(object data, IStepBody body, IStepExecutionContext context); + void AssignOutput(object data, IStepBody body, IStepExecutionContext context); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Exceptions/ActivityFailedException.cs b/source/core/Coflo.Abstractions/Steps/Exceptions/ActivityFailedException.cs new file mode 100644 index 0000000..85f17ad --- /dev/null +++ b/source/core/Coflo.Abstractions/Steps/Exceptions/ActivityFailedException.cs @@ -0,0 +1,8 @@ +namespace Coflo.Abstractions.Steps.Exceptions; + +public class ActivityFailedException : Exception +{ + public ActivityFailedException(object data) + { + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Models/ActionStepBody.cs b/source/core/Coflo.Abstractions/Steps/Models/ActionStepBody.cs new file mode 100644 index 0000000..75c07e9 --- /dev/null +++ b/source/core/Coflo.Abstractions/Steps/Models/ActionStepBody.cs @@ -0,0 +1,16 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Steps.Contracts; + +namespace Coflo.Abstractions.Steps.Models; + +public class ActionStepBody : StepBody +{ + public Action Body { get; set; } + + public override ExecutionResult Run(IStepExecutionContext context) + { + Body(context); + + return ExecutionResult.Next(); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Models/ActivityToken.cs b/source/core/Coflo.Abstractions/Steps/Models/ActivityToken.cs new file mode 100644 index 0000000..926a0a2 --- /dev/null +++ b/source/core/Coflo.Abstractions/Steps/Models/ActivityToken.cs @@ -0,0 +1,36 @@ +using System.Text; +using System.Text.Json; +namespace Coflo.Abstractions.Steps.Models; + +public class ActivityToken +{ + public long SubscriptionId { get; set; } + public string ActivityName { get; set; } + public string Nonce { get; set; } + + public string Encode() + { + var json = JsonSerializer.Serialize(this); + + return Convert.ToBase64String(Encoding.UTF8.GetBytes(json)); + } + + public static ActivityToken Create(long subscriptionId, string activityName) + { + return new ActivityToken + { + SubscriptionId = subscriptionId, + ActivityName = activityName, + Nonce = Guid.NewGuid().ToString() + }; + } + + public static ActivityToken Decode(string encodedToken) + { + var raw = Convert.FromBase64String(encodedToken); + var json = Encoding.UTF8.GetString(raw); + + return JsonSerializer.Deserialize(json) + ?? throw new Exception("Invalid token"); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Models/InlineStepBody.cs b/source/core/Coflo.Abstractions/Steps/Models/InlineStepBody.cs new file mode 100644 index 0000000..926b0b8 --- /dev/null +++ b/source/core/Coflo.Abstractions/Steps/Models/InlineStepBody.cs @@ -0,0 +1,20 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Steps.Contracts; + +namespace Coflo.Abstractions.Steps.Models; + +public class InlineStepBody : StepBody +{ + + private readonly Func _body; + + public InlineStepBody(Func body) + { + _body = body; + } + + public override ExecutionResult Run(IStepExecutionContext context) + { + return _body.Invoke(context); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Models/PendingActivity.cs b/source/core/Coflo.Abstractions/Steps/Models/PendingActivity.cs new file mode 100644 index 0000000..7cb243c --- /dev/null +++ b/source/core/Coflo.Abstractions/Steps/Models/PendingActivity.cs @@ -0,0 +1,9 @@ +namespace Coflo.Abstractions.Steps.Models; + +public class PendingActivity +{ + public string Token { get; set; } + public string ActivityName { get; set; } + public object Parameters { get; set; } + public DateTime TokenExpiry { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Models/StepBody.cs b/source/core/Coflo.Abstractions/Steps/Models/StepBody.cs new file mode 100644 index 0000000..8008e71 --- /dev/null +++ b/source/core/Coflo.Abstractions/Steps/Models/StepBody.cs @@ -0,0 +1,42 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Steps.Contracts; + +namespace Coflo.Abstractions.Steps.Models; + +public abstract class StepBody : IStepBody +{ + public abstract ExecutionResult Run(IStepExecutionContext context); + + public Task RunAsync(IStepExecutionContext context) + { + return Task.FromResult(Run(context)); + } + + protected Execution.Models.ExecutionResult OutcomeResult(object value) + { + return new Execution.Models.ExecutionResult + { + Proceed = true, + OutcomeValue = value + }; + } + + protected ExecutionResult PersistResult(object persistenceData) + { + return new ExecutionResult + { + Proceed = false, + PersistenceData = persistenceData + }; + } + + protected ExecutionResult SleepResult(object persistenceData, TimeSpan sleep) + { + return new ExecutionResult + { + Proceed = false, + PersistenceData = persistenceData, + SleepFor = sleep + }; + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Models/StepBodyAsync.cs b/source/core/Coflo.Abstractions/Steps/Models/StepBodyAsync.cs new file mode 100644 index 0000000..7f05a55 --- /dev/null +++ b/source/core/Coflo.Abstractions/Steps/Models/StepBodyAsync.cs @@ -0,0 +1,9 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Steps.Contracts; + +namespace Coflo.Abstractions.Steps.Models; + +public abstract class StepBodyAsync : IStepBody +{ + public abstract Task RunAsync(IStepExecutionContext context); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Models/WorkflowStepDelegate.cs b/source/core/Coflo.Abstractions/Steps/Models/WorkflowStepDelegate.cs new file mode 100644 index 0000000..cbca4cc --- /dev/null +++ b/source/core/Coflo.Abstractions/Steps/Models/WorkflowStepDelegate.cs @@ -0,0 +1,8 @@ +using Coflo.Abstractions.Execution.Models; + +namespace Coflo.Abstractions.Steps.Models; + +/// +/// Represents a function that executes a workflow step and returns a result. +/// +public delegate Task WorkflowStepDelegate(); \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflow.cs b/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflow.cs new file mode 100644 index 0000000..eb880aa --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflow.cs @@ -0,0 +1,14 @@ +namespace Coflo.Abstractions.Workflow.Contracts; + +public interface IWorkflow + where TData : new() +{ + long Id { get; } + int Version { get; } + + void Build(IWorkflowBuilder builder); +} + +public interface IWorkflow : IWorkflow +{ +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowBuilder.cs b/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowBuilder.cs new file mode 100644 index 0000000..4aa33d9 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowBuilder.cs @@ -0,0 +1,37 @@ +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Steps.Contracts; +using Coflo.Abstractions.Steps.Models; +using Coflo.Abstractions.Workflow.Enums; +using Coflo.Abstractions.Workflow.Models; + +namespace Coflo.Abstractions.Workflow.Contracts; + +public interface IWorkflowBuilder +{ + List Steps { get; } + + int LastStep { get; } + + IWorkflowBuilder UseData(); + + WorkflowDefinition Build(string id, int version); + + void AddStep(WorkflowStep step); + + void AttachBranch(IWorkflowBuilder branch); +} + +public interface IWorkflowBuilder : IWorkflowBuilder, IWorkflowModifier +{ + IStepBuilder StartWith(Action> stepSetup = null) where TStep : IStepBody; + + IStepBuilder StartWith(Func body); + + IStepBuilder StartWith(Action body); + + IEnumerable GetUpstreamSteps(int id); + + IWorkflowBuilder UseDefaultErrorBehavior(WorkflowErrorHandling behavior, TimeSpan? retryInterval = null); + + IWorkflowBuilder CreateBranch(); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowController.cs b/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowController.cs new file mode 100644 index 0000000..24c3647 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowController.cs @@ -0,0 +1,38 @@ +namespace Coflo.Abstractions.Workflow.Contracts; + +public interface IWorkflowController +{ + Task StartWorkflow(string workflowId, object data = null, string reference = null); + Task StartWorkflow(string workflowId, int? version, object data = null, string reference = null); + + Task StartWorkflow(string workflowId, TData data = null, string reference = null) + where TData : class, new(); + + Task StartWorkflow(string workflowId, int? version, TData data = null, string reference = null) + where TData : class, new(); + + Task PublishEvent(string eventName, string eventKey, object eventData, DateTime? effectiveDate = null); + void RegisterWorkflow() where TWorkflow : IWorkflow; + void RegisterWorkflow() where TWorkflow : IWorkflow where TData : new(); + + /// + /// Suspend the execution of a given workflow until .ResumeWorkflow is called + /// + /// + /// + Task SuspendWorkflow(string workflowId); + + /// + /// Resume a previously suspended workflow + /// + /// + /// + Task ResumeWorkflow(string workflowId); + + /// + /// Permanently terminate the exeuction of a given workflow + /// + /// + /// + Task TerminateWorkflow(string workflowId); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowModifier.cs b/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowModifier.cs new file mode 100644 index 0000000..4686462 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowModifier.cs @@ -0,0 +1,197 @@ +using System.Collections; +using System.Linq.Expressions; +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Primitives; +using Coflo.Abstractions.Primitives.Contracts; +using Coflo.Abstractions.Primitives.Control; +using Coflo.Abstractions.Primitives.Iteration; +using Coflo.Abstractions.Primitives.Recurring; +using Coflo.Abstractions.Steps.Contracts; +using Coflo.Abstractions.Steps.Models; +using ActionStepBody = Coflo.Abstractions.Steps.Models.ActionStepBody; +using InlineStepBody = Coflo.Abstractions.Steps.Models.InlineStepBody; + +namespace Coflo.Abstractions.Workflow.Contracts; + +public interface IWorkflowModifier + where TStepBody : IStepBody + +{ + /// + /// Specify the next step in the workflow + /// + /// The type of the step to execute + /// Configure additional parameters for this step + /// + IStepBuilder Then(Action> stepSetup = null) where TStep : IStepBody; + + /// + /// Specify the next step in the workflow + /// + /// + /// + /// + IStepBuilder Then(IStepBuilder newStep) where TStep : IStepBody; + + /// + /// Specify an inline next step in the workflow + /// + /// + /// + IStepBuilder Then(Func body); + + /// + /// Specify an inline next step in the workflow + /// + /// + /// + IStepBuilder Then(Action body); + + /// + /// Wait here until to specified event is published + /// + /// The name used to identify the kind of event to wait for + /// A specific key value within the context of the event to wait for + /// Listen for events as of this effective date + /// A conditon that when true will cancel this WaitFor + /// + IStepBuilder WaitFor(string eventName, Expression> eventKey, + Expression> effectiveDate = null, Expression> cancelCondition = null); + + /// + /// Wait here until to specified event is published + /// + /// The name used to identify the kind of event to wait for + /// A specific key value within the context of the event to wait for + /// Listen for events as of this effective date + /// A conditon that when true will cancel this WaitFor + /// + IStepBuilder WaitFor(string eventName, + Expression> eventKey, + Expression> effectiveDate = null, Expression> cancelCondition = null); + + /// + /// Wait for a specified period + /// + /// + /// + IStepBuilder Delay(Expression> period); + + /// + /// Evaluate an expression and take a different path depending on the value + /// + /// Expression to evaluate for decision + /// + IStepBuilder Decide(Expression> expression); + + /// + /// Execute a block of steps, once for each item in a collection in a parallel foreach + /// + /// Resolves a collection for iterate over + /// + IContainerStepBuilder ForEach(Expression> collection); + + /// + /// Execute a block of steps, once for each item in a collection in a RunParallel foreach + /// + /// Resolves a collection for iterate over + /// + IContainerStepBuilder ForEach(Expression> collection, + Expression> runParallel); + + /// + /// Execute a block of steps, once for each item in a collection in a RunParallel foreach + /// + /// Resolves a collection for iterate over + /// + IContainerStepBuilder ForEach( + Expression> collection, + Expression> runParallel); + + /// + /// Repeat a block of steps until a condition becomes true + /// + /// Resolves a condition to break out of the while loop + /// + IContainerStepBuilder While(Expression> condition); + + /// + /// Repeat a block of steps until a condition becomes true + /// + /// Resolves a condition to break out of the while loop + /// + IContainerStepBuilder While(Expression> condition); + + /// + /// Execute a block of steps if a condition is true + /// + /// Resolves a condition to evaluate + /// + IContainerStepBuilder If(Expression> condition); + + /// + /// Execute a block of steps if a condition is true + /// + /// Resolves a condition to evaluate + /// + IContainerStepBuilder If(Expression> condition); + + /// + /// Configure an outcome for this step, then wire it to a sequence + /// + /// + /// + IContainerStepBuilder When(Expression> outcomeValue, + string label = null); + + /// + /// Execute multiple blocks of steps in parallel + /// + /// + IParallelStepBuilder Parallel(); + + /// + /// Execute a sequence of steps in a container + /// + /// + IStepBuilder Saga(Action> builder); + + /// + /// Schedule a block of steps to execute in parallel sometime in the future + /// + /// The time span to wait before executing the block + /// + IContainerStepBuilder Schedule(Expression> time); + + /// + /// Schedule a block of steps to execute in parallel sometime in the future at a recurring interval + /// + /// The time span to wait between recurring executions + /// Resolves a condition to stop the recurring task + /// + IContainerStepBuilder Recur(Expression> interval, + Expression> until); + + /// + /// Wait here until an external activity is complete + /// + /// The name used to identify the activity to wait for + /// The data to pass the external activity worker + /// Listen for events as of this effective date + /// A conditon that when true will cancel this WaitFor + /// + IStepBuilder Activity(string activityName, Expression> parameters = null, + Expression> effectiveDate = null, Expression> cancelCondition = null); + + /// + /// Wait here until an external activity is complete + /// + /// The name used to identify the activity to wait for + /// The data to pass the external activity worker + /// Listen for events as of this effective date + /// A conditon that when true will cancel this WaitFor + /// + IStepBuilder Activity(Expression> activityName, + Expression> parameters = null, + Expression> effectiveDate = null, Expression> cancelCondition = null); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Enums/ExecutionPipelineDirective.cs b/source/core/Coflo.Abstractions/Workflow/Enums/ExecutionPipelineDirective.cs new file mode 100644 index 0000000..0a681b5 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Enums/ExecutionPipelineDirective.cs @@ -0,0 +1,8 @@ +namespace Coflo.Abstractions.Workflow.Enums; + +public enum ExecutionPipelineDirective +{ + Next = 0, + Defer = 1, + EndWorkflow = 2 +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Enums/PointerStatus.cs b/source/core/Coflo.Abstractions/Workflow/Enums/PointerStatus.cs new file mode 100644 index 0000000..83d5881 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Enums/PointerStatus.cs @@ -0,0 +1,15 @@ +namespace Coflo.Abstractions.Workflow.Enums; + +public enum PointerStatus +{ + Legacy = 0, + Pending = 1, + Running = 2, + Complete = 3, + Sleeping = 4, + WaitingForEvent = 5, + Failed = 6, + Compensated = 7, + Cancelled = 8, + PendingPredecessor = 9 +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Enums/WorkflowErrorHandling.cs b/source/core/Coflo.Abstractions/Workflow/Enums/WorkflowErrorHandling.cs new file mode 100644 index 0000000..ef83c82 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Enums/WorkflowErrorHandling.cs @@ -0,0 +1,9 @@ +namespace Coflo.Abstractions.Workflow.Enums; + +public enum WorkflowErrorHandling +{ + Retry = 0, + Suspend = 1, + Terminate = 2, + Compensate = 3 +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Enums/WorkflowStatus.cs b/source/core/Coflo.Abstractions/Workflow/Enums/WorkflowStatus.cs new file mode 100644 index 0000000..99e6429 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Enums/WorkflowStatus.cs @@ -0,0 +1,9 @@ +namespace Coflo.Abstractions.Workflow.Enums; + +public enum WorkflowStatus +{ + Runnable = 0, + Suspended = 1, + Complete = 2, + Terminated = 3, +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowDefinitionLoadException.cs b/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowDefinitionLoadException.cs new file mode 100644 index 0000000..e34d139 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowDefinitionLoadException.cs @@ -0,0 +1,9 @@ +namespace Coflo.Abstractions.Workflow.Exceptions; + +public class WorkflowDefinitionLoadException : Exception +{ + public WorkflowDefinitionLoadException(string message) + : base(message) + { + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowLockedException.cs b/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowLockedException.cs new file mode 100644 index 0000000..2b2c64a --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowLockedException.cs @@ -0,0 +1,9 @@ +namespace Coflo.Abstractions.Workflow.Exceptions; + +public class WorkflowLockedException : Exception +{ + public WorkflowLockedException(): base() + { + // + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowNotRegisteredException.cs b/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowNotRegisteredException.cs new file mode 100644 index 0000000..e170cd6 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowNotRegisteredException.cs @@ -0,0 +1,6 @@ +namespace Coflo.Abstractions.Workflow.Exceptions; + +public class WorkflowNotRegisteredException +{ + +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Models/ExecutionPointer.cs b/source/core/Coflo.Abstractions/Workflow/Models/ExecutionPointer.cs new file mode 100644 index 0000000..921958a --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Models/ExecutionPointer.cs @@ -0,0 +1,52 @@ +using Coflo.Abstractions.Workflow.Enums; + +namespace Coflo.Abstractions.Workflow.Models; + +public class ExecutionPointer +{ + private IReadOnlyCollection _scope = new List(); + + public string Id { get; set; } + + public int StepId { get; set; } + + public bool Active { get; set; } + + public DateTime? SleepUntil { get; set; } + + public object PersistenceData { get; set; } + + public DateTime? StartTime { get; set; } + + public DateTime? EndTime { get; set; } + + public string EventName { get; set; } + + public string EventKey { get; set; } + + public bool EventPublished { get; set; } + + public object EventData { get; set; } + + public Dictionary ExtensionAttributes { get; set; } = new(); + + public string StepName { get; set; } + + public int RetryCount { get; set; } + + public List Children { get; set; } = new(); + + public object ContextItem { get; set; } + + public string PredecessorId { get; set; } + + public object Outcome { get; set; } + + public PointerStatus Status { get; set; } = PointerStatus.Legacy; + + public IReadOnlyCollection Scope + { + get => _scope; + set => _scope = new List(value); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Models/ExecutionPointerCollection.cs b/source/core/Coflo.Abstractions/Workflow/Models/ExecutionPointerCollection.cs new file mode 100644 index 0000000..e92b081 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Models/ExecutionPointerCollection.cs @@ -0,0 +1,105 @@ +using System.Collections; +using Coflo.Abstractions.Workflow.Enums; + +namespace Coflo.Abstractions.Workflow.Models; + + public class ExecutionPointerCollection : ICollection + { + private readonly Dictionary _dictionary = new(); + private readonly Dictionary> _scopeMap = new(); + + public ExecutionPointerCollection() + { + } + + public ExecutionPointerCollection(int capacity) + { + _dictionary = new Dictionary(capacity); + } + + public ExecutionPointerCollection(ICollection pointers) + { + foreach (var ptr in pointers) + { + Add(ptr); + } + } + + public IEnumerator GetEnumerator() + { + return _dictionary.Values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public ExecutionPointer FindById(string id) + { + if (!_dictionary.ContainsKey(id)) + return null; + + return _dictionary[id]; + } + + public ICollection FindByScope(string stackFrame) + { + if (!_scopeMap.ContainsKey(stackFrame)) + return new List(); + + return _scopeMap[stackFrame]; + } + + public void Add(ExecutionPointer item) + { + _dictionary.Add(item.Id, item); + + foreach (var stackFrame in item.Scope) + { + if (!_scopeMap.ContainsKey(stackFrame)) + _scopeMap.Add(stackFrame, new List()); + _scopeMap[stackFrame].Add(item); + } + } + + public void Clear() + { + _dictionary.Clear(); + _scopeMap.Clear(); + } + + public bool Contains(ExecutionPointer item) + { + return _dictionary.ContainsValue(item); + } + + public void CopyTo(ExecutionPointer[] array, int arrayIndex) + { + _dictionary.Values.CopyTo(array, arrayIndex); + } + + public bool Remove(ExecutionPointer item) + { + foreach (var stackFrame in item.Scope) + { + _scopeMap[stackFrame].Remove(item); + } + + return _dictionary.Remove(item.Id); + } + + public ExecutionPointer Find(Predicate match) + { + return _dictionary.Values.FirstOrDefault(x => match(x)); + } + + public ICollection FindByStatus(PointerStatus status) + { + //TODO: track states in hash table + return _dictionary.Values.Where(x => x.Status == status).ToList(); + } + + public int Count => _dictionary.Count; + public bool IsReadOnly => false; + } \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowDefinition.cs b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowDefinition.cs new file mode 100644 index 0000000..f2a60bc --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowDefinition.cs @@ -0,0 +1,16 @@ +using Coflo.Abstractions.Workflow.Enums; + +namespace Coflo.Abstractions.Workflow.Models; + +public class WorkflowDefinition +{ + public string Id { get; set; } + public int Version { get; set; } + public string Description { get; set; } + public WorkflowStepCollection Steps { get; set; } = new(); + public Type DataType { get; set; } + public WorkflowErrorHandling DefaultErrorBehavior { get; set; } + public Type OnPostMiddlewareError { get; set; } + public Type OnExecuteMiddlewareError { get; set; } + public TimeSpan? DefaultErrorRetryInterval { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowExecutorResult.cs b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowExecutorResult.cs new file mode 100644 index 0000000..b91a1b8 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowExecutorResult.cs @@ -0,0 +1,10 @@ +using Coflo.Abstractions.Events.Models; +using Coflo.Abstractions.Execution.Models; + +namespace Coflo.Abstractions.Workflow.Models; + +public class WorkflowExecutorResult +{ + public List Subscriptions { get; set; } = new(); + public List Errors { get; set; } = new(); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowInstance.cs b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowInstance.cs new file mode 100644 index 0000000..223d1d8 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowInstance.cs @@ -0,0 +1,35 @@ +using Coflo.Abstractions.Workflow.Enums; + +namespace Coflo.Abstractions.Workflow.Models; + +public class WorkflowInstance +{ + public long Id { get; set; } + + public string WorkflowDefinitionId { get; set; } + + public int Version { get; set; } + + public string Description { get; set; } + + public string Reference { get; set; } + + public ExecutionPointerCollection ExecutionPointers { get; set; } = new(); + + public long? NextExecution { get; set; } + + public WorkflowStatus Status { get; set; } + + public object Data { get; set; } + + public DateTime CreateTime { get; set; } + + public DateTime? CompleteTime { get; set; } + + public bool IsBranchComplete(string parentId) + { + return ExecutionPointers + .FindByScope(parentId) + .All(x => x.EndTime != null); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowResult.cs b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowResult.cs new file mode 100644 index 0000000..16c7dd0 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowResult.cs @@ -0,0 +1,10 @@ +using Coflo.Abstractions.Events.Models; +using Coflo.Abstractions.Execution.Models; + +namespace Coflo.Abstractions.Workflow.Models; + +public class WorkflowResult +{ + public List Subscriptions { get; set; } = new(); + public List Errors { get; set; } = new(); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowStep.cs b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowStep.cs new file mode 100644 index 0000000..6775ceb --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowStep.cs @@ -0,0 +1,94 @@ +using System.Linq.Expressions; +using Coflo.Abstractions.Execution.Models; +using Coflo.Abstractions.Expr.Contracts; +using Coflo.Abstractions.Steps.Contracts; +using Coflo.Abstractions.Steps.Models; +using Coflo.Abstractions.Workflow.Contracts; +using Coflo.Abstractions.Workflow.Enums; + +namespace Coflo.Abstractions.Workflow.Models; + +public abstract class WorkflowStep +{ + public abstract Type BodyType { get; } + public virtual int Id { get; set; } + + public virtual string Name { get; set; } + + public virtual string ExternalId { get; set; } + + public virtual List Children { get; set; } = new(); + + public virtual List Outcomes { get; set; } = new(); + + public virtual List Inputs { get; set; } = new(); + + public virtual List Outputs { get; set; } = new(); + + public virtual WorkflowErrorHandling? ErrorBehavior { get; set; } + + public virtual TimeSpan? RetryInterval { get; set; } + + public virtual int? CompensationStepId { get; set; } + + public virtual bool ResumeChildrenAfterCompensation => true; + + public virtual bool RevertChildrenAfterCompensation => false; + + public virtual LambdaExpression CancelCondition { get; set; } + + public bool ProceedOnCancel { get; set; } = false; + + public virtual ExecutionPipelineDirective InitForExecution(WorkflowResult result, + WorkflowDefinition definition, WorkflowInstance workflow, ExecutionPointer executionPointer) + { + return ExecutionPipelineDirective.Next; + } + + public virtual ExecutionPipelineDirective BeforeExecute(WorkflowResult result, + IStepExecutionContext context, ExecutionPointer executionPointer, IStepBody body) + { + return ExecutionPipelineDirective.Next; + } + + public virtual void AfterExecute(WorkflowResult result, IStepExecutionContext context, + ExecutionResult stepResult, ExecutionPointer executionPointer) + { + } + + public virtual void PrimeForRetry(ExecutionPointer pointer) + { + } + + /// + /// Called after every workflow execution round, + /// every exectuon pointer with no end time, even if this step was not executed in this round + /// + /// + /// + /// + /// + public virtual void AfterWorkflowIteration(WorkflowResult result, WorkflowDefinition defintion, + WorkflowInstance workflow, ExecutionPointer executionPointer) + { + } + + public virtual IStepBody ConstructBody(IServiceProvider serviceProvider) + { + IStepBody body = (serviceProvider.GetService(BodyType) as IStepBody); + if (body == null) + { + var stepCtor = BodyType.GetConstructor(new Type[] { }); + if (stepCtor != null) + body = (stepCtor.Invoke(null) as IStepBody); + } + + return body; + } +} + +public class WorkflowStep : WorkflowStep + where TStepBody : IStepBody +{ + public override Type BodyType => typeof(TStepBody); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowStepCollection.cs b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowStepCollection.cs new file mode 100644 index 0000000..305da98 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowStepCollection.cs @@ -0,0 +1,76 @@ +using System.Collections; + +namespace Coflo.Abstractions.Workflow.Models; + +public class WorkflowStepCollection : ICollection +{ + private readonly Dictionary _dictionary = new(); + + public WorkflowStepCollection() + { + } + + public WorkflowStepCollection(int capacity) + { + _dictionary = new Dictionary(capacity); + } + + public WorkflowStepCollection(ICollection steps) + { + foreach (var step in steps) + { + Add(step); + } + } + + public IEnumerator GetEnumerator() + { + return _dictionary.Values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public WorkflowStep FindById(int id) + { + if (!_dictionary.ContainsKey(id)) + return null; + + return _dictionary[id]; + } + + public void Add(WorkflowStep item) + { + _dictionary.Add(item.Id, item); + } + + public void Clear() + { + _dictionary.Clear(); + } + + public bool Contains(WorkflowStep item) + { + return _dictionary.ContainsValue(item); + } + + public void CopyTo(WorkflowStep[] array, int arrayIndex) + { + _dictionary.Values.CopyTo(array, arrayIndex); + } + + public bool Remove(WorkflowStep item) + { + return _dictionary.Remove(item.Id); + } + + public WorkflowStep Find(Predicate match) + { + return _dictionary.Values.FirstOrDefault(x => match(x)); + } + + public int Count => _dictionary.Count; + public bool IsReadOnly => false; +} \ No newline at end of file From 94becaa4f196bff69578c07fa2545cff7a7b46f7 Mon Sep 17 00:00:00 2001 From: Thomas Wright Date: Sun, 16 Apr 2023 15:38:35 +0100 Subject: [PATCH 07/10] Change --- Coflo.sln | 12 +- README.md | 9 +- .../Coflo.Activities.Primitives.csproj | 13 ++ .../Coflo.Activities.Primitives/Control/If.cs | 40 ++++ .../Variables/SetVariable.cs | 36 ++++ .../Attributes/ActivityAttribute.cs | 11 + .../Attributes/ActivityInputAttribute.cs | 7 + .../Commands/StartActivityCommand.cs | 17 ++ .../Activities/Contracts/IActivity.cs | 6 + .../Activities/Contracts/IActivityBody.cs | 8 + .../Contracts/IActivityExecutionContext.cs | 11 + .../Contracts/IActivityExecutionResult.cs | 14 ++ .../Activities/Contracts/IActivityInstance.cs | 14 ++ .../Activities/Enums/ActivityCategory.cs | 17 ++ .../Enums/ActivityInstanceStatus.cs | 10 + .../Activities/Enums/ActivityStatus.cs | 10 + .../Activities/Models/Activity.cs | 15 ++ .../Activities/Models/ActivityDefinition.cs | 16 ++ .../Models/ActivityExecutionContext.cs | 19 ++ .../Models/ActivityExecutionResult.cs | 24 +++ .../Activities/Models/ActivityInputMapping.cs | 10 + .../Models/ActivityOutputMapping.cs | 9 + .../Activities/OutcomeNames.cs | 7 + .../Activities/Services/IActivityExecutor.cs | 12 ++ .../Stores/IActivityDefinitionStore.cs | 8 + .../Coflo.Abstractions.csproj | 1 + .../Connections/Contracts/IConnection.cs | 10 + .../Contracts/IEvaluationContext.cs | 9 + .../Evaluation/Contracts/IEvaluator.cs | 6 + .../Evaluation/Models/EvaluationContext.cs | 16 ++ .../Events/Contracts/IEventPublisher.cs | 8 + .../Coflo.Abstractions/Events/Models/Event.cs | 18 -- .../Events/Models/EventSubscription.cs | 26 --- .../Events/Models/ScheduledCommand.cs | 13 -- .../Execution/Models/ExecutionError.cs | 12 -- .../Execution/Models/ExecutionResult.cs | 104 --------- .../Expr/Contracts/IStepOutcome.cs | 14 -- .../Expr/Models/ExpressionOutcome.cs | 32 --- .../Contracts/IDistributedLockProvider.cs | 22 -- .../Persistence/Contracts/IEventRepository.cs | 19 -- .../Contracts/IPersistenceProvider.cs | 11 - .../Contracts/IScheduledCommandRepository.cs | 12 -- .../Contracts/ISubscriptionRepository.cs | 21 -- .../Contracts/IWorkflowRepository.cs | 24 --- .../CurruptPersistenceDataException.cs | 5 - .../Exceptions/NotFoundException.cs | 15 -- .../Models/ControlPersistenceData.cs | 6 - .../Models/IteratorPersistenceData.cs | 6 - .../Models/SchedulePersistenceData.cs | 6 - .../Contracts/IContainerStepBuilder.cs | 16 -- .../Contracts/IParallelStepBuilder.cs | 12 -- .../Implementation/ActionStepBody.cs | 16 -- .../Primitives/Implementation/Activity.cs | 53 ----- .../Implementation/ContainerStepBody.cs | 8 - .../Implementation/Control/Decide.cs | 15 -- .../Implementation/Control/Delay.cs | 20 -- .../Implementation/Control/EndStep.cs | 15 -- .../Primitives/Implementation/Control/If.cs | 36 ---- .../Implementation/Control/OutcomeSwitch.cs | 41 ---- .../Implementation/Control/WaitFor.cs | 34 --- .../Primitives/Implementation/Control/When.cs | 47 ----- .../Implementation/InlineStepBody.cs | 21 -- .../Implementation/Iteration/Foreach.cs | 53 ----- .../Implementation/Iteration/Sequence.cs | 23 -- .../Implementation/Iteration/While.cs | 31 --- .../Implementation/Recurring/Recur.cs | 26 --- .../Implementation/Recurring/Schedule.cs | 30 --- .../Implementation/SagaContainer.cs | 17 -- .../Implementation/SubWorkflowStepBody.cs | 14 -- .../Implementation/WorkflowStepInline.cs | 15 -- .../Primitives/Models/ActivityResult.cs | 9 - .../Steps/Attributes/StepAttribute.cs | 7 - .../Steps/Contracts/IActivityController.cs | 11 - .../Steps/Contracts/IStepBody.cs | 9 - .../Steps/Contracts/IStepBuilder.cs | 153 -------------- .../Steps/Contracts/IStepExecutionContext.cs | 18 -- .../Steps/Contracts/IStepParameter.cs | 7 - .../Exceptions/ActivityFailedException.cs | 8 - .../Steps/Models/ActionStepBody.cs | 16 -- .../Steps/Models/ActivityToken.cs | 36 ---- .../Steps/Models/InlineStepBody.cs | 20 -- .../Steps/Models/PendingActivity.cs | 9 - .../Steps/Models/StepBody.cs | 42 ---- .../Steps/Models/StepBodyAsync.cs | 9 - .../Steps/Models/WorkflowStepDelegate.cs | 8 - .../Contracts/IVariableCollection.cs | 8 + .../Variables/Enums/VariableType.cs | 9 + .../Variables/Model/VariableCollection.cs | 48 +++++ .../Variables/Model/VariableDefinition.cs | 52 +++++ .../Variables/Model/VariableInstance.cs | 61 ++++++ .../Workflow/Contracts/IWorkflow.cs | 14 -- .../Workflow/Contracts/IWorkflowBuilder.cs | 37 ---- .../Workflow/Contracts/IWorkflowController.cs | 38 ---- .../Workflow/Contracts/IWorkflowModifier.cs | 197 ------------------ .../Enums/ExecutionPipelineDirective.cs | 8 - .../Workflow/Enums/PointerStatus.cs | 15 -- .../Workflow/Enums/WorkflowErrorHandling.cs | 9 - .../Workflow/Enums/WorkflowStatus.cs | 9 - .../WorkflowDefinitionLoadException.cs | 9 - .../Exceptions/WorkflowLockedException.cs | 9 - .../WorkflowNotRegisteredException.cs | 6 - .../Workflow/Models/ExecutionPointer.cs | 52 ----- .../Models/ExecutionPointerCollection.cs | 105 ---------- .../Workflow/Models/WorkflowDefinition.cs | 16 -- .../Workflow/Models/WorkflowExecutorResult.cs | 10 - .../Workflow/Models/WorkflowInstance.cs | 35 ---- .../Workflow/Models/WorkflowResult.cs | 10 - .../Workflow/Models/WorkflowStep.cs | 94 --------- .../Workflow/Models/WorkflowStepCollection.cs | 76 ------- .../Commands/StartWorkflowExecutionCommand.cs | 13 ++ .../Contracts/IWorkflowDefinition.cs | 12 ++ .../Contracts/IWorkflowDefinitionVersion.cs | 16 ++ .../Workflows/Contracts/IWorkflowInstance.cs | 20 ++ .../Workflows/Enums/WorkflowStatus.cs | 10 + .../Workflows/Models/WorkflowDefinition.cs | 13 ++ .../Models/WorkflowDefinitionVersion.cs | 17 ++ .../Workflows/Models/WorkflowInstance.cs | 19 ++ .../Workflows/Models/WorkflowLogEntry.cs | 20 ++ .../ActivityCompletedNotification.cs | 12 ++ .../ActivityFailedNotification.cs | 8 + .../Workflows/Services/IWorkflowExecutor.cs | 17 ++ .../Stores/IWorkflowDefinitionStore.cs | 16 ++ .../Stores/IWorkflowInstanceStore.cs | 13 ++ .../Coflo.Core.csproj} | 0 .../core/Coflo.SDK/Activities/ActivityBase.cs | 8 - .../Coflo.Core.Snowflake.csproj | 4 +- .../Generators/IIdGenerator.cs | 4 +- .../Generators/IdGenerator.cs | 62 +++--- .../Models/SnowflakeId.cs | 2 +- .../Coflo.Hosting.Orchestrator.csproj | 4 + .../Services/WorkflowExecutor.cs | 134 ++++++++++++ source/hosting/Coflo.Hosting.Worker/Class1.cs | 5 - .../Coflo.Hosting.Worker.csproj | 4 + .../Commands/StartActivityCommandHandler.cs | 109 ++++++++++ .../Class1.cs | 5 - .../Coflo.Core.Snowflake.Tests.csproj | 12 +- .../IdGeneratorTests.cs | 19 +- 137 files changed, 1078 insertions(+), 2103 deletions(-) create mode 100644 source/activities/Coflo.Activities.Primitives/Coflo.Activities.Primitives.csproj create mode 100644 source/activities/Coflo.Activities.Primitives/Control/If.cs create mode 100644 source/activities/Coflo.Activities.Primitives/Variables/SetVariable.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Attributes/ActivityAttribute.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Attributes/ActivityInputAttribute.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Commands/StartActivityCommand.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Contracts/IActivity.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Contracts/IActivityBody.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Contracts/IActivityExecutionContext.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Contracts/IActivityExecutionResult.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Contracts/IActivityInstance.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Enums/ActivityCategory.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Enums/ActivityInstanceStatus.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Enums/ActivityStatus.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Models/Activity.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Models/ActivityDefinition.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Models/ActivityExecutionContext.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Models/ActivityExecutionResult.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Models/ActivityInputMapping.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Models/ActivityOutputMapping.cs create mode 100644 source/core/Coflo.Abstractions/Activities/OutcomeNames.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Services/IActivityExecutor.cs create mode 100644 source/core/Coflo.Abstractions/Activities/Stores/IActivityDefinitionStore.cs create mode 100644 source/core/Coflo.Abstractions/Connections/Contracts/IConnection.cs create mode 100644 source/core/Coflo.Abstractions/Evaluation/Contracts/IEvaluationContext.cs create mode 100644 source/core/Coflo.Abstractions/Evaluation/Contracts/IEvaluator.cs create mode 100644 source/core/Coflo.Abstractions/Evaluation/Models/EvaluationContext.cs create mode 100644 source/core/Coflo.Abstractions/Events/Contracts/IEventPublisher.cs delete mode 100644 source/core/Coflo.Abstractions/Events/Models/Event.cs delete mode 100644 source/core/Coflo.Abstractions/Events/Models/EventSubscription.cs delete mode 100644 source/core/Coflo.Abstractions/Events/Models/ScheduledCommand.cs delete mode 100644 source/core/Coflo.Abstractions/Execution/Models/ExecutionError.cs delete mode 100644 source/core/Coflo.Abstractions/Execution/Models/ExecutionResult.cs delete mode 100644 source/core/Coflo.Abstractions/Expr/Contracts/IStepOutcome.cs delete mode 100644 source/core/Coflo.Abstractions/Expr/Models/ExpressionOutcome.cs delete mode 100644 source/core/Coflo.Abstractions/Locking/Contracts/IDistributedLockProvider.cs delete mode 100644 source/core/Coflo.Abstractions/Persistence/Contracts/IEventRepository.cs delete mode 100644 source/core/Coflo.Abstractions/Persistence/Contracts/IPersistenceProvider.cs delete mode 100644 source/core/Coflo.Abstractions/Persistence/Contracts/IScheduledCommandRepository.cs delete mode 100644 source/core/Coflo.Abstractions/Persistence/Contracts/ISubscriptionRepository.cs delete mode 100644 source/core/Coflo.Abstractions/Persistence/Contracts/IWorkflowRepository.cs delete mode 100644 source/core/Coflo.Abstractions/Persistence/Exceptions/CurruptPersistenceDataException.cs delete mode 100644 source/core/Coflo.Abstractions/Persistence/Exceptions/NotFoundException.cs delete mode 100644 source/core/Coflo.Abstractions/Persistence/Models/ControlPersistenceData.cs delete mode 100644 source/core/Coflo.Abstractions/Persistence/Models/IteratorPersistenceData.cs delete mode 100644 source/core/Coflo.Abstractions/Persistence/Models/SchedulePersistenceData.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Contracts/IContainerStepBuilder.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Contracts/IParallelStepBuilder.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/ActionStepBody.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Activity.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/ContainerStepBody.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Control/Decide.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Control/Delay.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Control/EndStep.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Control/If.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Control/OutcomeSwitch.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Control/WaitFor.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Control/When.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/InlineStepBody.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/Foreach.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/Sequence.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/While.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Recurring/Recur.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/Recurring/Schedule.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/SagaContainer.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/SubWorkflowStepBody.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Implementation/WorkflowStepInline.cs delete mode 100644 source/core/Coflo.Abstractions/Primitives/Models/ActivityResult.cs delete mode 100644 source/core/Coflo.Abstractions/Steps/Attributes/StepAttribute.cs delete mode 100644 source/core/Coflo.Abstractions/Steps/Contracts/IActivityController.cs delete mode 100644 source/core/Coflo.Abstractions/Steps/Contracts/IStepBody.cs delete mode 100644 source/core/Coflo.Abstractions/Steps/Contracts/IStepBuilder.cs delete mode 100644 source/core/Coflo.Abstractions/Steps/Contracts/IStepExecutionContext.cs delete mode 100644 source/core/Coflo.Abstractions/Steps/Contracts/IStepParameter.cs delete mode 100644 source/core/Coflo.Abstractions/Steps/Exceptions/ActivityFailedException.cs delete mode 100644 source/core/Coflo.Abstractions/Steps/Models/ActionStepBody.cs delete mode 100644 source/core/Coflo.Abstractions/Steps/Models/ActivityToken.cs delete mode 100644 source/core/Coflo.Abstractions/Steps/Models/InlineStepBody.cs delete mode 100644 source/core/Coflo.Abstractions/Steps/Models/PendingActivity.cs delete mode 100644 source/core/Coflo.Abstractions/Steps/Models/StepBody.cs delete mode 100644 source/core/Coflo.Abstractions/Steps/Models/StepBodyAsync.cs delete mode 100644 source/core/Coflo.Abstractions/Steps/Models/WorkflowStepDelegate.cs create mode 100644 source/core/Coflo.Abstractions/Variables/Contracts/IVariableCollection.cs create mode 100644 source/core/Coflo.Abstractions/Variables/Enums/VariableType.cs create mode 100644 source/core/Coflo.Abstractions/Variables/Model/VariableCollection.cs create mode 100644 source/core/Coflo.Abstractions/Variables/Model/VariableDefinition.cs create mode 100644 source/core/Coflo.Abstractions/Variables/Model/VariableInstance.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflow.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowBuilder.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowController.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowModifier.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Enums/ExecutionPipelineDirective.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Enums/PointerStatus.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Enums/WorkflowErrorHandling.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Enums/WorkflowStatus.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowDefinitionLoadException.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowLockedException.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowNotRegisteredException.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Models/ExecutionPointer.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Models/ExecutionPointerCollection.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Models/WorkflowDefinition.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Models/WorkflowExecutorResult.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Models/WorkflowInstance.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Models/WorkflowResult.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Models/WorkflowStep.cs delete mode 100644 source/core/Coflo.Abstractions/Workflow/Models/WorkflowStepCollection.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Commands/StartWorkflowExecutionCommand.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Contracts/IWorkflowDefinition.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Contracts/IWorkflowDefinitionVersion.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Contracts/IWorkflowInstance.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Enums/WorkflowStatus.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinition.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinitionVersion.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Models/WorkflowInstance.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Models/WorkflowLogEntry.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Notifications/ActivityCompletedNotification.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Notifications/ActivityFailedNotification.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Services/IWorkflowExecutor.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Stores/IWorkflowDefinitionStore.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Stores/IWorkflowInstanceStore.cs rename source/core/{Coflo.SDK/Coflo.SDK.csproj => Coflo.Core/Coflo.Core.csproj} (100%) delete mode 100644 source/core/Coflo.SDK/Activities/ActivityBase.cs create mode 100644 source/hosting/Coflo.Hosting.Orchestrator/Services/WorkflowExecutor.cs delete mode 100644 source/hosting/Coflo.Hosting.Worker/Class1.cs create mode 100644 source/hosting/Coflo.Hosting.Worker/Commands/StartActivityCommandHandler.cs delete mode 100644 source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Class1.cs diff --git a/Coflo.sln b/Coflo.sln index 847b1e8..901276b 100644 --- a/Coflo.sln +++ b/Coflo.sln @@ -36,7 +36,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Hosting.Api.Rest", "s EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Hosting.Api.GRPC", "source\hosting\Coflo.Hosting.Api.GRPC\Coflo.Hosting.Api.GRPC.csproj", "{47131B79-A7E2-4A46-B9E8-98DDC0C20FCE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.SDK", "source\core\Coflo.SDK\Coflo.SDK.csproj", "{2569CD34-41EF-42A3-AE63-BE01A0124D44}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Core", "source\core\Coflo.Core\Coflo.Core.csproj", "{2569CD34-41EF-42A3-AE63-BE01A0124D44}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1230DC9F-E226-4466-B0B1-99B52B66232C}" EndProject @@ -50,6 +50,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Core.Snowflake", "sou EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Core.Snowflake.Tests", "tests\core\Coflo.Core.Snowflake.Tests\Coflo.Core.Snowflake.Tests.csproj", "{75110834-EBD6-450A-9092-B619CE02FEAC}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "activities", "activities", "{A4E3E7F1-AA46-4934-90BA-598CE381F0AA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Activities.Primitives", "source\activities\Coflo.Activities.Primitives\Coflo.Activities.Primitives.csproj", "{CDA31FB1-04BA-4A5D-9282-7EBAB755BE7E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -75,6 +79,8 @@ Global {3009ACF8-F2DC-492C-8E41-7E36AB3A7A6C} = {DEAF9660-31F2-4E6B-9A77-93BA08D7D122} {2FF4D33E-2964-41B3-B0B2-3C9D70616FBA} = {C46B2D26-B277-4EE0-9497-0473D52EE7A9} {75110834-EBD6-450A-9092-B619CE02FEAC} = {FA4C5E4B-B42D-4BC5-9F26-FD32CD88F75F} + {A4E3E7F1-AA46-4934-90BA-598CE381F0AA} = {F514E571-76C3-477F-86F0-3C8CCF512015} + {CDA31FB1-04BA-4A5D-9282-7EBAB755BE7E} = {A4E3E7F1-AA46-4934-90BA-598CE381F0AA} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {28DE14D7-4F2E-40D9-B847-69866240023D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -125,5 +131,9 @@ Global {75110834-EBD6-450A-9092-B619CE02FEAC}.Debug|Any CPU.Build.0 = Debug|Any CPU {75110834-EBD6-450A-9092-B619CE02FEAC}.Release|Any CPU.ActiveCfg = Release|Any CPU {75110834-EBD6-450A-9092-B619CE02FEAC}.Release|Any CPU.Build.0 = Release|Any CPU + {CDA31FB1-04BA-4A5D-9282-7EBAB755BE7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CDA31FB1-04BA-4A5D-9282-7EBAB755BE7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CDA31FB1-04BA-4A5D-9282-7EBAB755BE7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CDA31FB1-04BA-4A5D-9282-7EBAB755BE7E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/README.md b/README.md index 1250ff1..0dadc8b 100644 --- a/README.md +++ b/README.md @@ -5,26 +5,19 @@ ![Discord](https://img.shields.io/discord/1096095159860600832?label=Discord&logo=Discord&style=for-the-badge) Coflo is an in development, distrobuted workflow engine. + ## Roadmap - Additional browser support - Add more integrations - ## Run Locally - - - ## Features - - - ## Acknowledgements - ## Appendix ## Contributing diff --git a/source/activities/Coflo.Activities.Primitives/Coflo.Activities.Primitives.csproj b/source/activities/Coflo.Activities.Primitives/Coflo.Activities.Primitives.csproj new file mode 100644 index 0000000..2ef04c3 --- /dev/null +++ b/source/activities/Coflo.Activities.Primitives/Coflo.Activities.Primitives.csproj @@ -0,0 +1,13 @@ + + + + net7.0 + enable + enable + + + + + + + diff --git a/source/activities/Coflo.Activities.Primitives/Control/If.cs b/source/activities/Coflo.Activities.Primitives/Control/If.cs new file mode 100644 index 0000000..568a613 --- /dev/null +++ b/source/activities/Coflo.Activities.Primitives/Control/If.cs @@ -0,0 +1,40 @@ +using Coflo.Abstractions.Activities.Attributes; +using Coflo.Abstractions.Activities.Contracts; +using Coflo.Abstractions.Activities.Enums; +using Coflo.Abstractions.Activities.Models; +using Coflo.Abstractions.Evaluation.Contracts; +using Coflo.Abstractions.Evaluation.Models; +using Coflo.Abstractions.Variables.Model; + +namespace Coflo.Activities.Primitives.Control; + +[Activity(DisplayName = "If", Outcomes = new[] { "True", "False" }, Category = ActivityCategory.Decision)] +public class If : Activity +{ + private readonly IEvaluator _evaluator; + + [ActivityInput(DisplayName = "Condition")] + public string Condition { get; set; } = "false"; + + public If(IEvaluator evaluator) : base("IF") + { + _evaluator = evaluator; + } + + public override async Task ExecuteAsync(ActivityExecutionContext context) + { + try + { + var evaluationContext = new EvaluationContext(context.Variables, Condition); + var result = await _evaluator.EvaluateAsync(evaluationContext); + + return new ActivityExecutionResult(result ? "True" : "False", true, ActivityStatus.Completed, + new VariableCollection(), context.WorkflowInstanceId, context.ActivityInstanceId); + } + catch (Exception e) + { + return new ActivityExecutionResult(string.Empty, false, ActivityStatus.Faulted, + new VariableCollection(), context.WorkflowInstanceId, context.ActivityInstanceId); + } + } +} \ No newline at end of file diff --git a/source/activities/Coflo.Activities.Primitives/Variables/SetVariable.cs b/source/activities/Coflo.Activities.Primitives/Variables/SetVariable.cs new file mode 100644 index 0000000..7930f8a --- /dev/null +++ b/source/activities/Coflo.Activities.Primitives/Variables/SetVariable.cs @@ -0,0 +1,36 @@ +using Coflo.Abstractions.Activities.Attributes; +using Coflo.Abstractions.Activities.Contracts; +using Coflo.Abstractions.Activities.Enums; +using Coflo.Abstractions.Activities.Models; +using Coflo.Abstractions.Variables.Model; + +namespace Coflo.Activities.Primitives.Variables; + +[Activity(DisplayName = "Set Variable", Category = ActivityCategory.Workflow, Outcomes = new[] { Done, Failed })] +public class SetVariable : Activity +{ + private const string Done = "Done"; + private const string Failed = "Failed"; + + [ActivityInput(DisplayName = "Variable")] + public VariableDefinition Variable { get; set; } + + [ActivityInput(DisplayName = "Value")] + public object? Value { get; set; } + + public SetVariable() : base("SET_VARIABLE") + { + } + + public override Task ExecuteAsync(ActivityExecutionContext context) + { + var variable = new VariableInstance(Variable); + variable.SetValue(Value); + var isUpdated = context.Variables.AddOrUpdate(variable); + + var result = new ActivityExecutionResult(isUpdated ? Done : Failed, isUpdated, ActivityStatus.Completed, context.Variables, + context.WorkflowInstanceId, context.ActivityInstanceId); + + return Task.FromResult(result as IActivityExecutionResult); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Attributes/ActivityAttribute.cs b/source/core/Coflo.Abstractions/Activities/Attributes/ActivityAttribute.cs new file mode 100644 index 0000000..dc0fcd0 --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Attributes/ActivityAttribute.cs @@ -0,0 +1,11 @@ +using Coflo.Abstractions.Activities.Enums; + +namespace Coflo.Abstractions.Activities.Attributes; + +public class ActivityAttribute : Attribute +{ + public string DisplayName { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public ActivityCategory Category { get; set; } = ActivityCategory.None; + public string[] Outcomes { get; set; } = { OutcomeNames.Failure, OutcomeNames.Success }; +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Attributes/ActivityInputAttribute.cs b/source/core/Coflo.Abstractions/Activities/Attributes/ActivityInputAttribute.cs new file mode 100644 index 0000000..07b7407 --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Attributes/ActivityInputAttribute.cs @@ -0,0 +1,7 @@ +namespace Coflo.Abstractions.Activities.Attributes; + +[AttributeUsage(AttributeTargets.Property)] +public class ActivityInputAttribute : Attribute +{ + public string DisplayName { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Commands/StartActivityCommand.cs b/source/core/Coflo.Abstractions/Activities/Commands/StartActivityCommand.cs new file mode 100644 index 0000000..3af74db --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Commands/StartActivityCommand.cs @@ -0,0 +1,17 @@ +using Coflo.Abstractions.Variables.Model; +using Mediator; + +namespace Coflo.Abstractions.Activities.Commands; + +public class StartActivityCommand : ICommand +{ + public long WorkflowInstanceId { get; set; } + public long ActivityDefinitionId { get; set; } + public IVariableCollection VariableCollection { get; set; } + public StartActivityCommand(long workflowInstanceId, long activityDefinitionId, IVariableCollection variableCollection) + { + WorkflowInstanceId = workflowInstanceId; + ActivityDefinitionId = activityDefinitionId; + VariableCollection = variableCollection; + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Contracts/IActivity.cs b/source/core/Coflo.Abstractions/Activities/Contracts/IActivity.cs new file mode 100644 index 0000000..07edc11 --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Contracts/IActivity.cs @@ -0,0 +1,6 @@ +namespace Coflo.Abstractions.Activities.Contracts; + +public interface IActivity : IActivityBody +{ + public string Name { get; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Contracts/IActivityBody.cs b/source/core/Coflo.Abstractions/Activities/Contracts/IActivityBody.cs new file mode 100644 index 0000000..4422871 --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Contracts/IActivityBody.cs @@ -0,0 +1,8 @@ +using Coflo.Abstractions.Activities.Models; + +namespace Coflo.Abstractions.Activities.Contracts; + +public interface IActivityBody +{ + Task ExecuteAsync(ActivityExecutionContext context); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Contracts/IActivityExecutionContext.cs b/source/core/Coflo.Abstractions/Activities/Contracts/IActivityExecutionContext.cs new file mode 100644 index 0000000..0340784 --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Contracts/IActivityExecutionContext.cs @@ -0,0 +1,11 @@ +using Coflo.Abstractions.Variables.Model; + +namespace Coflo.Abstractions.Activities.Contracts; + +public interface IActivityExecutionContext +{ + public VariableCollection Variables { get; set; } + public long WorkflowInstanceId { get; set; } + public long ActivityInstanceId { get; set; } + public string ActivityName { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Contracts/IActivityExecutionResult.cs b/source/core/Coflo.Abstractions/Activities/Contracts/IActivityExecutionResult.cs new file mode 100644 index 0000000..a3eaeec --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Contracts/IActivityExecutionResult.cs @@ -0,0 +1,14 @@ +using Coflo.Abstractions.Activities.Enums; +using Coflo.Abstractions.Variables.Model; + +namespace Coflo.Abstractions.Activities.Contracts; + +public interface IActivityExecutionResult +{ + public bool IsSuccessful { get; set; } + public ActivityStatus Status { get; set; } + public IVariableCollection VariableCollection { get; set; } + public long WorkflowInstanceId { get; set; } + public long ActivityInstanceId { get; set; } + public string Outcome { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Contracts/IActivityInstance.cs b/source/core/Coflo.Abstractions/Activities/Contracts/IActivityInstance.cs new file mode 100644 index 0000000..d0bad0e --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Contracts/IActivityInstance.cs @@ -0,0 +1,14 @@ +using Coflo.Abstractions.Activities.Enums; +using Coflo.Abstractions.Variables.Model; + +namespace Coflo.Abstractions.Activities.Contracts; + +public interface IActivityInstance +{ + public long WorkflowInstanceId { get; set; } + public long ActivityInstanceId { get; set; } + public string ActivityName { get; set; } + public ActivityInstanceStatus Status { get; set; } + + public VariableCollection VariableCollection { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Enums/ActivityCategory.cs b/source/core/Coflo.Abstractions/Activities/Enums/ActivityCategory.cs new file mode 100644 index 0000000..b5ec622 --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Enums/ActivityCategory.cs @@ -0,0 +1,17 @@ +namespace Coflo.Abstractions.Activities.Enums; + +public enum ActivityCategory +{ + Action, + Decision, + Loop, + Scope, + Sequence, + Switch, + Terminate, + Throw, + Try, + Wait, + Workflow, + None +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Enums/ActivityInstanceStatus.cs b/source/core/Coflo.Abstractions/Activities/Enums/ActivityInstanceStatus.cs new file mode 100644 index 0000000..fc5d5cb --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Enums/ActivityInstanceStatus.cs @@ -0,0 +1,10 @@ +namespace Coflo.Abstractions.Activities.Enums; + +public enum ActivityInstanceStatus +{ + Created, + Running, + Completed, + Faulted, + Canceled +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Enums/ActivityStatus.cs b/source/core/Coflo.Abstractions/Activities/Enums/ActivityStatus.cs new file mode 100644 index 0000000..df2a916 --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Enums/ActivityStatus.cs @@ -0,0 +1,10 @@ +namespace Coflo.Abstractions.Activities.Enums; + +public enum ActivityStatus +{ + Waiting, + Executing, + Completed, + Faulted, + Canceled +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Models/Activity.cs b/source/core/Coflo.Abstractions/Activities/Models/Activity.cs new file mode 100644 index 0000000..570fc66 --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Models/Activity.cs @@ -0,0 +1,15 @@ +using Coflo.Abstractions.Activities.Contracts; + +namespace Coflo.Abstractions.Activities.Models; + +public abstract class Activity : IActivity +{ + protected Activity(string name) + { + Name = name; + } + + public string Name { get; } + + public abstract Task ExecuteAsync(ActivityExecutionContext context); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Models/ActivityDefinition.cs b/source/core/Coflo.Abstractions/Activities/Models/ActivityDefinition.cs new file mode 100644 index 0000000..fc9f6b7 --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Models/ActivityDefinition.cs @@ -0,0 +1,16 @@ +using Coflo.Abstractions.Connections.Contracts; + +namespace Coflo.Abstractions.Activities.Models; + +public class ActivityDefinition +{ + public long WorkflowDefinitionId { get; set; } + public long WorkflowVersionId { get; set; } + public long ActivityDefinitionId { get; set; } + public string DisplayName { get; set; } + public string ActivityName { get; set; } + + public ICollection InputMappings { get; set; } = new List(); + public ICollection InputNode { get; set; } = new List(); + public ICollection OutputNodes { get; set; } = new List(); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Models/ActivityExecutionContext.cs b/source/core/Coflo.Abstractions/Activities/Models/ActivityExecutionContext.cs new file mode 100644 index 0000000..fa7726a --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Models/ActivityExecutionContext.cs @@ -0,0 +1,19 @@ +using Coflo.Abstractions.Variables.Model; + +namespace Coflo.Abstractions.Activities.Models; + +public class ActivityExecutionContext +{ + public ActivityExecutionContext(IVariableCollection variables, long workflowInstanceId, long activityInstanceId, string activityName) + { + Variables = variables; + WorkflowInstanceId = workflowInstanceId; + ActivityInstanceId = activityInstanceId; + ActivityName = activityName; + } + + public IVariableCollection Variables { get; set; } + public long WorkflowInstanceId { get; set; } + public long ActivityInstanceId { get; set; } + public string ActivityName { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Models/ActivityExecutionResult.cs b/source/core/Coflo.Abstractions/Activities/Models/ActivityExecutionResult.cs new file mode 100644 index 0000000..d5bc513 --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Models/ActivityExecutionResult.cs @@ -0,0 +1,24 @@ +using Coflo.Abstractions.Activities.Contracts; +using Coflo.Abstractions.Activities.Enums; +using Coflo.Abstractions.Variables.Model; + +namespace Coflo.Abstractions.Activities.Models; + +public class ActivityExecutionResult : IActivityExecutionResult +{ + public bool IsSuccessful { get; set; } + public ActivityStatus Status { get; set; } + public IVariableCollection VariableCollection { get; set; } + public long WorkflowInstanceId { get; set; } + public long ActivityInstanceId { get; set; } + public string Outcome { get; set; } + + public ActivityExecutionResult(string outcome, bool isSuccessful, ActivityStatus status, + IVariableCollection variableCollection, long workflowInstanceId, long activityInstanceId) + { + Outcome = outcome; + IsSuccessful = isSuccessful; + Status = status; + VariableCollection = variableCollection; + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Models/ActivityInputMapping.cs b/source/core/Coflo.Abstractions/Activities/Models/ActivityInputMapping.cs new file mode 100644 index 0000000..224887d --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Models/ActivityInputMapping.cs @@ -0,0 +1,10 @@ +using Coflo.Abstractions.Variables.Model; + +namespace Coflo.Abstractions.Activities.Models; + +public class ActivityInputMapping +{ + public string ActivityInputField { get; set; } + public VariableDefinition? VariableDefinition { get; set; } + public object? Literal { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Models/ActivityOutputMapping.cs b/source/core/Coflo.Abstractions/Activities/Models/ActivityOutputMapping.cs new file mode 100644 index 0000000..7c3a133 --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Models/ActivityOutputMapping.cs @@ -0,0 +1,9 @@ +using Coflo.Abstractions.Variables.Model; + +namespace Coflo.Abstractions.Activities.Models; + +public class ActivityOutputMapping +{ + public string ActivityOutputField { get; set; } + public VariableDefinition Variable { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/OutcomeNames.cs b/source/core/Coflo.Abstractions/Activities/OutcomeNames.cs new file mode 100644 index 0000000..dbf5109 --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/OutcomeNames.cs @@ -0,0 +1,7 @@ +namespace Coflo.Abstractions.Activities; + +public class OutcomeNames +{ + public const string Success = "Success"; + public const string Failure = "Failure"; +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Services/IActivityExecutor.cs b/source/core/Coflo.Abstractions/Activities/Services/IActivityExecutor.cs new file mode 100644 index 0000000..a8620fe --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Services/IActivityExecutor.cs @@ -0,0 +1,12 @@ +using Coflo.Abstractions.Activities.Contracts; +using Coflo.Abstractions.Connections.Contracts; +using Coflo.Abstractions.Variables.Model; +using Coflo.Abstractions.Workflows.Contracts; +using Coflo.Abstractions.Workflows.Stores; + +namespace Coflo.Abstractions.Activities.Services; + +public interface IActivityExecutor +{ + Task ExecuteAsync(IActivity activity, VariableCollection variables, long workflowInstanceId, long nodeId); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Stores/IActivityDefinitionStore.cs b/source/core/Coflo.Abstractions/Activities/Stores/IActivityDefinitionStore.cs new file mode 100644 index 0000000..20dc52e --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Stores/IActivityDefinitionStore.cs @@ -0,0 +1,8 @@ +using Coflo.Abstractions.Activities.Models; + +namespace Coflo.Abstractions.Activities.Stores; + +public interface IActivityDefinitionStore +{ + Task GetActivityDefinitionAsync(long activityDefinitionId); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj b/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj index 77bf108..c9d98dc 100644 --- a/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj +++ b/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj @@ -7,6 +7,7 @@ + diff --git a/source/core/Coflo.Abstractions/Connections/Contracts/IConnection.cs b/source/core/Coflo.Abstractions/Connections/Contracts/IConnection.cs new file mode 100644 index 0000000..a7e32a2 --- /dev/null +++ b/source/core/Coflo.Abstractions/Connections/Contracts/IConnection.cs @@ -0,0 +1,10 @@ +using Coflo.Abstractions.Activities; + +namespace Coflo.Abstractions.Connections.Contracts; + +public class IConnection +{ + public long ActivityId { get; set; } + public long TargetActivityId { get; set; } + public string Outcome { get; set; } = OutcomeNames.Success; +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Evaluation/Contracts/IEvaluationContext.cs b/source/core/Coflo.Abstractions/Evaluation/Contracts/IEvaluationContext.cs new file mode 100644 index 0000000..a6bd0c0 --- /dev/null +++ b/source/core/Coflo.Abstractions/Evaluation/Contracts/IEvaluationContext.cs @@ -0,0 +1,9 @@ +using Coflo.Abstractions.Variables.Model; + +namespace Coflo.Abstractions.Evaluation.Contracts; + +public interface IEvaluationContext +{ + VariableCollection Variables { get; set; } + public string Condition { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Evaluation/Contracts/IEvaluator.cs b/source/core/Coflo.Abstractions/Evaluation/Contracts/IEvaluator.cs new file mode 100644 index 0000000..7485a88 --- /dev/null +++ b/source/core/Coflo.Abstractions/Evaluation/Contracts/IEvaluator.cs @@ -0,0 +1,6 @@ +namespace Coflo.Abstractions.Evaluation.Contracts; + +public interface IEvaluator +{ + Task EvaluateAsync(IEvaluationContext context); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Evaluation/Models/EvaluationContext.cs b/source/core/Coflo.Abstractions/Evaluation/Models/EvaluationContext.cs new file mode 100644 index 0000000..fc2a7e8 --- /dev/null +++ b/source/core/Coflo.Abstractions/Evaluation/Models/EvaluationContext.cs @@ -0,0 +1,16 @@ +using Coflo.Abstractions.Evaluation.Contracts; +using Coflo.Abstractions.Variables.Model; + +namespace Coflo.Abstractions.Evaluation.Models; + +public class EvaluationContext : IEvaluationContext +{ + public VariableCollection Variables { get; set; } + public string Condition { get; set; } + + public EvaluationContext(VariableCollection variables, string condition) + { + Variables = variables; + Condition = condition; + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Events/Contracts/IEventPublisher.cs b/source/core/Coflo.Abstractions/Events/Contracts/IEventPublisher.cs new file mode 100644 index 0000000..2cae0f9 --- /dev/null +++ b/source/core/Coflo.Abstractions/Events/Contracts/IEventPublisher.cs @@ -0,0 +1,8 @@ +using Mediator; + +namespace Coflo.Abstractions.Events; + +public interface IEventPublisher +{ + Task PublishAsync(T @event) where T : IMessage; +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Events/Models/Event.cs b/source/core/Coflo.Abstractions/Events/Models/Event.cs deleted file mode 100644 index 0a7834e..0000000 --- a/source/core/Coflo.Abstractions/Events/Models/Event.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Coflo.Abstractions.Events.Models; - -public class Event -{ - public long Id { get; set; } - - public string EventName { get; set; } - - public string EventKey { get; set; } - - public object EventData { get; set; } - - public DateTime EventTime { get; set; } - - public bool IsProcessed { get; set; } - - public const string EventTypeActivity = "WorkflowCore.Activity"; -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Events/Models/EventSubscription.cs b/source/core/Coflo.Abstractions/Events/Models/EventSubscription.cs deleted file mode 100644 index 816e048..0000000 --- a/source/core/Coflo.Abstractions/Events/Models/EventSubscription.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Coflo.Abstractions.Events.Models; - -public class EventSubscription -{ - public long Id { get; set; } - - public long WorkflowId { get; set; } - - public int StepId { get; set; } - - public string ExecutionPointerId { get; set; } - - public string EventName { get; set; } - - public string EventKey { get; set; } - - public DateTime SubscribeAsOf { get; set; } - - public object SubscriptionData { get; set; } - - public string ExternalToken { get; set; } - - public string ExternalWorkerId { get; set; } - - public DateTime? ExternalTokenExpiry { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Events/Models/ScheduledCommand.cs b/source/core/Coflo.Abstractions/Events/Models/ScheduledCommand.cs deleted file mode 100644 index f86ea8e..0000000 --- a/source/core/Coflo.Abstractions/Events/Models/ScheduledCommand.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Mediator; - -namespace Coflo.Abstractions.Events.Models; - -public class ScheduledCommand : ICommand -{ - public const string ProcessWorkflow = "ProcessWorkflow"; - public const string ProcessEvent = "ProcessEvent"; - - public string CommandName { get; set; } - public string Data { get; set; } - public long ExecuteTime { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Execution/Models/ExecutionError.cs b/source/core/Coflo.Abstractions/Execution/Models/ExecutionError.cs deleted file mode 100644 index 029e3ad..0000000 --- a/source/core/Coflo.Abstractions/Execution/Models/ExecutionError.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Coflo.Abstractions.Execution.Models; - -public class ExecutionError -{ - public DateTime ErrorTime { get; set; } - - public long WorkflowId { get; set; } - - public long ExecutionPointerId { get; set; } - - public string Message { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Execution/Models/ExecutionResult.cs b/source/core/Coflo.Abstractions/Execution/Models/ExecutionResult.cs deleted file mode 100644 index 0e20c32..0000000 --- a/source/core/Coflo.Abstractions/Execution/Models/ExecutionResult.cs +++ /dev/null @@ -1,104 +0,0 @@ -using Coflo.Abstractions.Events.Models; - -namespace Coflo.Abstractions.Execution.Models; - -public class ExecutionResult -{ - public bool Proceed { get; set; } - - public object OutcomeValue { get; set; } - - public TimeSpan? SleepFor { get; set; } - - public object PersistenceData { get; set; } - - public string EventName { get; set; } - - public string EventKey { get; set; } - - public DateTime EventAsOf { get; set; } - - public object SubscriptionData { get; set; } - - public List BranchValues { get; set; } = new(); - - public ExecutionResult() - { - } - - public ExecutionResult(object outcome) - { - Proceed = true; - OutcomeValue = outcome; - } - - public static ExecutionResult Outcome(object value) - { - return new ExecutionResult - { - Proceed = true, - OutcomeValue = value - }; - } - - public static ExecutionResult Next() - { - return new ExecutionResult - { - Proceed = true, - OutcomeValue = null - }; - } - - public static ExecutionResult Persist(object persistenceData) - { - return new ExecutionResult - { - Proceed = false, - PersistenceData = persistenceData - }; - } - - public static ExecutionResult Branch(List branches, object persistenceData) - { - return new ExecutionResult - { - Proceed = false, - PersistenceData = persistenceData, - BranchValues = branches - }; - } - - public static ExecutionResult Sleep(TimeSpan duration, object persistenceData) - { - return new ExecutionResult - { - Proceed = false, - SleepFor = duration, - PersistenceData = persistenceData - }; - } - - public static ExecutionResult WaitForEvent(string eventName, string eventKey, DateTime effectiveDate) - { - return new ExecutionResult - { - Proceed = false, - EventName = eventName, - EventKey = eventKey, - EventAsOf = effectiveDate.ToUniversalTime() - }; - } - - public static ExecutionResult WaitForActivity(string activityName, object subscriptionData, DateTime effectiveDate) - { - return new ExecutionResult - { - Proceed = false, - EventName = Event.EventTypeActivity, - EventKey = activityName, - SubscriptionData = subscriptionData, - EventAsOf = effectiveDate.ToUniversalTime() - }; - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Expr/Contracts/IStepOutcome.cs b/source/core/Coflo.Abstractions/Expr/Contracts/IStepOutcome.cs deleted file mode 100644 index 28adebb..0000000 --- a/source/core/Coflo.Abstractions/Expr/Contracts/IStepOutcome.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Steps.Models; - -namespace Coflo.Abstractions.Expr.Contracts; - -public interface IStepOutcome -{ - string ExternalNextStepId { get; set; } - string Label { get; set; } - int NextStep { get; set; } - - bool Matches(object data); - bool Matches(ExecutionResult executionResult, object data); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Expr/Models/ExpressionOutcome.cs b/source/core/Coflo.Abstractions/Expr/Models/ExpressionOutcome.cs deleted file mode 100644 index 3843b1b..0000000 --- a/source/core/Coflo.Abstractions/Expr/Models/ExpressionOutcome.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Linq.Expressions; -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Expr.Contracts; -using Coflo.Abstractions.Steps.Models; - -namespace Coflo.Abstractions.Expr.Models; - -public class ExpressionOutcome : IStepOutcome -{ - private readonly Func _func; - - public int NextStep { get; set; } - - public string Label { get; set; } - - public string ExternalNextStepId { get; set; } - - public ExpressionOutcome(Expression> expression) - { - _func = expression.Compile(); - } - - public bool Matches(ExecutionResult executionResult, object data) - { - return _func((TData)data, executionResult.OutcomeValue); - } - - public bool Matches(object data) - { - return _func((TData)data, null); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Locking/Contracts/IDistributedLockProvider.cs b/source/core/Coflo.Abstractions/Locking/Contracts/IDistributedLockProvider.cs deleted file mode 100644 index 83c696c..0000000 --- a/source/core/Coflo.Abstractions/Locking/Contracts/IDistributedLockProvider.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Coflo.Abstractions.Locking.Contracts; - -/// -/// The implemention of this interface will be responsible for -/// providing a (distributed) locking mechanism to manage in flight workflows -/// -public interface IDistributedLockProvider -{ - /// - /// Acquire a lock on the specified resource. - /// - /// Resource ID to lock. - /// - /// `true`, if the lock was acquired. - Task AcquireLock(string Id, CancellationToken cancellationToken); - - Task ReleaseLock(string Id); - - Task Start(); - - Task Stop(); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Contracts/IEventRepository.cs b/source/core/Coflo.Abstractions/Persistence/Contracts/IEventRepository.cs deleted file mode 100644 index 214d5b1..0000000 --- a/source/core/Coflo.Abstractions/Persistence/Contracts/IEventRepository.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Coflo.Abstractions.Events.Models; - -namespace Coflo.Abstractions.Persistence.Contracts; - -public interface IEventRepository -{ - Task CreateEvent(Event newEvent, CancellationToken cancellationToken = default); - - Task GetEvent(string id, CancellationToken cancellationToken = default); - - Task> GetRunnableEvents(DateTime asAt, CancellationToken cancellationToken = default); - - Task> GetEvents(string eventName, string eventKey, DateTime asOf, - CancellationToken cancellationToken = default); - - Task MarkEventProcessed(string id, CancellationToken cancellationToken = default); - - Task MarkEventUnprocessed(string id, CancellationToken cancellationToken = default); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Contracts/IPersistenceProvider.cs b/source/core/Coflo.Abstractions/Persistence/Contracts/IPersistenceProvider.cs deleted file mode 100644 index 6c3b8ff..0000000 --- a/source/core/Coflo.Abstractions/Persistence/Contracts/IPersistenceProvider.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Coflo.Abstractions.Execution.Models; - -namespace Coflo.Abstractions.Persistence.Contracts; - -public interface IPersistenceProvider : IWorkflowRepository, ISubscriptionRepository, IEventRepository, - IScheduledCommandRepository -{ - Task PersistErrors(IEnumerable errors, CancellationToken cancellationToken = default); - - void EnsureStoreExists(); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Contracts/IScheduledCommandRepository.cs b/source/core/Coflo.Abstractions/Persistence/Contracts/IScheduledCommandRepository.cs deleted file mode 100644 index a84cda2..0000000 --- a/source/core/Coflo.Abstractions/Persistence/Contracts/IScheduledCommandRepository.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Coflo.Abstractions.Events.Models; - -namespace Coflo.Abstractions.Persistence.Contracts; - -public interface IScheduledCommandRepository -{ - bool SupportsScheduledCommands { get; } - - Task ScheduleCommand(ScheduledCommand command); - - Task ProcessCommands(DateTimeOffset asOf, Func action, CancellationToken cancellationToken = default); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Contracts/ISubscriptionRepository.cs b/source/core/Coflo.Abstractions/Persistence/Contracts/ISubscriptionRepository.cs deleted file mode 100644 index 42c17c0..0000000 --- a/source/core/Coflo.Abstractions/Persistence/Contracts/ISubscriptionRepository.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Coflo.Abstractions.Events.Models; - -namespace Coflo.Abstractions.Persistence.Contracts; - -public interface ISubscriptionRepository -{ - Task CreateEventSubscription(EventSubscription subscription, CancellationToken cancellationToken = default); - - Task> GetSubscriptions(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default); - - Task TerminateSubscription(long eventSubscriptionId, CancellationToken cancellationToken = default); - - Task GetSubscription(long eventSubscriptionId, CancellationToken cancellationToken = default); - - Task GetFirstOpenSubscription(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default); - - Task SetSubscriptionToken(long eventSubscriptionId, string token, long workerId, DateTime expiry, CancellationToken cancellationToken = default); - - Task ClearSubscriptionToken(long eventSubscriptionId, string token, CancellationToken cancellationToken = default); - -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Contracts/IWorkflowRepository.cs b/source/core/Coflo.Abstractions/Persistence/Contracts/IWorkflowRepository.cs deleted file mode 100644 index e0a9e18..0000000 --- a/source/core/Coflo.Abstractions/Persistence/Contracts/IWorkflowRepository.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Coflo.Abstractions.Events.Models; -using Coflo.Abstractions.Workflow.Enums; -using Coflo.Abstractions.Workflow.Models; - -namespace Coflo.Abstractions.Persistence.Contracts; - -public interface IWorkflowRepository -{ - Task CreateNewWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default); - - Task PersistWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default); - - Task PersistWorkflow(WorkflowInstance workflow, List subscriptions, CancellationToken cancellationToken = default); - - Task> GetRunnableInstances(DateTime asAt, CancellationToken cancellationToken = default); - - [Obsolete] - Task> GetWorkflowInstances(WorkflowStatus? status, string type, DateTime? createdFrom, DateTime? createdTo, int skip, int take); - - Task GetWorkflowInstance(string Id, CancellationToken cancellationToken = default); - - Task> GetWorkflowInstances(IEnumerable ids, CancellationToken cancellationToken = default); - -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Exceptions/CurruptPersistenceDataException.cs b/source/core/Coflo.Abstractions/Persistence/Exceptions/CurruptPersistenceDataException.cs deleted file mode 100644 index 7edd3e1..0000000 --- a/source/core/Coflo.Abstractions/Persistence/Exceptions/CurruptPersistenceDataException.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Coflo.Abstractions.Persistence.Exceptions; - -public class CorruptPersistenceDataException : Exception -{ -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Exceptions/NotFoundException.cs b/source/core/Coflo.Abstractions/Persistence/Exceptions/NotFoundException.cs deleted file mode 100644 index 98ab89a..0000000 --- a/source/core/Coflo.Abstractions/Persistence/Exceptions/NotFoundException.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Coflo.Abstractions.Persistence.Exceptions; - -public class NotFoundException : Exception -{ - - public NotFoundException() : base() - { - - } - - public NotFoundException(string message) : base(message) - { - - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Models/ControlPersistenceData.cs b/source/core/Coflo.Abstractions/Persistence/Models/ControlPersistenceData.cs deleted file mode 100644 index 4eddd64..0000000 --- a/source/core/Coflo.Abstractions/Persistence/Models/ControlPersistenceData.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Coflo.Abstractions.Persistence.Models; - -public class ControlPersistenceData -{ - public bool ChildrenActive { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Models/IteratorPersistenceData.cs b/source/core/Coflo.Abstractions/Persistence/Models/IteratorPersistenceData.cs deleted file mode 100644 index 1478cf2..0000000 --- a/source/core/Coflo.Abstractions/Persistence/Models/IteratorPersistenceData.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Coflo.Abstractions.Persistence.Models; - -public class IteratorPersistenceData : ControlPersistenceData -{ - public int Index { get; set; } = 0; -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Persistence/Models/SchedulePersistenceData.cs b/source/core/Coflo.Abstractions/Persistence/Models/SchedulePersistenceData.cs deleted file mode 100644 index 60b45ad..0000000 --- a/source/core/Coflo.Abstractions/Persistence/Models/SchedulePersistenceData.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Coflo.Abstractions.Persistence.Models; - -public class SchedulePersistenceData -{ - public bool Elapsed { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Contracts/IContainerStepBuilder.cs b/source/core/Coflo.Abstractions/Primitives/Contracts/IContainerStepBuilder.cs deleted file mode 100644 index 8d0f6d7..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Contracts/IContainerStepBuilder.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Coflo.Abstractions.Steps.Contracts; -using Coflo.Abstractions.Workflow.Contracts; - -namespace Coflo.Abstractions.Primitives.Contracts; - -public interface IContainerStepBuilder - where TStepBody : IStepBody - where TReturnStep : IStepBody -{ - /// - /// The block of steps to execute - /// - /// - /// - IStepBuilder Do(Action> builder); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Contracts/IParallelStepBuilder.cs b/source/core/Coflo.Abstractions/Primitives/Contracts/IParallelStepBuilder.cs deleted file mode 100644 index 7d5f417..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Contracts/IParallelStepBuilder.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Coflo.Abstractions.Primitives.Iteration; -using Coflo.Abstractions.Steps.Contracts; -using Coflo.Abstractions.Workflow.Contracts; - -namespace Coflo.Abstractions.Primitives.Contracts; - -public interface IParallelStepBuilder - where TStepBody : IStepBody -{ - IParallelStepBuilder Do(Action> builder); - IStepBuilder Join(); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/ActionStepBody.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/ActionStepBody.cs deleted file mode 100644 index eb631fe..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/ActionStepBody.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Steps.Contracts; -using Coflo.Abstractions.Steps.Models; - -namespace Coflo.Abstractions.Primitives; - -public class ActionStepBody : StepBody -{ - public Action Body { get; set; } - - public override ExecutionResult Run(IStepExecutionContext context) - { - Body(context); - return ExecutionResult.Next(); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Activity.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Activity.cs deleted file mode 100644 index 57e4033..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/Activity.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Primitives.Models; -using Coflo.Abstractions.Steps.Contracts; -using Coflo.Abstractions.Steps.Exceptions; -using Coflo.Abstractions.Steps.Models; - -namespace Coflo.Abstractions.Primitives; - -public class Activity : StepBody -{ - public string ActivityName { get; set; } - - public DateTime EffectiveDate { get; set; } - - public object Parameters { get; set; } - - public object Result { get; set; } - - public override ExecutionResult Run(IStepExecutionContext context) - { - if (!context.ExecutionPointer.EventPublished) - { - DateTime effectiveDate = DateTime.MinValue; - - if (EffectiveDate != null) - { - effectiveDate = EffectiveDate; - } - - return ExecutionResult.WaitForActivity(ActivityName, Parameters, effectiveDate); - } - - if (context.ExecutionPointer.EventData is ActivityResult result) - { - var actResult = result; - - if (actResult.Status == ActivityResult.StatusType.Success) - { - Result = actResult.Data; - } - else - { - throw new ActivityFailedException(actResult.Data); - } - } - else - { - Result = context.ExecutionPointer.EventData; - } - - return ExecutionResult.Next(); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/ContainerStepBody.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/ContainerStepBody.cs deleted file mode 100644 index c74dab3..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/ContainerStepBody.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Coflo.Abstractions.Steps.Models; - -namespace Coflo.Abstractions.Primitives; - -public abstract class ContainerStepBody : StepBody -{ - -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/Decide.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/Decide.cs deleted file mode 100644 index 81ba220..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/Decide.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Steps.Contracts; -using Coflo.Abstractions.Steps.Models; - -namespace Coflo.Abstractions.Primitives.Control; - -public class Decide : StepBody -{ - public object Expression { get; set; } - - public override ExecutionResult Run(IStepExecutionContext context) - { - return ExecutionResult.Outcome(Expression); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/Delay.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/Delay.cs deleted file mode 100644 index 755357d..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/Delay.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Steps.Contracts; -using Coflo.Abstractions.Steps.Models; - -namespace Coflo.Abstractions.Primitives.Control; - -public class Delay : StepBody -{ - public TimeSpan Period { get; set; } - - public override ExecutionResult Run(IStepExecutionContext context) - { - if (context.PersistenceData != null) - { - return ExecutionResult.Next(); - } - - return ExecutionResult.Sleep(Period, true); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/EndStep.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/EndStep.cs deleted file mode 100644 index b9fb208..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/EndStep.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Coflo.Abstractions.Workflow.Enums; -using Coflo.Abstractions.Workflow.Models; - -namespace Coflo.Abstractions.Primitives.Control; - -public class EndStep : WorkflowStep -{ - public override Type BodyType => null!; - - public override ExecutionPipelineDirective InitForExecution(WorkflowResult result, WorkflowDefinition definition, - WorkflowInstance workflow, ExecutionPointer executionPointer) - { - return ExecutionPipelineDirective.EndWorkflow; - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/If.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/If.cs deleted file mode 100644 index f36dca5..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/If.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Persistence.Exceptions; -using Coflo.Abstractions.Persistence.Models; -using Coflo.Abstractions.Steps.Contracts; - -namespace Coflo.Abstractions.Primitives.Control; - -public class If : ContainerStepBody -{ - public bool Condition { get; set; } - - public override ExecutionResult Run(IStepExecutionContext context) - { - if (context.PersistenceData == null) - { - if (Condition) - { - return ExecutionResult.Branch(new List { context.Item }, new ControlPersistenceData { ChildrenActive = true }); - } - - return ExecutionResult.Next(); - } - - if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) - { - if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) - { - return ExecutionResult.Next(); - } - - return ExecutionResult.Persist(context.PersistenceData); - } - - throw new CorruptPersistenceDataException(); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/OutcomeSwitch.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/OutcomeSwitch.cs deleted file mode 100644 index 0fb8ad4..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/OutcomeSwitch.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Persistence.Exceptions; -using Coflo.Abstractions.Persistence.Models; -using Coflo.Abstractions.Steps.Contracts; - -namespace Coflo.Abstractions.Primitives.Control; - -public class OutcomeSwitch : ContainerStepBody -{ - public override ExecutionResult Run(IStepExecutionContext context) - { - if (context.PersistenceData == null) - { - var result = ExecutionResult.Branch(new List { context.Item }, new ControlPersistenceData { ChildrenActive = true }); - result.OutcomeValue = GetPreviousOutcome(context); - return result; - } - - if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) - { - if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) - { - return ExecutionResult.Next(); - } - else - { - var result = ExecutionResult.Persist(context.PersistenceData); - result.OutcomeValue = GetPreviousOutcome(context); - return result; - } - } - - throw new CorruptPersistenceDataException(); - } - - private object GetPreviousOutcome(IStepExecutionContext context) - { - var prevPointer = context.Workflow.ExecutionPointers.FindById(context.ExecutionPointer.PredecessorId); - return prevPointer.Outcome; - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/WaitFor.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/WaitFor.cs deleted file mode 100644 index c6a9994..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/WaitFor.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Steps.Contracts; -using Coflo.Abstractions.Steps.Models; - -namespace Coflo.Abstractions.Primitives.Control; - -public class WaitFor : StepBody -{ - public string EventKey { get; set; } - - public string EventName { get; set; } - - public DateTime EffectiveDate { get; set; } - - public object EventData { get; set; } - - public override ExecutionResult Run(IStepExecutionContext context) - { - if (!context.ExecutionPointer.EventPublished) - { - DateTime effectiveDate = DateTime.MinValue; - - if (EffectiveDate != null) - { - effectiveDate = EffectiveDate; - } - - return ExecutionResult.WaitForEvent(EventName, EventKey, effectiveDate); - } - - EventData = context.ExecutionPointer.EventData; - return ExecutionResult.Next(); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/When.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Control/When.cs deleted file mode 100644 index 37c93cf..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/Control/When.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Persistence.Exceptions; -using Coflo.Abstractions.Persistence.Models; -using Coflo.Abstractions.Steps.Contracts; - -namespace Coflo.Abstractions.Primitives.Control; - -public class When : ContainerStepBody -{ - public object ExpectedOutcome { get; set; } - - public override ExecutionResult Run(IStepExecutionContext context) - { - var switchOutcome = GetSwitchOutcome(context); - - if (ExpectedOutcome != switchOutcome) - { - if (Convert.ToString(ExpectedOutcome) != Convert.ToString(switchOutcome)) - { - return ExecutionResult.Next(); - } - } - - if (context.PersistenceData == null) - { - return ExecutionResult.Branch(new List { context.Item }, new ControlPersistenceData { ChildrenActive = true }); - } - - if ((context.PersistenceData is ControlPersistenceData) && ((context.PersistenceData as ControlPersistenceData).ChildrenActive)) - { - if (context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) - { - return ExecutionResult.Next(); - } - - return ExecutionResult.Persist(context.PersistenceData); - } - - throw new CorruptPersistenceDataException(); - } - - private object GetSwitchOutcome(IStepExecutionContext context) - { - var switchPointer = context.Workflow.ExecutionPointers.First(x => x.Children.Contains(context.ExecutionPointer.Id)); - return switchPointer.Outcome; - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/InlineStepBody.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/InlineStepBody.cs deleted file mode 100644 index 09bfe6a..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/InlineStepBody.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Steps.Contracts; -using Coflo.Abstractions.Steps.Models; - -namespace Coflo.Abstractions.Primitives; - -public class InlineStepBody : StepBody -{ - - private readonly Func _body; - - public InlineStepBody(Func body) - { - _body = body; - } - - public override ExecutionResult Run(IStepExecutionContext context) - { - return _body.Invoke(context); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/Foreach.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/Foreach.cs deleted file mode 100644 index c9a75cd..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/Foreach.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections; -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Persistence.Models; -using Coflo.Abstractions.Steps.Contracts; - -namespace Coflo.Abstractions.Primitives.Iteration; - -public class Foreach : ContainerStepBody -{ - public IEnumerable Collection { get; set; } - public bool RunParallel { get; set; } = true; - - public override ExecutionResult Run(IStepExecutionContext context) - { - switch (context.PersistenceData) - { - case null: - { - var values = Collection.Cast().ToList(); - - if (!values.Any()) - return ExecutionResult.Next(); - - return ExecutionResult.Branch( - RunParallel ? new List(values) : new List(new object[] { values.ElementAt(0) }), - new IteratorPersistenceData { ChildrenActive = true }); - } - case IteratorPersistenceData { ChildrenActive: true } persistenceData: - { - if (!context.Workflow.IsBranchComplete(context.ExecutionPointer.Id)) - return ExecutionResult.Persist(persistenceData); - - if (RunParallel) return ExecutionResult.Next(); - - var values = Collection.Cast(); - - persistenceData.Index++; - - return persistenceData.Index < values.Count() - ? ExecutionResult.Branch(new List(new object[] { values.ElementAt(persistenceData.Index) }), - persistenceData) - : ExecutionResult.Next(); - } - } - - if (context.PersistenceData is not ControlPersistenceData { ChildrenActive: true }) - return ExecutionResult.Persist(context.PersistenceData); - - return context.Workflow.IsBranchComplete(context.ExecutionPointer.Id) - ? ExecutionResult.Next() - : ExecutionResult.Persist(context.PersistenceData); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/Sequence.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/Sequence.cs deleted file mode 100644 index 9489634..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/Sequence.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Persistence.Exceptions; -using Coflo.Abstractions.Persistence.Models; -using Coflo.Abstractions.Steps.Contracts; - -namespace Coflo.Abstractions.Primitives.Iteration; - -public class Sequence : ContainerStepBody -{ - public override ExecutionResult Run(IStepExecutionContext context) - { - return context.PersistenceData switch - { - null => ExecutionResult.Branch(new List { context.Item }, - new ControlPersistenceData { ChildrenActive = true }), - ControlPersistenceData { ChildrenActive: true } => - context.Workflow.IsBranchComplete(context.ExecutionPointer.Id) - ? ExecutionResult.Next() - : ExecutionResult.Persist(context.PersistenceData), - _ => throw new CorruptPersistenceDataException() - }; - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/While.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/While.cs deleted file mode 100644 index d868158..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/Iteration/While.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Persistence.Exceptions; -using Coflo.Abstractions.Persistence.Models; -using Coflo.Abstractions.Steps.Contracts; - -namespace Coflo.Abstractions.Primitives.Iteration; - -public class While : ContainerStepBody -{ - public bool Condition { get; set; } - - public override ExecutionResult Run(IStepExecutionContext context) - { - switch (context.PersistenceData) - { - case null when Condition: - return ExecutionResult.Branch(new List { context.Item }, - new ControlPersistenceData { ChildrenActive = true }); - case null: - return ExecutionResult.Next(); - case ControlPersistenceData data when (data.ChildrenActive): - { - return ExecutionResult.Persist(!context.Workflow.IsBranchComplete(context.ExecutionPointer.Id) - ? context.PersistenceData - : null); //re-evaluate condition on next pass - } - default: - throw new CorruptPersistenceDataException(); - } - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Recurring/Recur.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Recurring/Recur.cs deleted file mode 100644 index 59776cf..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/Recurring/Recur.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Steps.Contracts; - -namespace Coflo.Abstractions.Primitives.Recurring; - -public class Recur : ContainerStepBody -{ - public TimeSpan Interval { get; set; } - - public bool StopCondition { get; set; } - - public override ExecutionResult Run(IStepExecutionContext context) - { - if (StopCondition) - { - return ExecutionResult.Next(); - } - - return new ExecutionResult - { - Proceed = false, - BranchValues = new List { null }, - SleepFor = Interval - }; - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/Recurring/Schedule.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/Recurring/Schedule.cs deleted file mode 100644 index e34560e..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/Recurring/Schedule.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Persistence.Models; -using Coflo.Abstractions.Steps.Contracts; - -namespace Coflo.Abstractions.Primitives.Recurring; - -public class Schedule : ContainerStepBody -{ - public TimeSpan Interval { get; set; } - - public override ExecutionResult Run(IStepExecutionContext context) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - if (context.PersistenceData == null) - { - return ExecutionResult.Sleep(Interval, new SchedulePersistenceData { Elapsed = false }); - } - - if (context.PersistenceData is not SchedulePersistenceData data) throw new ArgumentException(); - - if (!data.Elapsed) - return ExecutionResult.Branch(new List { context.Item }, - new SchedulePersistenceData { Elapsed = true }); - - - return context.Workflow.IsBranchComplete(context.ExecutionPointer.Id) - ? ExecutionResult.Next() - : ExecutionResult.Persist(context.PersistenceData); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/SagaContainer.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/SagaContainer.cs deleted file mode 100644 index 22c830d..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/SagaContainer.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Coflo.Abstractions.Steps.Contracts; -using Coflo.Abstractions.Workflow.Models; - -namespace Coflo.Abstractions.Primitives; - -public class SagaContainer : WorkflowStep - where TStepBody : IStepBody -{ - public override bool ResumeChildrenAfterCompensation => false; - public override bool RevertChildrenAfterCompensation => true; - - public override void PrimeForRetry(ExecutionPointer pointer) - { - base.PrimeForRetry(pointer); - pointer.PersistenceData = null; - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/SubWorkflowStepBody.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/SubWorkflowStepBody.cs deleted file mode 100644 index e8ee42c..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/SubWorkflowStepBody.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Steps.Contracts; -using Coflo.Abstractions.Steps.Models; - -namespace Coflo.Abstractions.Primitives; - -public class SubWorkflowStepBody : StepBody -{ - public override ExecutionResult Run(IStepExecutionContext context) - { - // TODO: What is this supposed to do? - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Implementation/WorkflowStepInline.cs b/source/core/Coflo.Abstractions/Primitives/Implementation/WorkflowStepInline.cs deleted file mode 100644 index 1a107a6..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Implementation/WorkflowStepInline.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Steps.Contracts; -using Coflo.Abstractions.Workflow.Models; - -namespace Coflo.Abstractions.Primitives; - -public class WorkflowStepInline : WorkflowStep -{ - public Func Body { get; set; } - - public override IStepBody ConstructBody(IServiceProvider serviceProvider) - { - return new InlineStepBody(Body); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Primitives/Models/ActivityResult.cs b/source/core/Coflo.Abstractions/Primitives/Models/ActivityResult.cs deleted file mode 100644 index 0dd9acd..0000000 --- a/source/core/Coflo.Abstractions/Primitives/Models/ActivityResult.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Coflo.Abstractions.Primitives.Models; - -public class ActivityResult -{ - public enum StatusType { Success, Fail } - public StatusType Status { get; set; } - public long SubscriptionId { get; set; } - public object Data { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Attributes/StepAttribute.cs b/source/core/Coflo.Abstractions/Steps/Attributes/StepAttribute.cs deleted file mode 100644 index 971a2d4..0000000 --- a/source/core/Coflo.Abstractions/Steps/Attributes/StepAttribute.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Coflo.Abstractions.Steps.Attributes; - -public class StepAttribute -{ - public string Name { get; set; } - public string DisplayName { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Contracts/IActivityController.cs b/source/core/Coflo.Abstractions/Steps/Contracts/IActivityController.cs deleted file mode 100644 index 24f60f5..0000000 --- a/source/core/Coflo.Abstractions/Steps/Contracts/IActivityController.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Coflo.Abstractions.Steps.Models; - -namespace Coflo.Abstractions.Steps.Contracts; - -public interface IActivityController -{ - Task GetPendingActivity(string activityName, long workerId, TimeSpan? timeout = null); - Task ReleaseActivityToken(string token); - Task SubmitActivitySuccess(string token, object result); - Task SubmitActivityFailure(string token, object result); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Contracts/IStepBody.cs b/source/core/Coflo.Abstractions/Steps/Contracts/IStepBody.cs deleted file mode 100644 index 340958c..0000000 --- a/source/core/Coflo.Abstractions/Steps/Contracts/IStepBody.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Steps.Models; - -namespace Coflo.Abstractions.Steps.Contracts; - -public interface IStepBody -{ - Task RunAsync(IStepExecutionContext context); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Contracts/IStepBuilder.cs b/source/core/Coflo.Abstractions/Steps/Contracts/IStepBuilder.cs deleted file mode 100644 index 443c02a..0000000 --- a/source/core/Coflo.Abstractions/Steps/Contracts/IStepBuilder.cs +++ /dev/null @@ -1,153 +0,0 @@ -using System.Linq.Expressions; -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Steps.Models; -using Coflo.Abstractions.Workflow.Contracts; -using Coflo.Abstractions.Workflow.Enums; -using Coflo.Abstractions.Workflow.Models; - -namespace Coflo.Abstractions.Steps.Contracts; - -public interface IStepBuilder : IWorkflowModifier - where TStepBody : IStepBody -{ - IWorkflowBuilder WorkflowBuilder { get; } - - WorkflowStep Step { get; set; } - - /// - /// Specifies a display name for the step - /// - /// A display name for the step for easy identification in logs, etc... - /// - IStepBuilder Name(string name); - - /// - /// Specifies a custom Id to reference this step - /// - /// A custom Id to reference this step - /// - IStepBuilder Id(string id); - - /// - /// Specify the next step in the workflow by Id - /// - /// - /// - IStepBuilder Attach(string id); - - /// - /// Configure an outcome branch for this step, then wire it to another step - /// - /// - /// - IStepBuilder Branch(object outcomeValue, IStepBuilder branch) - where TStep : IStepBody; - - /// - /// Configure an outcome branch for this step, then wire it to another step - /// - /// - /// - IStepBuilder Branch(Expression> outcomeExpression, - IStepBuilder branch) where TStep : IStepBody; - - /// - /// Map properties on the step to properties on the workflow data object before the step executes - /// - /// - /// Property on the step - /// - /// - IStepBuilder Input(Expression> stepProperty, - Expression> value); - - /// - /// Map properties on the step to properties on the workflow data object before the step executes - /// - /// - /// The property on the step - /// - /// - IStepBuilder Input(Expression> stepProperty, - Expression> value); - - /// - /// Manipulate properties on the step before its executed. - /// - /// - /// - IStepBuilder Input(Action action); - - IStepBuilder Input(Action action); - - /// - /// Map properties on the workflow data object to properties on the step after the step executes - /// - /// - /// Property on the data object - /// - /// - IStepBuilder Output(Expression> dataProperty, - Expression> value); - - /// - /// Manipulate properties on the data object after the step executes - /// - /// - /// - IStepBuilder Output(Action action); - - IStepBuilder End(string name) where TStep : IStepBody; - - /// - /// Configure the behavior when this step throws an unhandled exception - /// - /// What action to take when this step throws an unhandled exception - /// If the behavior is retry, how often - /// - IStepBuilder OnError(WorkflowErrorHandling behavior, TimeSpan? retryInterval = null); - - /// - /// Ends the workflow and marks it as complete - /// - /// - IStepBuilder EndWorkflow(); - - /// - /// Undo step if unhandled exception is thrown by this step - /// - /// The type of the step to execute - /// Configure additional parameters for this step - /// - IStepBuilder CompensateWith(Action> stepSetup = null) - where TStep : IStepBody; - - /// - /// Undo step if unhandled exception is thrown by this step - /// - /// - /// - IStepBuilder CompensateWith(Func body); - - /// - /// Undo step if unhandled exception is thrown by this step - /// - /// - /// - IStepBuilder CompensateWith(Action body); - - /// - /// Undo step if unhandled exception is thrown by this step - /// - /// - /// - IStepBuilder CompensateWithSequence(Action> builder); - - /// - /// Prematurely cancel the execution of this step on a condition - /// - /// - /// - IStepBuilder CancelCondition(Expression> cancelCondition, - bool proceedAfterCancel = false); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Contracts/IStepExecutionContext.cs b/source/core/Coflo.Abstractions/Steps/Contracts/IStepExecutionContext.cs deleted file mode 100644 index d47abe5..0000000 --- a/source/core/Coflo.Abstractions/Steps/Contracts/IStepExecutionContext.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Coflo.Abstractions.Workflow.Models; - -namespace Coflo.Abstractions.Steps.Contracts; - -public interface IStepExecutionContext -{ - object Item { get; set; } - - ExecutionPointer ExecutionPointer { get; set; } - - object PersistenceData { get; set; } - - WorkflowStep Step { get; set; } - - WorkflowInstance Workflow { get; set; } - - CancellationToken CancellationToken { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Contracts/IStepParameter.cs b/source/core/Coflo.Abstractions/Steps/Contracts/IStepParameter.cs deleted file mode 100644 index 3a0f342..0000000 --- a/source/core/Coflo.Abstractions/Steps/Contracts/IStepParameter.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Coflo.Abstractions.Steps.Contracts; - -public interface IStepParameter -{ - void AssignInput(object data, IStepBody body, IStepExecutionContext context); - void AssignOutput(object data, IStepBody body, IStepExecutionContext context); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Exceptions/ActivityFailedException.cs b/source/core/Coflo.Abstractions/Steps/Exceptions/ActivityFailedException.cs deleted file mode 100644 index 85f17ad..0000000 --- a/source/core/Coflo.Abstractions/Steps/Exceptions/ActivityFailedException.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Coflo.Abstractions.Steps.Exceptions; - -public class ActivityFailedException : Exception -{ - public ActivityFailedException(object data) - { - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Models/ActionStepBody.cs b/source/core/Coflo.Abstractions/Steps/Models/ActionStepBody.cs deleted file mode 100644 index 75c07e9..0000000 --- a/source/core/Coflo.Abstractions/Steps/Models/ActionStepBody.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Steps.Contracts; - -namespace Coflo.Abstractions.Steps.Models; - -public class ActionStepBody : StepBody -{ - public Action Body { get; set; } - - public override ExecutionResult Run(IStepExecutionContext context) - { - Body(context); - - return ExecutionResult.Next(); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Models/ActivityToken.cs b/source/core/Coflo.Abstractions/Steps/Models/ActivityToken.cs deleted file mode 100644 index 926a0a2..0000000 --- a/source/core/Coflo.Abstractions/Steps/Models/ActivityToken.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Text; -using System.Text.Json; -namespace Coflo.Abstractions.Steps.Models; - -public class ActivityToken -{ - public long SubscriptionId { get; set; } - public string ActivityName { get; set; } - public string Nonce { get; set; } - - public string Encode() - { - var json = JsonSerializer.Serialize(this); - - return Convert.ToBase64String(Encoding.UTF8.GetBytes(json)); - } - - public static ActivityToken Create(long subscriptionId, string activityName) - { - return new ActivityToken - { - SubscriptionId = subscriptionId, - ActivityName = activityName, - Nonce = Guid.NewGuid().ToString() - }; - } - - public static ActivityToken Decode(string encodedToken) - { - var raw = Convert.FromBase64String(encodedToken); - var json = Encoding.UTF8.GetString(raw); - - return JsonSerializer.Deserialize(json) - ?? throw new Exception("Invalid token"); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Models/InlineStepBody.cs b/source/core/Coflo.Abstractions/Steps/Models/InlineStepBody.cs deleted file mode 100644 index 926b0b8..0000000 --- a/source/core/Coflo.Abstractions/Steps/Models/InlineStepBody.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Steps.Contracts; - -namespace Coflo.Abstractions.Steps.Models; - -public class InlineStepBody : StepBody -{ - - private readonly Func _body; - - public InlineStepBody(Func body) - { - _body = body; - } - - public override ExecutionResult Run(IStepExecutionContext context) - { - return _body.Invoke(context); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Models/PendingActivity.cs b/source/core/Coflo.Abstractions/Steps/Models/PendingActivity.cs deleted file mode 100644 index 7cb243c..0000000 --- a/source/core/Coflo.Abstractions/Steps/Models/PendingActivity.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Coflo.Abstractions.Steps.Models; - -public class PendingActivity -{ - public string Token { get; set; } - public string ActivityName { get; set; } - public object Parameters { get; set; } - public DateTime TokenExpiry { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Models/StepBody.cs b/source/core/Coflo.Abstractions/Steps/Models/StepBody.cs deleted file mode 100644 index 8008e71..0000000 --- a/source/core/Coflo.Abstractions/Steps/Models/StepBody.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Steps.Contracts; - -namespace Coflo.Abstractions.Steps.Models; - -public abstract class StepBody : IStepBody -{ - public abstract ExecutionResult Run(IStepExecutionContext context); - - public Task RunAsync(IStepExecutionContext context) - { - return Task.FromResult(Run(context)); - } - - protected Execution.Models.ExecutionResult OutcomeResult(object value) - { - return new Execution.Models.ExecutionResult - { - Proceed = true, - OutcomeValue = value - }; - } - - protected ExecutionResult PersistResult(object persistenceData) - { - return new ExecutionResult - { - Proceed = false, - PersistenceData = persistenceData - }; - } - - protected ExecutionResult SleepResult(object persistenceData, TimeSpan sleep) - { - return new ExecutionResult - { - Proceed = false, - PersistenceData = persistenceData, - SleepFor = sleep - }; - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Models/StepBodyAsync.cs b/source/core/Coflo.Abstractions/Steps/Models/StepBodyAsync.cs deleted file mode 100644 index 7f05a55..0000000 --- a/source/core/Coflo.Abstractions/Steps/Models/StepBodyAsync.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Steps.Contracts; - -namespace Coflo.Abstractions.Steps.Models; - -public abstract class StepBodyAsync : IStepBody -{ - public abstract Task RunAsync(IStepExecutionContext context); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Steps/Models/WorkflowStepDelegate.cs b/source/core/Coflo.Abstractions/Steps/Models/WorkflowStepDelegate.cs deleted file mode 100644 index cbca4cc..0000000 --- a/source/core/Coflo.Abstractions/Steps/Models/WorkflowStepDelegate.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Coflo.Abstractions.Execution.Models; - -namespace Coflo.Abstractions.Steps.Models; - -/// -/// Represents a function that executes a workflow step and returns a result. -/// -public delegate Task WorkflowStepDelegate(); \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Variables/Contracts/IVariableCollection.cs b/source/core/Coflo.Abstractions/Variables/Contracts/IVariableCollection.cs new file mode 100644 index 0000000..a1cbc04 --- /dev/null +++ b/source/core/Coflo.Abstractions/Variables/Contracts/IVariableCollection.cs @@ -0,0 +1,8 @@ +namespace Coflo.Abstractions.Variables.Model; + +public interface IVariableCollection +{ + VariableInstance? this[string name] { get; } + void Add(VariableInstance variable); + void AddRange(IEnumerable variables); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Variables/Enums/VariableType.cs b/source/core/Coflo.Abstractions/Variables/Enums/VariableType.cs new file mode 100644 index 0000000..2fe51a8 --- /dev/null +++ b/source/core/Coflo.Abstractions/Variables/Enums/VariableType.cs @@ -0,0 +1,9 @@ +namespace Coflo.Abstractions.Variables.Enums; + +public enum VariableType +{ + String, + Number, + Boolean, + Object +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Variables/Model/VariableCollection.cs b/source/core/Coflo.Abstractions/Variables/Model/VariableCollection.cs new file mode 100644 index 0000000..b43b46c --- /dev/null +++ b/source/core/Coflo.Abstractions/Variables/Model/VariableCollection.cs @@ -0,0 +1,48 @@ +namespace Coflo.Abstractions.Variables.Model; + +public class VariableCollection : IVariableCollection +{ + private readonly IDictionary _variables; + + public VariableCollection(IDictionary variables) + { + _variables = new Dictionary(variables); + } + + public VariableCollection() : this(new Dictionary()) + { + + } + + public VariableInstance? this[string name] => _variables[name]; + + public bool Contains(string name) + { + return _variables.ContainsKey(name); + } + + public bool AddOrUpdate(VariableInstance variable) + { + if (Contains(variable.Name)) + { + _variables[variable.Name] = variable; + return true; + } + + _variables.Add(variable.Name, variable); + return false; + } + + public void Add(VariableInstance variable) + { + _variables.Add(variable.Name, variable); + } + + public void AddRange(IEnumerable variables) + { + foreach (var variable in variables) + { + Add(variable); + } + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Variables/Model/VariableDefinition.cs b/source/core/Coflo.Abstractions/Variables/Model/VariableDefinition.cs new file mode 100644 index 0000000..a36687d --- /dev/null +++ b/source/core/Coflo.Abstractions/Variables/Model/VariableDefinition.cs @@ -0,0 +1,52 @@ +using Coflo.Abstractions.Variables.Enums; + +namespace Coflo.Abstractions.Variables.Model; + +public class VariableDefinition +{ + public string Name { get; set; } = null!; + public VariableType VariableType { get; set; } = VariableType.String; + public bool IsArray { get; set; } + public object? DefaultValue { get; private set; } + public bool Nullable { get; set; } + public bool Persist { get; set; } = true; + public VariableDefinition(string name, VariableType variableType, bool isArray, object? defaultValue) + { + Name = name; + VariableType = variableType; + IsArray = isArray; + + if (SetDefaultValue(defaultValue)) + throw new ArgumentException($"Invalid default value for variable type {variableType}"); + } + + public VariableDefinition(VariableDefinition variableDefinition) : this(variableDefinition.Name, + variableDefinition.VariableType, variableDefinition.IsArray, variableDefinition.DefaultValue) + { + } + + private bool SetDefaultValue(object? value) + { + switch (value) + { + case null: + DefaultValue = null; + return true; + case string when VariableType == VariableType.String: + DefaultValue = value; + return true; + case int when VariableType == VariableType.Number: + DefaultValue = value; + return true; + case bool when VariableType == VariableType.Boolean: + DefaultValue = value; + return true; + } + + if (value.GetType() != typeof(object) || VariableType != VariableType.Object) return false; + + DefaultValue = value; + + return true; + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Variables/Model/VariableInstance.cs b/source/core/Coflo.Abstractions/Variables/Model/VariableInstance.cs new file mode 100644 index 0000000..c12d736 --- /dev/null +++ b/source/core/Coflo.Abstractions/Variables/Model/VariableInstance.cs @@ -0,0 +1,61 @@ +using Coflo.Abstractions.Variables.Enums; + +namespace Coflo.Abstractions.Variables.Model; + +public class VariableInstance : VariableDefinition +{ + public VariableInstance(VariableDefinition definition) : base(definition) + { + + } + + public object? Value { get; private set; } + + public void SetValue(object? value) + { + if (value == null) + { + Value = null; + return; + } + + switch (value) + { + case string when VariableType == VariableType.String: + Value = value; + return; + case int when VariableType == VariableType.Number: + Value = value; + return; + case bool when VariableType == VariableType.Boolean: + Value = value; + return; + } + + if (value.GetType() != typeof(object) || VariableType != VariableType.Object) return; + + Value = value; + } + + public bool ValidateValue(object? value) + { + if (value == null) + { + return true; + } + + switch (value) + { + case string when VariableType == VariableType.String: + return true; + case int when VariableType == VariableType.Number: + return true; + case bool when VariableType == VariableType.Boolean: + return true; + } + + if (value.GetType() != typeof(object) || VariableType != VariableType.Object) return false; + + return true; + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflow.cs b/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflow.cs deleted file mode 100644 index eb880aa..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflow.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Coflo.Abstractions.Workflow.Contracts; - -public interface IWorkflow - where TData : new() -{ - long Id { get; } - int Version { get; } - - void Build(IWorkflowBuilder builder); -} - -public interface IWorkflow : IWorkflow -{ -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowBuilder.cs b/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowBuilder.cs deleted file mode 100644 index 4aa33d9..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowBuilder.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Steps.Contracts; -using Coflo.Abstractions.Steps.Models; -using Coflo.Abstractions.Workflow.Enums; -using Coflo.Abstractions.Workflow.Models; - -namespace Coflo.Abstractions.Workflow.Contracts; - -public interface IWorkflowBuilder -{ - List Steps { get; } - - int LastStep { get; } - - IWorkflowBuilder UseData(); - - WorkflowDefinition Build(string id, int version); - - void AddStep(WorkflowStep step); - - void AttachBranch(IWorkflowBuilder branch); -} - -public interface IWorkflowBuilder : IWorkflowBuilder, IWorkflowModifier -{ - IStepBuilder StartWith(Action> stepSetup = null) where TStep : IStepBody; - - IStepBuilder StartWith(Func body); - - IStepBuilder StartWith(Action body); - - IEnumerable GetUpstreamSteps(int id); - - IWorkflowBuilder UseDefaultErrorBehavior(WorkflowErrorHandling behavior, TimeSpan? retryInterval = null); - - IWorkflowBuilder CreateBranch(); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowController.cs b/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowController.cs deleted file mode 100644 index 24c3647..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowController.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Coflo.Abstractions.Workflow.Contracts; - -public interface IWorkflowController -{ - Task StartWorkflow(string workflowId, object data = null, string reference = null); - Task StartWorkflow(string workflowId, int? version, object data = null, string reference = null); - - Task StartWorkflow(string workflowId, TData data = null, string reference = null) - where TData : class, new(); - - Task StartWorkflow(string workflowId, int? version, TData data = null, string reference = null) - where TData : class, new(); - - Task PublishEvent(string eventName, string eventKey, object eventData, DateTime? effectiveDate = null); - void RegisterWorkflow() where TWorkflow : IWorkflow; - void RegisterWorkflow() where TWorkflow : IWorkflow where TData : new(); - - /// - /// Suspend the execution of a given workflow until .ResumeWorkflow is called - /// - /// - /// - Task SuspendWorkflow(string workflowId); - - /// - /// Resume a previously suspended workflow - /// - /// - /// - Task ResumeWorkflow(string workflowId); - - /// - /// Permanently terminate the exeuction of a given workflow - /// - /// - /// - Task TerminateWorkflow(string workflowId); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowModifier.cs b/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowModifier.cs deleted file mode 100644 index 4686462..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Contracts/IWorkflowModifier.cs +++ /dev/null @@ -1,197 +0,0 @@ -using System.Collections; -using System.Linq.Expressions; -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Primitives; -using Coflo.Abstractions.Primitives.Contracts; -using Coflo.Abstractions.Primitives.Control; -using Coflo.Abstractions.Primitives.Iteration; -using Coflo.Abstractions.Primitives.Recurring; -using Coflo.Abstractions.Steps.Contracts; -using Coflo.Abstractions.Steps.Models; -using ActionStepBody = Coflo.Abstractions.Steps.Models.ActionStepBody; -using InlineStepBody = Coflo.Abstractions.Steps.Models.InlineStepBody; - -namespace Coflo.Abstractions.Workflow.Contracts; - -public interface IWorkflowModifier - where TStepBody : IStepBody - -{ - /// - /// Specify the next step in the workflow - /// - /// The type of the step to execute - /// Configure additional parameters for this step - /// - IStepBuilder Then(Action> stepSetup = null) where TStep : IStepBody; - - /// - /// Specify the next step in the workflow - /// - /// - /// - /// - IStepBuilder Then(IStepBuilder newStep) where TStep : IStepBody; - - /// - /// Specify an inline next step in the workflow - /// - /// - /// - IStepBuilder Then(Func body); - - /// - /// Specify an inline next step in the workflow - /// - /// - /// - IStepBuilder Then(Action body); - - /// - /// Wait here until to specified event is published - /// - /// The name used to identify the kind of event to wait for - /// A specific key value within the context of the event to wait for - /// Listen for events as of this effective date - /// A conditon that when true will cancel this WaitFor - /// - IStepBuilder WaitFor(string eventName, Expression> eventKey, - Expression> effectiveDate = null, Expression> cancelCondition = null); - - /// - /// Wait here until to specified event is published - /// - /// The name used to identify the kind of event to wait for - /// A specific key value within the context of the event to wait for - /// Listen for events as of this effective date - /// A conditon that when true will cancel this WaitFor - /// - IStepBuilder WaitFor(string eventName, - Expression> eventKey, - Expression> effectiveDate = null, Expression> cancelCondition = null); - - /// - /// Wait for a specified period - /// - /// - /// - IStepBuilder Delay(Expression> period); - - /// - /// Evaluate an expression and take a different path depending on the value - /// - /// Expression to evaluate for decision - /// - IStepBuilder Decide(Expression> expression); - - /// - /// Execute a block of steps, once for each item in a collection in a parallel foreach - /// - /// Resolves a collection for iterate over - /// - IContainerStepBuilder ForEach(Expression> collection); - - /// - /// Execute a block of steps, once for each item in a collection in a RunParallel foreach - /// - /// Resolves a collection for iterate over - /// - IContainerStepBuilder ForEach(Expression> collection, - Expression> runParallel); - - /// - /// Execute a block of steps, once for each item in a collection in a RunParallel foreach - /// - /// Resolves a collection for iterate over - /// - IContainerStepBuilder ForEach( - Expression> collection, - Expression> runParallel); - - /// - /// Repeat a block of steps until a condition becomes true - /// - /// Resolves a condition to break out of the while loop - /// - IContainerStepBuilder While(Expression> condition); - - /// - /// Repeat a block of steps until a condition becomes true - /// - /// Resolves a condition to break out of the while loop - /// - IContainerStepBuilder While(Expression> condition); - - /// - /// Execute a block of steps if a condition is true - /// - /// Resolves a condition to evaluate - /// - IContainerStepBuilder If(Expression> condition); - - /// - /// Execute a block of steps if a condition is true - /// - /// Resolves a condition to evaluate - /// - IContainerStepBuilder If(Expression> condition); - - /// - /// Configure an outcome for this step, then wire it to a sequence - /// - /// - /// - IContainerStepBuilder When(Expression> outcomeValue, - string label = null); - - /// - /// Execute multiple blocks of steps in parallel - /// - /// - IParallelStepBuilder Parallel(); - - /// - /// Execute a sequence of steps in a container - /// - /// - IStepBuilder Saga(Action> builder); - - /// - /// Schedule a block of steps to execute in parallel sometime in the future - /// - /// The time span to wait before executing the block - /// - IContainerStepBuilder Schedule(Expression> time); - - /// - /// Schedule a block of steps to execute in parallel sometime in the future at a recurring interval - /// - /// The time span to wait between recurring executions - /// Resolves a condition to stop the recurring task - /// - IContainerStepBuilder Recur(Expression> interval, - Expression> until); - - /// - /// Wait here until an external activity is complete - /// - /// The name used to identify the activity to wait for - /// The data to pass the external activity worker - /// Listen for events as of this effective date - /// A conditon that when true will cancel this WaitFor - /// - IStepBuilder Activity(string activityName, Expression> parameters = null, - Expression> effectiveDate = null, Expression> cancelCondition = null); - - /// - /// Wait here until an external activity is complete - /// - /// The name used to identify the activity to wait for - /// The data to pass the external activity worker - /// Listen for events as of this effective date - /// A conditon that when true will cancel this WaitFor - /// - IStepBuilder Activity(Expression> activityName, - Expression> parameters = null, - Expression> effectiveDate = null, Expression> cancelCondition = null); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Enums/ExecutionPipelineDirective.cs b/source/core/Coflo.Abstractions/Workflow/Enums/ExecutionPipelineDirective.cs deleted file mode 100644 index 0a681b5..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Enums/ExecutionPipelineDirective.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Coflo.Abstractions.Workflow.Enums; - -public enum ExecutionPipelineDirective -{ - Next = 0, - Defer = 1, - EndWorkflow = 2 -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Enums/PointerStatus.cs b/source/core/Coflo.Abstractions/Workflow/Enums/PointerStatus.cs deleted file mode 100644 index 83d5881..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Enums/PointerStatus.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Coflo.Abstractions.Workflow.Enums; - -public enum PointerStatus -{ - Legacy = 0, - Pending = 1, - Running = 2, - Complete = 3, - Sleeping = 4, - WaitingForEvent = 5, - Failed = 6, - Compensated = 7, - Cancelled = 8, - PendingPredecessor = 9 -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Enums/WorkflowErrorHandling.cs b/source/core/Coflo.Abstractions/Workflow/Enums/WorkflowErrorHandling.cs deleted file mode 100644 index ef83c82..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Enums/WorkflowErrorHandling.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Coflo.Abstractions.Workflow.Enums; - -public enum WorkflowErrorHandling -{ - Retry = 0, - Suspend = 1, - Terminate = 2, - Compensate = 3 -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Enums/WorkflowStatus.cs b/source/core/Coflo.Abstractions/Workflow/Enums/WorkflowStatus.cs deleted file mode 100644 index 99e6429..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Enums/WorkflowStatus.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Coflo.Abstractions.Workflow.Enums; - -public enum WorkflowStatus -{ - Runnable = 0, - Suspended = 1, - Complete = 2, - Terminated = 3, -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowDefinitionLoadException.cs b/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowDefinitionLoadException.cs deleted file mode 100644 index e34d139..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowDefinitionLoadException.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Coflo.Abstractions.Workflow.Exceptions; - -public class WorkflowDefinitionLoadException : Exception -{ - public WorkflowDefinitionLoadException(string message) - : base(message) - { - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowLockedException.cs b/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowLockedException.cs deleted file mode 100644 index 2b2c64a..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowLockedException.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Coflo.Abstractions.Workflow.Exceptions; - -public class WorkflowLockedException : Exception -{ - public WorkflowLockedException(): base() - { - // - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowNotRegisteredException.cs b/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowNotRegisteredException.cs deleted file mode 100644 index e170cd6..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Exceptions/WorkflowNotRegisteredException.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Coflo.Abstractions.Workflow.Exceptions; - -public class WorkflowNotRegisteredException -{ - -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Models/ExecutionPointer.cs b/source/core/Coflo.Abstractions/Workflow/Models/ExecutionPointer.cs deleted file mode 100644 index 921958a..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Models/ExecutionPointer.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Coflo.Abstractions.Workflow.Enums; - -namespace Coflo.Abstractions.Workflow.Models; - -public class ExecutionPointer -{ - private IReadOnlyCollection _scope = new List(); - - public string Id { get; set; } - - public int StepId { get; set; } - - public bool Active { get; set; } - - public DateTime? SleepUntil { get; set; } - - public object PersistenceData { get; set; } - - public DateTime? StartTime { get; set; } - - public DateTime? EndTime { get; set; } - - public string EventName { get; set; } - - public string EventKey { get; set; } - - public bool EventPublished { get; set; } - - public object EventData { get; set; } - - public Dictionary ExtensionAttributes { get; set; } = new(); - - public string StepName { get; set; } - - public int RetryCount { get; set; } - - public List Children { get; set; } = new(); - - public object ContextItem { get; set; } - - public string PredecessorId { get; set; } - - public object Outcome { get; set; } - - public PointerStatus Status { get; set; } = PointerStatus.Legacy; - - public IReadOnlyCollection Scope - { - get => _scope; - set => _scope = new List(value); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Models/ExecutionPointerCollection.cs b/source/core/Coflo.Abstractions/Workflow/Models/ExecutionPointerCollection.cs deleted file mode 100644 index e92b081..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Models/ExecutionPointerCollection.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System.Collections; -using Coflo.Abstractions.Workflow.Enums; - -namespace Coflo.Abstractions.Workflow.Models; - - public class ExecutionPointerCollection : ICollection - { - private readonly Dictionary _dictionary = new(); - private readonly Dictionary> _scopeMap = new(); - - public ExecutionPointerCollection() - { - } - - public ExecutionPointerCollection(int capacity) - { - _dictionary = new Dictionary(capacity); - } - - public ExecutionPointerCollection(ICollection pointers) - { - foreach (var ptr in pointers) - { - Add(ptr); - } - } - - public IEnumerator GetEnumerator() - { - return _dictionary.Values.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public ExecutionPointer FindById(string id) - { - if (!_dictionary.ContainsKey(id)) - return null; - - return _dictionary[id]; - } - - public ICollection FindByScope(string stackFrame) - { - if (!_scopeMap.ContainsKey(stackFrame)) - return new List(); - - return _scopeMap[stackFrame]; - } - - public void Add(ExecutionPointer item) - { - _dictionary.Add(item.Id, item); - - foreach (var stackFrame in item.Scope) - { - if (!_scopeMap.ContainsKey(stackFrame)) - _scopeMap.Add(stackFrame, new List()); - _scopeMap[stackFrame].Add(item); - } - } - - public void Clear() - { - _dictionary.Clear(); - _scopeMap.Clear(); - } - - public bool Contains(ExecutionPointer item) - { - return _dictionary.ContainsValue(item); - } - - public void CopyTo(ExecutionPointer[] array, int arrayIndex) - { - _dictionary.Values.CopyTo(array, arrayIndex); - } - - public bool Remove(ExecutionPointer item) - { - foreach (var stackFrame in item.Scope) - { - _scopeMap[stackFrame].Remove(item); - } - - return _dictionary.Remove(item.Id); - } - - public ExecutionPointer Find(Predicate match) - { - return _dictionary.Values.FirstOrDefault(x => match(x)); - } - - public ICollection FindByStatus(PointerStatus status) - { - //TODO: track states in hash table - return _dictionary.Values.Where(x => x.Status == status).ToList(); - } - - public int Count => _dictionary.Count; - public bool IsReadOnly => false; - } \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowDefinition.cs b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowDefinition.cs deleted file mode 100644 index f2a60bc..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowDefinition.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Coflo.Abstractions.Workflow.Enums; - -namespace Coflo.Abstractions.Workflow.Models; - -public class WorkflowDefinition -{ - public string Id { get; set; } - public int Version { get; set; } - public string Description { get; set; } - public WorkflowStepCollection Steps { get; set; } = new(); - public Type DataType { get; set; } - public WorkflowErrorHandling DefaultErrorBehavior { get; set; } - public Type OnPostMiddlewareError { get; set; } - public Type OnExecuteMiddlewareError { get; set; } - public TimeSpan? DefaultErrorRetryInterval { get; set; } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowExecutorResult.cs b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowExecutorResult.cs deleted file mode 100644 index b91a1b8..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowExecutorResult.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Coflo.Abstractions.Events.Models; -using Coflo.Abstractions.Execution.Models; - -namespace Coflo.Abstractions.Workflow.Models; - -public class WorkflowExecutorResult -{ - public List Subscriptions { get; set; } = new(); - public List Errors { get; set; } = new(); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowInstance.cs b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowInstance.cs deleted file mode 100644 index 223d1d8..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowInstance.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Coflo.Abstractions.Workflow.Enums; - -namespace Coflo.Abstractions.Workflow.Models; - -public class WorkflowInstance -{ - public long Id { get; set; } - - public string WorkflowDefinitionId { get; set; } - - public int Version { get; set; } - - public string Description { get; set; } - - public string Reference { get; set; } - - public ExecutionPointerCollection ExecutionPointers { get; set; } = new(); - - public long? NextExecution { get; set; } - - public WorkflowStatus Status { get; set; } - - public object Data { get; set; } - - public DateTime CreateTime { get; set; } - - public DateTime? CompleteTime { get; set; } - - public bool IsBranchComplete(string parentId) - { - return ExecutionPointers - .FindByScope(parentId) - .All(x => x.EndTime != null); - } -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowResult.cs b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowResult.cs deleted file mode 100644 index 16c7dd0..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowResult.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Coflo.Abstractions.Events.Models; -using Coflo.Abstractions.Execution.Models; - -namespace Coflo.Abstractions.Workflow.Models; - -public class WorkflowResult -{ - public List Subscriptions { get; set; } = new(); - public List Errors { get; set; } = new(); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowStep.cs b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowStep.cs deleted file mode 100644 index 6775ceb..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowStep.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System.Linq.Expressions; -using Coflo.Abstractions.Execution.Models; -using Coflo.Abstractions.Expr.Contracts; -using Coflo.Abstractions.Steps.Contracts; -using Coflo.Abstractions.Steps.Models; -using Coflo.Abstractions.Workflow.Contracts; -using Coflo.Abstractions.Workflow.Enums; - -namespace Coflo.Abstractions.Workflow.Models; - -public abstract class WorkflowStep -{ - public abstract Type BodyType { get; } - public virtual int Id { get; set; } - - public virtual string Name { get; set; } - - public virtual string ExternalId { get; set; } - - public virtual List Children { get; set; } = new(); - - public virtual List Outcomes { get; set; } = new(); - - public virtual List Inputs { get; set; } = new(); - - public virtual List Outputs { get; set; } = new(); - - public virtual WorkflowErrorHandling? ErrorBehavior { get; set; } - - public virtual TimeSpan? RetryInterval { get; set; } - - public virtual int? CompensationStepId { get; set; } - - public virtual bool ResumeChildrenAfterCompensation => true; - - public virtual bool RevertChildrenAfterCompensation => false; - - public virtual LambdaExpression CancelCondition { get; set; } - - public bool ProceedOnCancel { get; set; } = false; - - public virtual ExecutionPipelineDirective InitForExecution(WorkflowResult result, - WorkflowDefinition definition, WorkflowInstance workflow, ExecutionPointer executionPointer) - { - return ExecutionPipelineDirective.Next; - } - - public virtual ExecutionPipelineDirective BeforeExecute(WorkflowResult result, - IStepExecutionContext context, ExecutionPointer executionPointer, IStepBody body) - { - return ExecutionPipelineDirective.Next; - } - - public virtual void AfterExecute(WorkflowResult result, IStepExecutionContext context, - ExecutionResult stepResult, ExecutionPointer executionPointer) - { - } - - public virtual void PrimeForRetry(ExecutionPointer pointer) - { - } - - /// - /// Called after every workflow execution round, - /// every exectuon pointer with no end time, even if this step was not executed in this round - /// - /// - /// - /// - /// - public virtual void AfterWorkflowIteration(WorkflowResult result, WorkflowDefinition defintion, - WorkflowInstance workflow, ExecutionPointer executionPointer) - { - } - - public virtual IStepBody ConstructBody(IServiceProvider serviceProvider) - { - IStepBody body = (serviceProvider.GetService(BodyType) as IStepBody); - if (body == null) - { - var stepCtor = BodyType.GetConstructor(new Type[] { }); - if (stepCtor != null) - body = (stepCtor.Invoke(null) as IStepBody); - } - - return body; - } -} - -public class WorkflowStep : WorkflowStep - where TStepBody : IStepBody -{ - public override Type BodyType => typeof(TStepBody); -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowStepCollection.cs b/source/core/Coflo.Abstractions/Workflow/Models/WorkflowStepCollection.cs deleted file mode 100644 index 305da98..0000000 --- a/source/core/Coflo.Abstractions/Workflow/Models/WorkflowStepCollection.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Collections; - -namespace Coflo.Abstractions.Workflow.Models; - -public class WorkflowStepCollection : ICollection -{ - private readonly Dictionary _dictionary = new(); - - public WorkflowStepCollection() - { - } - - public WorkflowStepCollection(int capacity) - { - _dictionary = new Dictionary(capacity); - } - - public WorkflowStepCollection(ICollection steps) - { - foreach (var step in steps) - { - Add(step); - } - } - - public IEnumerator GetEnumerator() - { - return _dictionary.Values.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public WorkflowStep FindById(int id) - { - if (!_dictionary.ContainsKey(id)) - return null; - - return _dictionary[id]; - } - - public void Add(WorkflowStep item) - { - _dictionary.Add(item.Id, item); - } - - public void Clear() - { - _dictionary.Clear(); - } - - public bool Contains(WorkflowStep item) - { - return _dictionary.ContainsValue(item); - } - - public void CopyTo(WorkflowStep[] array, int arrayIndex) - { - _dictionary.Values.CopyTo(array, arrayIndex); - } - - public bool Remove(WorkflowStep item) - { - return _dictionary.Remove(item.Id); - } - - public WorkflowStep Find(Predicate match) - { - return _dictionary.Values.FirstOrDefault(x => match(x)); - } - - public int Count => _dictionary.Count; - public bool IsReadOnly => false; -} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Commands/StartWorkflowExecutionCommand.cs b/source/core/Coflo.Abstractions/Workflows/Commands/StartWorkflowExecutionCommand.cs new file mode 100644 index 0000000..436d29a --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Commands/StartWorkflowExecutionCommand.cs @@ -0,0 +1,13 @@ +using Mediator; + +namespace Coflo.Abstractions.Workflows.Commands; + +public class StartWorkflowExecutionCommand : ICommand +{ + public long WorkflowInstanceId { get; set; } + + public StartWorkflowExecutionCommand(long workflowInstanceId) + { + WorkflowInstanceId = workflowInstanceId; + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Contracts/IWorkflowDefinition.cs b/source/core/Coflo.Abstractions/Workflows/Contracts/IWorkflowDefinition.cs new file mode 100644 index 0000000..8275c91 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Contracts/IWorkflowDefinition.cs @@ -0,0 +1,12 @@ +using Coflo.Abstractions.Activities.Models; +using Coflo.Abstractions.Connections.Contracts; +using Coflo.Abstractions.Variables.Model; + +namespace Coflo.Abstractions.Workflows.Contracts; + +public interface IWorkflowDefinition +{ + public long WorkflowDefinitionId { get; } + public string Name { get; } + public ICollection Versions { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Contracts/IWorkflowDefinitionVersion.cs b/source/core/Coflo.Abstractions/Workflows/Contracts/IWorkflowDefinitionVersion.cs new file mode 100644 index 0000000..680d87b --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Contracts/IWorkflowDefinitionVersion.cs @@ -0,0 +1,16 @@ +using Coflo.Abstractions.Activities.Models; +using Coflo.Abstractions.Connections.Contracts; +using Coflo.Abstractions.Variables.Model; + +namespace Coflo.Abstractions.Workflows.Contracts; + +public interface IWorkflowDefinitionVersion +{ + public long WorkflowVersionId { get; } + public long WorkflowDefinitionId { get; } + public IWorkflowDefinition WorkflowDefinition { get; } + + public ICollection Connections { get; set; } + public ICollection ActivityDefinitions { get; set; } + public ICollection VariableDefinitions { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Contracts/IWorkflowInstance.cs b/source/core/Coflo.Abstractions/Workflows/Contracts/IWorkflowInstance.cs new file mode 100644 index 0000000..97fd68f --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Contracts/IWorkflowInstance.cs @@ -0,0 +1,20 @@ +using Coflo.Abstractions.Connections.Contracts; +using Coflo.Abstractions.Variables.Model; +using Coflo.Abstractions.Workflows.Models; +using NodaTime; + +namespace Coflo.Abstractions.Workflows.Contracts; + +public interface IWorkflowInstance +{ + public long InstanceId { get; set; } + public long WorkflowDefinitionId { get; set; } + public long WorkflowVersionId { get; set; } + + public IVariableCollection Variables { get; set; } + List WorkflowLogs { get; set; } + public WorkflowStatus Status { get; set; } + + public Instant CreatedAt { get; set; } + public Instant? CompletedAt { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Enums/WorkflowStatus.cs b/source/core/Coflo.Abstractions/Workflows/Enums/WorkflowStatus.cs new file mode 100644 index 0000000..03388bd --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Enums/WorkflowStatus.cs @@ -0,0 +1,10 @@ +namespace Coflo.Abstractions.Workflows; + +public enum WorkflowStatus +{ + Created, + Running, + Completed, + Failed, + Cancelled +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinition.cs b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinition.cs new file mode 100644 index 0000000..1dc4748 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinition.cs @@ -0,0 +1,13 @@ +using Coflo.Abstractions.Activities.Models; +using Coflo.Abstractions.Connections.Contracts; +using Coflo.Abstractions.Variables.Model; +using Coflo.Abstractions.Workflows.Contracts; + +namespace Coflo.Abstractions.Workflows.Models; + +public class WorkflowDefinition : IWorkflowDefinition +{ + public long WorkflowDefinitionId { get; set; } + public string Name { get; set; } + public ICollection Versions { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinitionVersion.cs b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinitionVersion.cs new file mode 100644 index 0000000..dccd2e8 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinitionVersion.cs @@ -0,0 +1,17 @@ +using Coflo.Abstractions.Activities.Models; +using Coflo.Abstractions.Connections.Contracts; +using Coflo.Abstractions.Variables.Model; +using Coflo.Abstractions.Workflows.Contracts; + +namespace Coflo.Abstractions.Workflows.Models; + +public class WorkflowDefinitionVersion : IWorkflowDefinitionVersion +{ + public long WorkflowVersionId { get; set; } + public long WorkflowDefinitionId { get; set; } + public IWorkflowDefinition WorkflowDefinition { get; set; } + + public ICollection Connections { get; set; } + public ICollection ActivityDefinitions { get; set; } + public ICollection VariableDefinitions { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Models/WorkflowInstance.cs b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowInstance.cs new file mode 100644 index 0000000..3dbad00 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowInstance.cs @@ -0,0 +1,19 @@ +using Coflo.Abstractions.Variables.Model; +using Coflo.Abstractions.Workflows.Contracts; +using NodaTime; + +namespace Coflo.Abstractions.Workflows.Models; + +public class WorkflowInstance : IWorkflowInstance +{ + public long InstanceId { get; set; } + public long WorkflowDefinitionId { get; set; } + public long WorkflowVersionId { get; set; } + + public IVariableCollection Variables { get; set; } + public List WorkflowLogs { get; set; } + + public WorkflowStatus Status { get; set; } + public Instant CreatedAt { get; set; } + public Instant? CompletedAt { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Models/WorkflowLogEntry.cs b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowLogEntry.cs new file mode 100644 index 0000000..474c0ca --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowLogEntry.cs @@ -0,0 +1,20 @@ +namespace Coflo.Abstractions.Workflows.Models; + +public class WorkflowLogEntry +{ + public long WorkflowInstanceId { get; set; } + public string Message { get; set; } + public object[] Params { get; set; } + + public WorkflowLogEntry(long workflowInstanceId, string message, params object[] @params) + { + WorkflowInstanceId = workflowInstanceId; + Message = message; + Params = @params; + } + + public override string ToString() + { + return string.Format(Message, Params); + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Notifications/ActivityCompletedNotification.cs b/source/core/Coflo.Abstractions/Workflows/Notifications/ActivityCompletedNotification.cs new file mode 100644 index 0000000..6ed718c --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Notifications/ActivityCompletedNotification.cs @@ -0,0 +1,12 @@ +using Coflo.Abstractions.Variables.Model; +using Mediator; + +namespace Coflo.Abstractions.Workflows.Notifications; + +public class ActivityCompletedNotification : INotification +{ + public long NodeId { get; set; } + public long WorkflowInstanceId { get; set; } + public IVariableCollection Variables { get; set; } + public string Outcome { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Notifications/ActivityFailedNotification.cs b/source/core/Coflo.Abstractions/Workflows/Notifications/ActivityFailedNotification.cs new file mode 100644 index 0000000..fe4ae48 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Notifications/ActivityFailedNotification.cs @@ -0,0 +1,8 @@ +namespace Coflo.Abstractions.Workflows.Notifications; + +public class ActivityFailedNotification +{ + public long NodeId { get; set; } + public long WorkflowInstanceId { get; set; } + public string Error { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Services/IWorkflowExecutor.cs b/source/core/Coflo.Abstractions/Workflows/Services/IWorkflowExecutor.cs new file mode 100644 index 0000000..2e8a1a9 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Services/IWorkflowExecutor.cs @@ -0,0 +1,17 @@ +using Coflo.Abstractions.Variables.Model; +using Coflo.Abstractions.Workflows.Notifications; + +namespace Coflo.Abstractions.Workflows.Services; + +public interface IWorkflowExecutor +{ + Task InitializeWorkflow(long workflowDefinitionId, VariableCollection variables); + + Task ExecuteWorkflow(long workflowInstanceId); + + Task ActivityCompleted(ActivityCompletedNotification completedNotification); + + Task ActivityFailed(ActivityFailedNotification failedNotification); + + Task WorkflowCompleted(long workflowInstanceId); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Stores/IWorkflowDefinitionStore.cs b/source/core/Coflo.Abstractions/Workflows/Stores/IWorkflowDefinitionStore.cs new file mode 100644 index 0000000..4daaabb --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Stores/IWorkflowDefinitionStore.cs @@ -0,0 +1,16 @@ +using Coflo.Abstractions.Workflows.Models; + +namespace Coflo.Abstractions.Workflows.Stores; + +public interface IWorkflowDefinitionStore +{ + Task GetWorkflowDefinitionAsync(long workflowDefinitionId); + Task GetWorkflowDefinitionVersionAsync(long workflowDefinitionId, long workflowVersionId); + Task> GetWorkflowDefinitionVersionsAsync(long workflowDefinitionId); + + Task SaveWorkflowDefinitionAsync(WorkflowDefinition workflowDefinition); + Task SaveWorkflowDefinitionVersionAsync(WorkflowDefinition workflowDefinition); + + Task DeleteWorkflowDefinitionAsync(long workflowDefinitionId); + Task DeleteWorkflowDefinitionVersionAsync(long workflowDefinitionId, long workflowVersionId); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Stores/IWorkflowInstanceStore.cs b/source/core/Coflo.Abstractions/Workflows/Stores/IWorkflowInstanceStore.cs new file mode 100644 index 0000000..da2295d --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Stores/IWorkflowInstanceStore.cs @@ -0,0 +1,13 @@ +using Coflo.Abstractions.Workflows.Models; + +namespace Coflo.Abstractions.Workflows.Stores; + +public interface IWorkflowInstanceStore +{ + Task GetWorkflowInstanceAsync(long workflowInstanceId); + Task> GetWorkflowInstancesAsync(long workflowDefinitionId); + + Task SaveWorkflowInstanceAsync(WorkflowInstance workflowInstance, bool persist = false); + + Task DeleteWorkflowInstanceAsync(long workflowInstanceId); +} \ No newline at end of file diff --git a/source/core/Coflo.SDK/Coflo.SDK.csproj b/source/core/Coflo.Core/Coflo.Core.csproj similarity index 100% rename from source/core/Coflo.SDK/Coflo.SDK.csproj rename to source/core/Coflo.Core/Coflo.Core.csproj diff --git a/source/core/Coflo.SDK/Activities/ActivityBase.cs b/source/core/Coflo.SDK/Activities/ActivityBase.cs deleted file mode 100644 index c7c9abb..0000000 --- a/source/core/Coflo.SDK/Activities/ActivityBase.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Coflo.Abstractions.Models.Activity; - -namespace Coflo.SDK.Activities; - -public abstract class ActivityBase : ActivityDefinition -{ - public abstract ActivityResult ExecuteActivityAsync(ActivityExecutionContext context); -} \ No newline at end of file diff --git a/source/hosting/Coflo.Core.Snowflake/Coflo.Core.Snowflake.csproj b/source/hosting/Coflo.Core.Snowflake/Coflo.Core.Snowflake.csproj index da7176b..e74d845 100644 --- a/source/hosting/Coflo.Core.Snowflake/Coflo.Core.Snowflake.csproj +++ b/source/hosting/Coflo.Core.Snowflake/Coflo.Core.Snowflake.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/source/hosting/Coflo.Core.Snowflake/Generators/IIdGenerator.cs b/source/hosting/Coflo.Core.Snowflake/Generators/IIdGenerator.cs index 2bcdf17..130b277 100644 --- a/source/hosting/Coflo.Core.Snowflake/Generators/IIdGenerator.cs +++ b/source/hosting/Coflo.Core.Snowflake/Generators/IIdGenerator.cs @@ -1,6 +1,4 @@ -using Coflo.Core.Snowflake.Models; - -namespace Coflo.Core.Snowflake.Generators; +namespace Coflo.Core.Snowflake.Generators; public interface IIdGenerator { diff --git a/source/hosting/Coflo.Core.Snowflake/Generators/IdGenerator.cs b/source/hosting/Coflo.Core.Snowflake/Generators/IdGenerator.cs index ab89416..968eeda 100644 --- a/source/hosting/Coflo.Core.Snowflake/Generators/IdGenerator.cs +++ b/source/hosting/Coflo.Core.Snowflake/Generators/IdGenerator.cs @@ -1,5 +1,4 @@ -using System.Data; -using Coflo.Core.Snowflake.Models; +using Coflo.Core.Snowflake.Models; using Microsoft.Extensions.Configuration; using NodaTime; @@ -7,83 +6,76 @@ namespace Coflo.Core.Snowflake.Generators; public class IdGenerator : IIdGenerator { - private readonly IClock _clock; - private readonly long _machineId; - private const long TimestampBits = 41; private const long SequenceBits = 8; private const long MachineIdBits = 63 - TimestampBits - SequenceBits; private const long Epoch = 1_638_400_000_000L; - private long _lastTimestamp = -1L; - private long _sequence = 0L; + private readonly IClock _clock; + private readonly long _machineId; private readonly Mutex _mutex; - + private long _lastTimestamp = -1L; + private long _sequence; + public IdGenerator(IClock clock, IConfiguration configuration) { _clock = clock; _machineId = configuration.GetValue("MachineId"); - + _mutex = new Mutex(); } - + public Task NextId() { var timestamp = GetTimestamp(); - + _mutex.WaitOne(); - + if (timestamp == _lastTimestamp) { - _sequence = (_sequence + 1) & ((1 << (int) SequenceBits) - 1); - - if (_sequence == 0) - { - timestamp = GetNextTimestamp(); - } + _sequence = (_sequence + 1) & ((1 << (int)SequenceBits) - 1); + + if (_sequence == 0) timestamp = GetNextTimestamp(); } else { _sequence = 0; } - + _lastTimestamp = timestamp; - + _mutex.ReleaseMutex(); - + return Task.FromResult(EncodeId(_clock.GetCurrentInstant(), _machineId, _sequence)); } - + private long GetTimestamp() { return _clock.GetCurrentInstant().ToUnixTimeMilliseconds() - Epoch; } - + private long GetNextTimestamp() { var timestamp = GetTimestamp(); - - while (timestamp <= _lastTimestamp) - { - timestamp = GetTimestamp(); - } + + while (timestamp <= _lastTimestamp) timestamp = GetTimestamp(); return timestamp; } - + internal static long EncodeId(Instant timestamp, long machineId, long sequence) { var timestampDelta = timestamp.ToUnixTimeMilliseconds() - Epoch; - var id = (timestampDelta << (int) (SequenceBits + MachineIdBits)) | (machineId << (int) SequenceBits) | sequence; - + var id = (timestampDelta << (int)(SequenceBits + MachineIdBits)) | (machineId << (int)SequenceBits) | sequence; + return id; } - + public static SnowflakeId DecodeId(long id) { - var sequence = id & ((1 << (int) SequenceBits) - 1); - var machineId = (id >> (int) SequenceBits) & ((1 << (int) MachineIdBits) - 1); - var timestamp = (id >> (int) (SequenceBits + MachineIdBits)) + Epoch; + var sequence = id & ((1 << (int)SequenceBits) - 1); + var machineId = (id >> (int)SequenceBits) & ((1 << (int)MachineIdBits) - 1); + var timestamp = (id >> (int)(SequenceBits + MachineIdBits)) + Epoch; return SnowflakeId.Create(id, Instant.FromUnixTimeMilliseconds(timestamp), machineId, sequence); } diff --git a/source/hosting/Coflo.Core.Snowflake/Models/SnowflakeId.cs b/source/hosting/Coflo.Core.Snowflake/Models/SnowflakeId.cs index 1c5cf53..a398c58 100644 --- a/source/hosting/Coflo.Core.Snowflake/Models/SnowflakeId.cs +++ b/source/hosting/Coflo.Core.Snowflake/Models/SnowflakeId.cs @@ -8,7 +8,7 @@ public class SnowflakeId public Instant Timestamp { get; private set; } public long MachineId { get; private set; } public long Sequence { get; private set; } - + internal static SnowflakeId Create(long id, Instant timestamp, long machineId, long sequence) { return new SnowflakeId diff --git a/source/hosting/Coflo.Hosting.Orchestrator/Coflo.Hosting.Orchestrator.csproj b/source/hosting/Coflo.Hosting.Orchestrator/Coflo.Hosting.Orchestrator.csproj index 6836c68..2ef04c3 100644 --- a/source/hosting/Coflo.Hosting.Orchestrator/Coflo.Hosting.Orchestrator.csproj +++ b/source/hosting/Coflo.Hosting.Orchestrator/Coflo.Hosting.Orchestrator.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/source/hosting/Coflo.Hosting.Orchestrator/Services/WorkflowExecutor.cs b/source/hosting/Coflo.Hosting.Orchestrator/Services/WorkflowExecutor.cs new file mode 100644 index 0000000..cb93431 --- /dev/null +++ b/source/hosting/Coflo.Hosting.Orchestrator/Services/WorkflowExecutor.cs @@ -0,0 +1,134 @@ +using Ardalis.GuardClauses; +using Coflo.Abstractions.Activities.Commands; +using Coflo.Abstractions.Activities.Stores; +using Coflo.Abstractions.Events; +using Coflo.Abstractions.Variables.Model; +using Coflo.Abstractions.Workflows; +using Coflo.Abstractions.Workflows.Commands; +using Coflo.Abstractions.Workflows.Models; +using Coflo.Abstractions.Workflows.Notifications; +using Coflo.Abstractions.Workflows.Services; +using Coflo.Abstractions.Workflows.Stores; +using Coflo.Core.Snowflake.Generators; +using NodaTime; + +namespace Coflo.Hosting.Orchestrator.Services; + +public class WorkflowExecutor : IWorkflowExecutor +{ + private readonly IWorkflowDefinitionStore _workflowDefinitionStore; + private readonly IWorkflowInstanceStore _workflowInstanceStore; + private readonly IActivityDefinitionStore _activityDefinitionStore; + private readonly IClock _clock; + private readonly IIdGenerator _idGenerator; + private readonly IEventPublisher _eventPublisher; + + public WorkflowExecutor(IWorkflowDefinitionStore workflowDefinitionStore, + IWorkflowInstanceStore workflowInstanceStore, IActivityDefinitionStore activityDefinitionStore, IClock clock, + IIdGenerator idGenerator, + IEventPublisher eventPublisher) + { + _workflowDefinitionStore = workflowDefinitionStore; + _workflowInstanceStore = workflowInstanceStore; + _activityDefinitionStore = activityDefinitionStore; + _clock = clock; + _idGenerator = idGenerator; + _eventPublisher = eventPublisher; + } + + public async Task InitializeWorkflow(long workflowDefinitionId, long workflowVersionId, + VariableCollection variables) + { + var definition = await _workflowDefinitionStore.GetWorkflowDefinitionAsync(workflowDefinitionId); + var time = _clock.GetCurrentInstant(); + + var instance = new WorkflowInstance + { + WorkflowDefinitionId = workflowDefinitionId, + WorkflowVersionId = workflowVersionId, + Variables = variables, + Status = WorkflowStatus.Created, + CreatedAt = time + }; + + instance.InstanceId = await _idGenerator.NextId(); + + await _workflowInstanceStore.SaveWorkflowInstanceAsync(instance); + + await _eventPublisher.PublishAsync(new StartWorkflowExecutionCommand(instance.InstanceId)); + + return instance.InstanceId; + } + + public async Task ExecuteWorkflow(long workflowInstanceId) + { + var workflowInstance = await _workflowInstanceStore.GetWorkflowInstanceAsync(workflowInstanceId); + var version = + await _workflowDefinitionStore.GetWorkflowDefinitionVersionAsync(workflowInstance.WorkflowDefinitionId, + workflowInstance.WorkflowVersionId); + + var startActivity = version.ActivityDefinitions.FirstOrDefault(x => x.ActivityName == "START"); + + Guard.Against.Null(startActivity, nameof(startActivity)); + + var firstConnection = + version.Connections.FirstOrDefault(x => x.ActivityId == startActivity.ActivityDefinitionId); + + Guard.Against.Null(firstConnection, nameof(firstConnection)); + + var nextActivity = + version.ActivityDefinitions.FirstOrDefault(x => x.ActivityDefinitionId == firstConnection.TargetActivityId); + + Guard.Against.Null(nextActivity, nameof(nextActivity)); + + var activityDefinition = + await _activityDefinitionStore.GetActivityDefinitionAsync(nextActivity.ActivityDefinitionId); + + await _eventPublisher.PublishAsync(new StartActivityCommand(workflowInstanceId, + activityDefinition.ActivityDefinitionId)); + + workflowInstance.Status = WorkflowStatus.Running; + + await _workflowInstanceStore.SaveWorkflowInstanceAsync(workflowInstance); + } + + public async Task ActivityCompleted(ActivityCompletedNotification completedNotification) + { + var workflowInstance = + await _workflowInstanceStore.GetWorkflowInstanceAsync(completedNotification.WorkflowInstanceId); + + var version = await _workflowDefinitionStore.GetWorkflowDefinitionVersionAsync( + workflowInstance.WorkflowDefinitionId, + workflowInstance.WorkflowVersionId); + + workflowInstance.WorkflowLogs.Add(new WorkflowLogEntry(workflowInstance.InstanceId, "Activity completed, {0}", + _clock.GetCurrentInstant())); + + var nextConnection = + version.Connections.FirstOrDefault(x => x.ActivityId == completedNotification.NodeId && x.Outcome == completedNotification.Outcome); + + if (nextConnection == null) + { + await WorkflowCompleted(workflowInstance.InstanceId); + return; + } + + await _eventPublisher.PublishAsync(new StartActivityCommand(workflowInstance.InstanceId, + nextConnection.TargetActivityId)); + } + + public Task ActivityFailed(ActivityFailedNotification failedNotification) + { + throw new NotImplementedException(); + } + + public Task RunNextActivity(long workflowInstanceId, long activityDefinitionId) + { + throw new NotImplementedException(); + } + + public Task WorkflowCompleted(long workflowInstanceId) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/source/hosting/Coflo.Hosting.Worker/Class1.cs b/source/hosting/Coflo.Hosting.Worker/Class1.cs deleted file mode 100644 index bc52155..0000000 --- a/source/hosting/Coflo.Hosting.Worker/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Coflo.Hosting.Worker; - -public class Class1 -{ -} \ No newline at end of file diff --git a/source/hosting/Coflo.Hosting.Worker/Coflo.Hosting.Worker.csproj b/source/hosting/Coflo.Hosting.Worker/Coflo.Hosting.Worker.csproj index 6836c68..2ef04c3 100644 --- a/source/hosting/Coflo.Hosting.Worker/Coflo.Hosting.Worker.csproj +++ b/source/hosting/Coflo.Hosting.Worker/Coflo.Hosting.Worker.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/source/hosting/Coflo.Hosting.Worker/Commands/StartActivityCommandHandler.cs b/source/hosting/Coflo.Hosting.Worker/Commands/StartActivityCommandHandler.cs new file mode 100644 index 0000000..f89f304 --- /dev/null +++ b/source/hosting/Coflo.Hosting.Worker/Commands/StartActivityCommandHandler.cs @@ -0,0 +1,109 @@ +using System.Data; +using System.Reflection; +using Ardalis.GuardClauses; +using Coflo.Abstractions.Activities.Attributes; +using Coflo.Abstractions.Activities.Commands; +using Coflo.Abstractions.Activities.Contracts; +using Coflo.Abstractions.Activities.Models; +using Coflo.Abstractions.Activities.Stores; +using Coflo.Abstractions.Events; +using Coflo.Abstractions.Variables.Model; +using Coflo.Abstractions.Workflows.Notifications; +using Coflo.Core.Snowflake.Generators; +using Mediator; +using Microsoft.Extensions.DependencyInjection; + +namespace Coflo.Hosting.Worker.Commands; + +public class StartActivityCommandHandler : ICommandHandler +{ + private readonly IActivityDefinitionStore _activityDefinitionStore; + private readonly IIdGenerator _idGenerator; + private readonly IServiceProvider _serviceProvider; + private readonly IEventPublisher _publisher; + + public StartActivityCommandHandler(IActivityDefinitionStore activityDefinitionStore, IIdGenerator idGenerator, + IServiceProvider serviceProvider, IEventPublisher publisher) + { + _activityDefinitionStore = activityDefinitionStore; + _idGenerator = idGenerator; + _serviceProvider = serviceProvider; + _publisher = publisher; + } + + public async ValueTask Handle(StartActivityCommand command, CancellationToken cancellationToken) + { + var activityDef = await _activityDefinitionStore.GetActivityDefinitionAsync(command.ActivityDefinitionId); + + var activityType = Type.GetType(activityDef.ActivityName, true, true); + + Guard.Against.Null(activityType); + + var activity = (IActivity)_serviceProvider.GetRequiredService(activityType); + + var executionContext = new ActivityExecutionContext(command.VariableCollection, command.WorkflowInstanceId, + await _idGenerator.NextId(), activityDef.ActivityName); + + PopulateInputVariablesToProperties(activityType, activity, activityDef, command.VariableCollection); + + var result = await activity.ExecuteAsync(executionContext); + + await _publisher.PublishAsync(new ActivityCompletedNotification + { + Variables = result.VariableCollection, + WorkflowInstanceId = command.WorkflowInstanceId, + Outcome = result.Outcome, + NodeId = command.ActivityDefinitionId + }); + + return Unit.Value; + } + + internal void PopulateInputVariablesToProperties(Type activityType, IActivity activity, + ActivityDefinition activityDefinition, + IVariableCollection variables) + { + var properties = activityType.GetProperties(); + + foreach (var property in properties) + { + var inputAttribute = property.GetCustomAttribute(); + + if (inputAttribute == null) continue; + var input = activityDefinition.InputMappings.FirstOrDefault(x => x.ActivityInputField == property.Name); + + if (input?.VariableDefinition?.Name == null) continue; + + var variable = variables[input.VariableDefinition.Name]; + + if (variable?.GetType() != property.GetType()) + throw new ConstraintException("Variable type does not match property type"); + + property.SetValue(activity, variable.Value); + } + } + + internal void PopulateOutputVariablesToProperties(Type activityType, IActivity activity, + ActivityDefinition activityDefinition, + IVariableCollection variables) + { + var properties = activityType.GetProperties(); + + foreach (var property in properties) + { + var outputAttribute = property.GetCustomAttribute(); + + if (outputAttribute == null) continue; + var output = activityDefinition.OutputMappings.FirstOrDefault(x => x.ActivityOutputField == property.Name); + + if (output?.VariableDefinition?.Name == null) continue; + + var variable = variables[output.VariableDefinition.Name]; + + if (variable?.GetType() != property.GetType()) + throw new ConstraintException("Variable type does not match property type"); + + property.SetValue(activity, variable.Value); + } + } +} \ No newline at end of file diff --git a/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Class1.cs b/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Class1.cs deleted file mode 100644 index 0f39bc7..0000000 --- a/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Coflo.Infrastructure.Persistance.MySQL; - -public class Class1 -{ -} \ No newline at end of file diff --git a/tests/core/Coflo.Core.Snowflake.Tests/Coflo.Core.Snowflake.Tests.csproj b/tests/core/Coflo.Core.Snowflake.Tests/Coflo.Core.Snowflake.Tests.csproj index 21d9ac4..0541b78 100644 --- a/tests/core/Coflo.Core.Snowflake.Tests/Coflo.Core.Snowflake.Tests.csproj +++ b/tests/core/Coflo.Core.Snowflake.Tests/Coflo.Core.Snowflake.Tests.csproj @@ -10,11 +10,11 @@ - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -26,7 +26,7 @@ - + diff --git a/tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs b/tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs index b7bd78e..062be60 100644 --- a/tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs +++ b/tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs @@ -1,10 +1,8 @@ -using System.Diagnostics; using System.Globalization; using Coflo.Core.Snowflake.Generators; using FluentAssertions; using FluentAssertions.Extensions; using Microsoft.Extensions.Configuration; -using Moq; using NodaTime; using NodaTime.Testing; using Xunit.Abstractions; @@ -13,20 +11,21 @@ namespace Coflo.Core.Snowflake.Tests; public class IdGeneratorTests { - private readonly ITestOutputHelper _testOutputHelper; - private readonly IdGenerator _idGenerator; - private readonly FakeClock _fakeClock; private readonly DateTimeFormatInfo _dateTimeFormat; + private readonly FakeClock _fakeClock; + private readonly IdGenerator _idGenerator; + private readonly ITestOutputHelper _testOutputHelper; public IdGeneratorTests(ITestOutputHelper testOutputHelper) { _testOutputHelper = testOutputHelper; _dateTimeFormat = new CultureInfo("en-GB").DateTimeFormat; - _fakeClock = new FakeClock(Instant.FromDateTimeUtc(DateTime.Parse("14/04/2023 00:00:00", _dateTimeFormat).AsUtc())); + _fakeClock = + new FakeClock(Instant.FromDateTimeUtc(DateTime.Parse("14/04/2023 00:00:00", _dateTimeFormat).AsUtc())); var inMemorySettings = new Dictionary { - { "MachineId", "1" }, + { "MachineId", "1" } }; var mockConfiguration = new ConfigurationBuilder() @@ -58,7 +57,8 @@ public async Task Assert_NextId_Returns_Correct_Id() public async Task Assert_NextId_Returns_Correct_Id_When_Sequence_Overflows(int sequence) { var expectedInstant = - Instant.FromDateTimeUtc(DateTime.Parse("14/04/2023 00:00:00", _dateTimeFormat).AsUtc() + TimeSpan.FromSeconds(sequence)); + Instant.FromDateTimeUtc(DateTime.Parse("14/04/2023 00:00:00", _dateTimeFormat).AsUtc() + + TimeSpan.FromSeconds(sequence)); _fakeClock.Reset(expectedInstant); var result = await _idGenerator.NextId(); @@ -73,7 +73,8 @@ public async Task Assert_NextId_Returns_Correct_Id_When_Sequence_Overflows(int s public async Task Assert_NextId_Returns_Correct_Id_When_Sequence_Overflows_Then_Resets() { var expectedInstant = - Instant.FromDateTimeUtc(DateTime.Parse("14/04/2023 00:00:00", _dateTimeFormat).AsUtc() + TimeSpan.FromSeconds(5)); + Instant.FromDateTimeUtc(DateTime.Parse("14/04/2023 00:00:00", _dateTimeFormat).AsUtc() + + TimeSpan.FromSeconds(5)); _fakeClock.Reset(expectedInstant); var result = await _idGenerator.NextId(); From 2608367d0a8d26e804cb4033615f329672e46e0b Mon Sep 17 00:00:00 2001 From: Tom Date: Sun, 16 Apr 2023 18:07:48 +0100 Subject: [PATCH 08/10] Input variables mapping correctly --- Coflo.sln | 10 ++ .../Variables/SetVariable.cs | 4 +- .../Attributes/ActivityOutputAttribute.cs | 7 ++ .../Activities/Models/ActivityDefinition.cs | 2 + .../Models/ActivityOutputMapping.cs | 2 +- .../Connections/Contracts/IConnection.cs | 4 +- .../Connections/Models/Connection.cs | 11 ++ .../Contracts/IEvaluationContext.cs | 2 +- .../Evaluation/Models/EvaluationContext.cs | 4 +- .../Contracts/IVariableCollection.cs | 1 + .../Variables/Model/VariableDefinition.cs | 2 +- .../WorkflowCompletedNotification.cs | 13 ++ .../Workflows/Services/IWorkflowExecutor.cs | 3 +- .../Services/WorkflowExecutor.cs | 21 ++-- .../Coflo.Hosting.Worker/AssemblyInfo.cs | 3 + .../Coflo.Hosting.Worker.csproj | 1 + .../Commands/StartActivityCommandHandler.cs | 32 ++--- .../Coflo.Hosting.Worker.Tests.csproj | 30 +++++ .../StartActivityCommandHandlerTests.cs | 115 ++++++++++++++++++ .../Coflo.Hosting.Worker.Tests/Usings.cs | 1 + 20 files changed, 236 insertions(+), 32 deletions(-) create mode 100644 source/core/Coflo.Abstractions/Activities/Attributes/ActivityOutputAttribute.cs create mode 100644 source/core/Coflo.Abstractions/Connections/Models/Connection.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowCompletedNotification.cs create mode 100644 source/hosting/Coflo.Hosting.Worker/AssemblyInfo.cs create mode 100644 tests/hosting/Coflo.Hosting.Worker.Tests/Coflo.Hosting.Worker.Tests.csproj create mode 100644 tests/hosting/Coflo.Hosting.Worker.Tests/Commands/StartActivityCommandHandlerTests.cs create mode 100644 tests/hosting/Coflo.Hosting.Worker.Tests/Usings.cs diff --git a/Coflo.sln b/Coflo.sln index 901276b..f0bbfe1 100644 --- a/Coflo.sln +++ b/Coflo.sln @@ -54,6 +54,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "activities", "activities", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Activities.Primitives", "source\activities\Coflo.Activities.Primitives\Coflo.Activities.Primitives.csproj", "{CDA31FB1-04BA-4A5D-9282-7EBAB755BE7E}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "hosting", "hosting", "{3DA27C4E-FE4A-4805-BDB7-61BDFFB9B30F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Hosting.Worker.Tests", "tests\hosting\Coflo.Hosting.Worker.Tests\Coflo.Hosting.Worker.Tests.csproj", "{423ECCA1-DBC1-4947-8BD6-AAF3A8A55B37}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -81,6 +85,8 @@ Global {75110834-EBD6-450A-9092-B619CE02FEAC} = {FA4C5E4B-B42D-4BC5-9F26-FD32CD88F75F} {A4E3E7F1-AA46-4934-90BA-598CE381F0AA} = {F514E571-76C3-477F-86F0-3C8CCF512015} {CDA31FB1-04BA-4A5D-9282-7EBAB755BE7E} = {A4E3E7F1-AA46-4934-90BA-598CE381F0AA} + {3DA27C4E-FE4A-4805-BDB7-61BDFFB9B30F} = {1230DC9F-E226-4466-B0B1-99B52B66232C} + {423ECCA1-DBC1-4947-8BD6-AAF3A8A55B37} = {3DA27C4E-FE4A-4805-BDB7-61BDFFB9B30F} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {28DE14D7-4F2E-40D9-B847-69866240023D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -135,5 +141,9 @@ Global {CDA31FB1-04BA-4A5D-9282-7EBAB755BE7E}.Debug|Any CPU.Build.0 = Debug|Any CPU {CDA31FB1-04BA-4A5D-9282-7EBAB755BE7E}.Release|Any CPU.ActiveCfg = Release|Any CPU {CDA31FB1-04BA-4A5D-9282-7EBAB755BE7E}.Release|Any CPU.Build.0 = Release|Any CPU + {423ECCA1-DBC1-4947-8BD6-AAF3A8A55B37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {423ECCA1-DBC1-4947-8BD6-AAF3A8A55B37}.Debug|Any CPU.Build.0 = Debug|Any CPU + {423ECCA1-DBC1-4947-8BD6-AAF3A8A55B37}.Release|Any CPU.ActiveCfg = Release|Any CPU + {423ECCA1-DBC1-4947-8BD6-AAF3A8A55B37}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/source/activities/Coflo.Activities.Primitives/Variables/SetVariable.cs b/source/activities/Coflo.Activities.Primitives/Variables/SetVariable.cs index 7930f8a..f973647 100644 --- a/source/activities/Coflo.Activities.Primitives/Variables/SetVariable.cs +++ b/source/activities/Coflo.Activities.Primitives/Variables/SetVariable.cs @@ -12,10 +12,10 @@ public class SetVariable : Activity private const string Done = "Done"; private const string Failed = "Failed"; - [ActivityInput(DisplayName = "Variable")] + [ActivityInput(DisplayName = "Variable")] public VariableDefinition Variable { get; set; } - [ActivityInput(DisplayName = "Value")] + [ActivityInput(DisplayName = "Value")] public object? Value { get; set; } public SetVariable() : base("SET_VARIABLE") diff --git a/source/core/Coflo.Abstractions/Activities/Attributes/ActivityOutputAttribute.cs b/source/core/Coflo.Abstractions/Activities/Attributes/ActivityOutputAttribute.cs new file mode 100644 index 0000000..b808858 --- /dev/null +++ b/source/core/Coflo.Abstractions/Activities/Attributes/ActivityOutputAttribute.cs @@ -0,0 +1,7 @@ +namespace Coflo.Abstractions.Activities.Attributes; + +[AttributeUsage(AttributeTargets.Property)] +public class ActivityOutputAttribute : Attribute +{ + public string DisplayName { get; set; } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Models/ActivityDefinition.cs b/source/core/Coflo.Abstractions/Activities/Models/ActivityDefinition.cs index fc9f6b7..dffc493 100644 --- a/source/core/Coflo.Abstractions/Activities/Models/ActivityDefinition.cs +++ b/source/core/Coflo.Abstractions/Activities/Models/ActivityDefinition.cs @@ -11,6 +11,8 @@ public class ActivityDefinition public string ActivityName { get; set; } public ICollection InputMappings { get; set; } = new List(); + public ICollection OutputMappings { get; set; } = new List(); + public ICollection InputNode { get; set; } = new List(); public ICollection OutputNodes { get; set; } = new List(); } \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Activities/Models/ActivityOutputMapping.cs b/source/core/Coflo.Abstractions/Activities/Models/ActivityOutputMapping.cs index 7c3a133..7ca27e4 100644 --- a/source/core/Coflo.Abstractions/Activities/Models/ActivityOutputMapping.cs +++ b/source/core/Coflo.Abstractions/Activities/Models/ActivityOutputMapping.cs @@ -5,5 +5,5 @@ namespace Coflo.Abstractions.Activities.Models; public class ActivityOutputMapping { public string ActivityOutputField { get; set; } - public VariableDefinition Variable { get; set; } + public VariableDefinition? VariableDefinition { get; set; } } \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Connections/Contracts/IConnection.cs b/source/core/Coflo.Abstractions/Connections/Contracts/IConnection.cs index a7e32a2..d8adb16 100644 --- a/source/core/Coflo.Abstractions/Connections/Contracts/IConnection.cs +++ b/source/core/Coflo.Abstractions/Connections/Contracts/IConnection.cs @@ -2,9 +2,9 @@ namespace Coflo.Abstractions.Connections.Contracts; -public class IConnection +public interface IConnection { public long ActivityId { get; set; } public long TargetActivityId { get; set; } - public string Outcome { get; set; } = OutcomeNames.Success; + public string Outcome { get; set; } } \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Connections/Models/Connection.cs b/source/core/Coflo.Abstractions/Connections/Models/Connection.cs new file mode 100644 index 0000000..a39575c --- /dev/null +++ b/source/core/Coflo.Abstractions/Connections/Models/Connection.cs @@ -0,0 +1,11 @@ +using Coflo.Abstractions.Activities; +using Coflo.Abstractions.Connections.Contracts; + +namespace Coflo.Abstractions.Connections.Models; + +public class Connection : IConnection +{ + public long ActivityId { get; set; } + public long TargetActivityId { get; set; } + public string Outcome { get; set; } = OutcomeNames.Success; +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Evaluation/Contracts/IEvaluationContext.cs b/source/core/Coflo.Abstractions/Evaluation/Contracts/IEvaluationContext.cs index a6bd0c0..00aac26 100644 --- a/source/core/Coflo.Abstractions/Evaluation/Contracts/IEvaluationContext.cs +++ b/source/core/Coflo.Abstractions/Evaluation/Contracts/IEvaluationContext.cs @@ -4,6 +4,6 @@ namespace Coflo.Abstractions.Evaluation.Contracts; public interface IEvaluationContext { - VariableCollection Variables { get; set; } + IVariableCollection Variables { get; set; } public string Condition { get; set; } } \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Evaluation/Models/EvaluationContext.cs b/source/core/Coflo.Abstractions/Evaluation/Models/EvaluationContext.cs index fc2a7e8..87466b7 100644 --- a/source/core/Coflo.Abstractions/Evaluation/Models/EvaluationContext.cs +++ b/source/core/Coflo.Abstractions/Evaluation/Models/EvaluationContext.cs @@ -5,10 +5,10 @@ namespace Coflo.Abstractions.Evaluation.Models; public class EvaluationContext : IEvaluationContext { - public VariableCollection Variables { get; set; } + public IVariableCollection Variables { get; set; } public string Condition { get; set; } - public EvaluationContext(VariableCollection variables, string condition) + public EvaluationContext(IVariableCollection variables, string condition) { Variables = variables; Condition = condition; diff --git a/source/core/Coflo.Abstractions/Variables/Contracts/IVariableCollection.cs b/source/core/Coflo.Abstractions/Variables/Contracts/IVariableCollection.cs index a1cbc04..743fa8a 100644 --- a/source/core/Coflo.Abstractions/Variables/Contracts/IVariableCollection.cs +++ b/source/core/Coflo.Abstractions/Variables/Contracts/IVariableCollection.cs @@ -4,5 +4,6 @@ public interface IVariableCollection { VariableInstance? this[string name] { get; } void Add(VariableInstance variable); + bool AddOrUpdate(VariableInstance variable); void AddRange(IEnumerable variables); } \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Variables/Model/VariableDefinition.cs b/source/core/Coflo.Abstractions/Variables/Model/VariableDefinition.cs index a36687d..96ef972 100644 --- a/source/core/Coflo.Abstractions/Variables/Model/VariableDefinition.cs +++ b/source/core/Coflo.Abstractions/Variables/Model/VariableDefinition.cs @@ -16,7 +16,7 @@ public VariableDefinition(string name, VariableType variableType, bool isArray, VariableType = variableType; IsArray = isArray; - if (SetDefaultValue(defaultValue)) + if (!SetDefaultValue(defaultValue)) throw new ArgumentException($"Invalid default value for variable type {variableType}"); } diff --git a/source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowCompletedNotification.cs b/source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowCompletedNotification.cs new file mode 100644 index 0000000..e961c66 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowCompletedNotification.cs @@ -0,0 +1,13 @@ +using Mediator; + +namespace Coflo.Abstractions.Workflows.Notifications; + +public class WorkflowCompletedNotification : INotification +{ + public long WorkflowInstanceId { get; } + + public WorkflowCompletedNotification(long workflowInstanceId) + { + WorkflowInstanceId = workflowInstanceId; + } +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Services/IWorkflowExecutor.cs b/source/core/Coflo.Abstractions/Workflows/Services/IWorkflowExecutor.cs index 2e8a1a9..8c1c4ae 100644 --- a/source/core/Coflo.Abstractions/Workflows/Services/IWorkflowExecutor.cs +++ b/source/core/Coflo.Abstractions/Workflows/Services/IWorkflowExecutor.cs @@ -5,7 +5,8 @@ namespace Coflo.Abstractions.Workflows.Services; public interface IWorkflowExecutor { - Task InitializeWorkflow(long workflowDefinitionId, VariableCollection variables); + Task InitializeWorkflow(long workflowDefinitionId, long workflowVersionId, + VariableCollection variables); Task ExecuteWorkflow(long workflowInstanceId); diff --git a/source/hosting/Coflo.Hosting.Orchestrator/Services/WorkflowExecutor.cs b/source/hosting/Coflo.Hosting.Orchestrator/Services/WorkflowExecutor.cs index cb93431..e9a4d44 100644 --- a/source/hosting/Coflo.Hosting.Orchestrator/Services/WorkflowExecutor.cs +++ b/source/hosting/Coflo.Hosting.Orchestrator/Services/WorkflowExecutor.cs @@ -39,7 +39,6 @@ public WorkflowExecutor(IWorkflowDefinitionStore workflowDefinitionStore, public async Task InitializeWorkflow(long workflowDefinitionId, long workflowVersionId, VariableCollection variables) { - var definition = await _workflowDefinitionStore.GetWorkflowDefinitionAsync(workflowDefinitionId); var time = _clock.GetCurrentInstant(); var instance = new WorkflowInstance @@ -48,11 +47,10 @@ public async Task InitializeWorkflow(long workflowDefinitionId, long workf WorkflowVersionId = workflowVersionId, Variables = variables, Status = WorkflowStatus.Created, - CreatedAt = time + CreatedAt = time, + InstanceId = await _idGenerator.NextId() }; - instance.InstanceId = await _idGenerator.NextId(); - await _workflowInstanceStore.SaveWorkflowInstanceAsync(instance); await _eventPublisher.PublishAsync(new StartWorkflowExecutionCommand(instance.InstanceId)); @@ -85,7 +83,7 @@ await _workflowDefinitionStore.GetWorkflowDefinitionVersionAsync(workflowInstanc await _activityDefinitionStore.GetActivityDefinitionAsync(nextActivity.ActivityDefinitionId); await _eventPublisher.PublishAsync(new StartActivityCommand(workflowInstanceId, - activityDefinition.ActivityDefinitionId)); + activityDefinition.ActivityDefinitionId, workflowInstance.Variables)); workflowInstance.Status = WorkflowStatus.Running; @@ -114,7 +112,7 @@ public async Task ActivityCompleted(ActivityCompletedNotification completedNotif } await _eventPublisher.PublishAsync(new StartActivityCommand(workflowInstance.InstanceId, - nextConnection.TargetActivityId)); + nextConnection.TargetActivityId, workflowInstance.Variables)); } public Task ActivityFailed(ActivityFailedNotification failedNotification) @@ -127,8 +125,15 @@ public Task RunNextActivity(long workflowInstanceId, long activityDefinitionId) throw new NotImplementedException(); } - public Task WorkflowCompleted(long workflowInstanceId) + public async Task WorkflowCompleted(long workflowInstanceId) { - throw new NotImplementedException(); + var workflowInstance = await _workflowInstanceStore.GetWorkflowInstanceAsync(workflowInstanceId); + + workflowInstance.Status = WorkflowStatus.Completed; + workflowInstance.CompletedAt = _clock.GetCurrentInstant(); + + await _workflowInstanceStore.SaveWorkflowInstanceAsync(workflowInstance, true); + + await _eventPublisher.PublishAsync(new WorkflowCompletedNotification(workflowInstanceId)); } } \ No newline at end of file diff --git a/source/hosting/Coflo.Hosting.Worker/AssemblyInfo.cs b/source/hosting/Coflo.Hosting.Worker/AssemblyInfo.cs new file mode 100644 index 0000000..518e675 --- /dev/null +++ b/source/hosting/Coflo.Hosting.Worker/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Coflo.Hosting.Worker.Tests")] \ No newline at end of file diff --git a/source/hosting/Coflo.Hosting.Worker/Coflo.Hosting.Worker.csproj b/source/hosting/Coflo.Hosting.Worker/Coflo.Hosting.Worker.csproj index 2ef04c3..a3d1967 100644 --- a/source/hosting/Coflo.Hosting.Worker/Coflo.Hosting.Worker.csproj +++ b/source/hosting/Coflo.Hosting.Worker/Coflo.Hosting.Worker.csproj @@ -7,6 +7,7 @@ + diff --git a/source/hosting/Coflo.Hosting.Worker/Commands/StartActivityCommandHandler.cs b/source/hosting/Coflo.Hosting.Worker/Commands/StartActivityCommandHandler.cs index f89f304..b83c455 100644 --- a/source/hosting/Coflo.Hosting.Worker/Commands/StartActivityCommandHandler.cs +++ b/source/hosting/Coflo.Hosting.Worker/Commands/StartActivityCommandHandler.cs @@ -38,13 +38,14 @@ public async ValueTask Handle(StartActivityCommand command, CancellationTo var activityType = Type.GetType(activityDef.ActivityName, true, true); Guard.Against.Null(activityType); - + var activity = (IActivity)_serviceProvider.GetRequiredService(activityType); var executionContext = new ActivityExecutionContext(command.VariableCollection, command.WorkflowInstanceId, await _idGenerator.NextId(), activityDef.ActivityName); PopulateInputVariablesToProperties(activityType, activity, activityDef, command.VariableCollection); + PopulateOutputVariablesToProperties(activityType, activity, activityDef, command.VariableCollection); var result = await activity.ExecuteAsync(executionContext); @@ -55,7 +56,7 @@ await _publisher.PublishAsync(new ActivityCompletedNotification Outcome = result.Outcome, NodeId = command.ActivityDefinitionId }); - + return Unit.Value; } @@ -65,24 +66,23 @@ internal void PopulateInputVariablesToProperties(Type activityType, IActivity ac { var properties = activityType.GetProperties(); - foreach (var property in properties) + foreach (var property in properties.Where(x => + activityDefinition.InputMappings.Any(y => y.ActivityInputField == x.Name) && + x.GetCustomAttribute() != null)) { - var inputAttribute = property.GetCustomAttribute(); - - if (inputAttribute == null) continue; var input = activityDefinition.InputMappings.FirstOrDefault(x => x.ActivityInputField == property.Name); if (input?.VariableDefinition?.Name == null) continue; - + var variable = variables[input.VariableDefinition.Name]; - if (variable?.GetType() != property.GetType()) + if (variable?.Value.GetType() != property.PropertyType) throw new ConstraintException("Variable type does not match property type"); - + property.SetValue(activity, variable.Value); } } - + internal void PopulateOutputVariablesToProperties(Type activityType, IActivity activity, ActivityDefinition activityDefinition, IVariableCollection variables) @@ -96,14 +96,18 @@ internal void PopulateOutputVariablesToProperties(Type activityType, IActivity a if (outputAttribute == null) continue; var output = activityDefinition.OutputMappings.FirstOrDefault(x => x.ActivityOutputField == property.Name); - if (output?.VariableDefinition?.Name == null) continue; - + if (string.IsNullOrEmpty(output?.VariableDefinition?.Name)) continue; + var variable = variables[output.VariableDefinition.Name]; if (variable?.GetType() != property.GetType()) throw new ConstraintException("Variable type does not match property type"); - - property.SetValue(activity, variable.Value); + + var value = property.GetValue(activity); + + variable.SetValue(value); + + variables.AddOrUpdate(variable); } } } \ No newline at end of file diff --git a/tests/hosting/Coflo.Hosting.Worker.Tests/Coflo.Hosting.Worker.Tests.csproj b/tests/hosting/Coflo.Hosting.Worker.Tests/Coflo.Hosting.Worker.Tests.csproj new file mode 100644 index 0000000..79492d3 --- /dev/null +++ b/tests/hosting/Coflo.Hosting.Worker.Tests/Coflo.Hosting.Worker.Tests.csproj @@ -0,0 +1,30 @@ + + + + net7.0 + enable + enable + + false + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/tests/hosting/Coflo.Hosting.Worker.Tests/Commands/StartActivityCommandHandlerTests.cs b/tests/hosting/Coflo.Hosting.Worker.Tests/Commands/StartActivityCommandHandlerTests.cs new file mode 100644 index 0000000..cc8f0ec --- /dev/null +++ b/tests/hosting/Coflo.Hosting.Worker.Tests/Commands/StartActivityCommandHandlerTests.cs @@ -0,0 +1,115 @@ +using System.Data; +using Coflo.Abstractions.Activities.Commands; +using Coflo.Abstractions.Activities.Models; +using Coflo.Abstractions.Activities.Stores; +using Coflo.Abstractions.Evaluation.Contracts; +using Coflo.Abstractions.Events; +using Coflo.Abstractions.Variables.Enums; +using Coflo.Abstractions.Variables.Model; +using Coflo.Activities.Primitives.Control; +using Coflo.Core.Snowflake.Generators; +using Coflo.Hosting.Worker.Commands; +using FluentAssertions; +using Moq; + +namespace Coflo.Hosting.Worker.Tests.Commands; + +public class StartActivityCommandHandlerTests +{ + public StartActivityCommandHandlerTests() + { + } + + [Fact] + public async Task Input_Mapping_Maps_To_Activity_Properties() + { + // Arrange + var mockActivityDefinitionStore = new Mock(); + var mockIdGenerator = new Mock(); + var mockServiceProvider = new Mock(); + var mockEventPublisher = new Mock(); + var mockEvaluator = new Mock(); + + mockEvaluator.Setup(x => x.EvaluateAsync(It.IsAny())) + .ReturnsAsync(() => true); + + var command = new StartActivityCommandHandler(mockActivityDefinitionStore.Object, mockIdGenerator.Object, + mockServiceProvider.Object, mockEventPublisher.Object); + + var variableDef = new VariableDefinition("test", VariableType.String, false, "TEST"); + var variable = new VariableInstance(variableDef); + variable.SetValue("TEST2"); + + var variableCollection = new VariableCollection(); + + variableCollection.AddOrUpdate(variable); + + var ifActivity = new If(mockEvaluator.Object); + var activityDef = new ActivityDefinition + { + DisplayName = "Test", + ActivityName = "IF", + InputMappings = new List() + { + new ActivityInputMapping() + { + VariableDefinition = variableDef, + ActivityInputField = nameof(If.Condition) + } + } + }; + + // Act + + command.PopulateInputVariablesToProperties(typeof(If), ifActivity, activityDef, variableCollection); + + // Assert + ifActivity.Condition.Should().Be(variable.Value as string); + } + + [Fact] + public async Task Input_Mapping_Throws_Constraint_Exception() + { + // Arrange + var mockActivityDefinitionStore = new Mock(); + var mockIdGenerator = new Mock(); + var mockServiceProvider = new Mock(); + var mockEventPublisher = new Mock(); + var mockEvaluator = new Mock(); + + mockEvaluator.Setup(x => x.EvaluateAsync(It.IsAny())) + .ReturnsAsync(() => true); + + var command = new StartActivityCommandHandler(mockActivityDefinitionStore.Object, mockIdGenerator.Object, + mockServiceProvider.Object, mockEventPublisher.Object); + + var variableDef = new VariableDefinition("test", VariableType.Boolean, false, false); + var variable = new VariableInstance(variableDef); + variable.SetValue(false); + + var variableCollection = new VariableCollection(); + + variableCollection.AddOrUpdate(variable); + + var ifActivity = new If(mockEvaluator.Object); + var activityDef = new ActivityDefinition + { + DisplayName = "Test", + ActivityName = "IF", + InputMappings = new List() + { + new ActivityInputMapping() + { + VariableDefinition = variableDef, + ActivityInputField = nameof(If.Condition) + } + } + }; + + // Act + var act = () => command.PopulateInputVariablesToProperties(typeof(If), ifActivity, activityDef, variableCollection); + + // Assert + act.Should().ThrowExactly(); + } +} \ No newline at end of file diff --git a/tests/hosting/Coflo.Hosting.Worker.Tests/Usings.cs b/tests/hosting/Coflo.Hosting.Worker.Tests/Usings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/tests/hosting/Coflo.Hosting.Worker.Tests/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file From 8dfa9805edf4457a7903251fb702bba3b69cea21 Mon Sep 17 00:00:00 2001 From: Tom Date: Sun, 16 Apr 2023 19:13:20 +0100 Subject: [PATCH 09/10] Bootstrapping to a basic engine --- Coflo.sln | 34 ++++------- .../Stores/IActivityDefinitionStore.cs | 2 +- .../Caching/Contracts/ICacheProvider.cs | 11 ++++ .../Variables/Model/VariableDefinition.cs | 8 +-- source/core/Coflo.Core/Coflo.Core.csproj | 8 +++ .../Coflo.Core/Services/CapEventPublisher.cs | 19 +++++++ .../hosting/Coflo.Hosting.Api.GRPC/Class1.cs | 5 -- .../Coflo.Hosting.Api.GRPC.csproj | 9 --- .../hosting/Coflo.Hosting.Api.Rest/Class1.cs | 5 -- .../Coflo.Hosting.Api.Rest.csproj | 9 --- .../CapControllers/WorkflowEventController.cs | 56 +++++++++++++++++++ .../ServiceCollectionExtensions.cs | 16 ++++++ .../CapControllers/ActivityEventController.cs | 23 ++++++++ .../ServiceCollectionExtensions.cs | 15 +++++ .../Coflo.Infrastructure.Caching.Redis.csproj | 17 ++++++ .../RedisCacheProvider.cs | 47 ++++++++++++++++ .../Class1.cs | 5 -- ...o.Infrastructure.Messaging.RabbitMQ.csproj | 9 --- ...lo.Infrastructure.Persistance.MySQL.csproj | 4 ++ .../Stores/MySqlActivityDefinitionStore.cs | 21 +++++++ .../Stores/MySqlWorkflowDefinitionStore.cs | 50 +++++++++++++++++ .../Stores/MySqlWorkflowInstanceStore.cs | 27 +++++++++ 22 files changed, 329 insertions(+), 71 deletions(-) create mode 100644 source/core/Coflo.Abstractions/Caching/Contracts/ICacheProvider.cs create mode 100644 source/core/Coflo.Core/Services/CapEventPublisher.cs delete mode 100644 source/hosting/Coflo.Hosting.Api.GRPC/Class1.cs delete mode 100644 source/hosting/Coflo.Hosting.Api.GRPC/Coflo.Hosting.Api.GRPC.csproj delete mode 100644 source/hosting/Coflo.Hosting.Api.Rest/Class1.cs delete mode 100644 source/hosting/Coflo.Hosting.Api.Rest/Coflo.Hosting.Api.Rest.csproj create mode 100644 source/hosting/Coflo.Hosting.Orchestrator/CapControllers/WorkflowEventController.cs create mode 100644 source/hosting/Coflo.Hosting.Orchestrator/ServiceCollectionExtensions.cs create mode 100644 source/hosting/Coflo.Hosting.Worker/CapControllers/ActivityEventController.cs create mode 100644 source/hosting/Coflo.Hosting.Worker/ServiceCollectionExtensions.cs create mode 100644 source/infrastructure/caching/Coflo.Infrastructure.Caching.Redis/Coflo.Infrastructure.Caching.Redis.csproj create mode 100644 source/infrastructure/caching/Coflo.Infrastructure.Caching.Redis/RedisCacheProvider.cs delete mode 100644 source/infrastructure/messaging/Coflo.Infrastructure.Messaging.RabbitMQ/Class1.cs delete mode 100644 source/infrastructure/messaging/Coflo.Infrastructure.Messaging.RabbitMQ/Coflo.Infrastructure.Messaging.RabbitMQ.csproj create mode 100644 source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlActivityDefinitionStore.cs create mode 100644 source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlWorkflowDefinitionStore.cs create mode 100644 source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlWorkflowInstanceStore.cs diff --git a/Coflo.sln b/Coflo.sln index f0bbfe1..bd3c0b0 100644 --- a/Coflo.sln +++ b/Coflo.sln @@ -16,10 +16,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Abstractions", "sourc EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "infrastructure", "infrastructure", "{E9952EF9-60A2-441B-BAFB-C285D8ED4761}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Infrastructure.Messaging.RabbitMQ", "source\infrastructure\messaging\Coflo.Infrastructure.Messaging.RabbitMQ\Coflo.Infrastructure.Messaging.RabbitMQ.csproj", "{28F9D028-7AD1-4345-AFCF-13413E5F6250}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "messaging", "messaging", "{63D82DB8-ADE8-4D8E-A1BC-DB9A29ADCACE}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "locking", "locking", "{B57221CE-ABE3-420A-8802-C3FF5FD03EAD}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Infrastructure.Locking.Redis", "source\infrastructure\locking\Coflo.Infrastructure.Locking.Redis\Coflo.Infrastructure.Locking.Redis.csproj", "{F7A0F3BB-F8E3-4739-AB71-7704403C8FC8}" @@ -32,10 +28,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "hosting", "hosting", "{DEAF EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Hosting.Worker", "source\hosting\Coflo.Hosting.Worker\Coflo.Hosting.Worker.csproj", "{60FE92B2-095B-46C3-8FEC-8C2350E92884}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Hosting.Api.Rest", "source\hosting\Coflo.Hosting.Api.Rest\Coflo.Hosting.Api.Rest.csproj", "{AE88171D-FB16-4686-B23E-1C5F75DF7DC2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Hosting.Api.GRPC", "source\hosting\Coflo.Hosting.Api.GRPC\Coflo.Hosting.Api.GRPC.csproj", "{47131B79-A7E2-4A46-B9E8-98DDC0C20FCE}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Core", "source\core\Coflo.Core\Coflo.Core.csproj", "{2569CD34-41EF-42A3-AE63-BE01A0124D44}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1230DC9F-E226-4466-B0B1-99B52B66232C}" @@ -58,6 +50,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "hosting", "hosting", "{3DA2 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Hosting.Worker.Tests", "tests\hosting\Coflo.Hosting.Worker.Tests\Coflo.Hosting.Worker.Tests.csproj", "{423ECCA1-DBC1-4947-8BD6-AAF3A8A55B37}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "caching", "caching", "{6F96D4D7-A683-44F1-8BBD-03684B7DF5DD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Infrastructure.Caching.Redis", "source\infrastructure\caching\Coflo.Infrastructure.Caching.Redis\Coflo.Infrastructure.Caching.Redis.csproj", "{850C0239-46E5-4B2B-BBE8-954C4E93EDB4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -67,16 +63,12 @@ Global {C46B2D26-B277-4EE0-9497-0473D52EE7A9} = {F514E571-76C3-477F-86F0-3C8CCF512015} {28DE14D7-4F2E-40D9-B847-69866240023D} = {C46B2D26-B277-4EE0-9497-0473D52EE7A9} {E9952EF9-60A2-441B-BAFB-C285D8ED4761} = {F514E571-76C3-477F-86F0-3C8CCF512015} - {63D82DB8-ADE8-4D8E-A1BC-DB9A29ADCACE} = {E9952EF9-60A2-441B-BAFB-C285D8ED4761} - {28F9D028-7AD1-4345-AFCF-13413E5F6250} = {63D82DB8-ADE8-4D8E-A1BC-DB9A29ADCACE} {B57221CE-ABE3-420A-8802-C3FF5FD03EAD} = {E9952EF9-60A2-441B-BAFB-C285D8ED4761} {F7A0F3BB-F8E3-4739-AB71-7704403C8FC8} = {B57221CE-ABE3-420A-8802-C3FF5FD03EAD} {B4807B40-63D6-4119-ACE5-8E65EBBFFFAD} = {E9952EF9-60A2-441B-BAFB-C285D8ED4761} {C4FA592B-9A2E-4062-8746-D8684ECB545E} = {B4807B40-63D6-4119-ACE5-8E65EBBFFFAD} {DEAF9660-31F2-4E6B-9A77-93BA08D7D122} = {F514E571-76C3-477F-86F0-3C8CCF512015} {60FE92B2-095B-46C3-8FEC-8C2350E92884} = {DEAF9660-31F2-4E6B-9A77-93BA08D7D122} - {AE88171D-FB16-4686-B23E-1C5F75DF7DC2} = {DEAF9660-31F2-4E6B-9A77-93BA08D7D122} - {47131B79-A7E2-4A46-B9E8-98DDC0C20FCE} = {DEAF9660-31F2-4E6B-9A77-93BA08D7D122} {2569CD34-41EF-42A3-AE63-BE01A0124D44} = {C46B2D26-B277-4EE0-9497-0473D52EE7A9} {FA4C5E4B-B42D-4BC5-9F26-FD32CD88F75F} = {1230DC9F-E226-4466-B0B1-99B52B66232C} {7FE22FFD-CB25-446A-9192-76127270C3E1} = {FA4C5E4B-B42D-4BC5-9F26-FD32CD88F75F} @@ -87,16 +79,14 @@ Global {CDA31FB1-04BA-4A5D-9282-7EBAB755BE7E} = {A4E3E7F1-AA46-4934-90BA-598CE381F0AA} {3DA27C4E-FE4A-4805-BDB7-61BDFFB9B30F} = {1230DC9F-E226-4466-B0B1-99B52B66232C} {423ECCA1-DBC1-4947-8BD6-AAF3A8A55B37} = {3DA27C4E-FE4A-4805-BDB7-61BDFFB9B30F} + {6F96D4D7-A683-44F1-8BBD-03684B7DF5DD} = {E9952EF9-60A2-441B-BAFB-C285D8ED4761} + {850C0239-46E5-4B2B-BBE8-954C4E93EDB4} = {6F96D4D7-A683-44F1-8BBD-03684B7DF5DD} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {28DE14D7-4F2E-40D9-B847-69866240023D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {28DE14D7-4F2E-40D9-B847-69866240023D}.Debug|Any CPU.Build.0 = Debug|Any CPU {28DE14D7-4F2E-40D9-B847-69866240023D}.Release|Any CPU.ActiveCfg = Release|Any CPU {28DE14D7-4F2E-40D9-B847-69866240023D}.Release|Any CPU.Build.0 = Release|Any CPU - {28F9D028-7AD1-4345-AFCF-13413E5F6250}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {28F9D028-7AD1-4345-AFCF-13413E5F6250}.Debug|Any CPU.Build.0 = Debug|Any CPU - {28F9D028-7AD1-4345-AFCF-13413E5F6250}.Release|Any CPU.ActiveCfg = Release|Any CPU - {28F9D028-7AD1-4345-AFCF-13413E5F6250}.Release|Any CPU.Build.0 = Release|Any CPU {F7A0F3BB-F8E3-4739-AB71-7704403C8FC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F7A0F3BB-F8E3-4739-AB71-7704403C8FC8}.Debug|Any CPU.Build.0 = Debug|Any CPU {F7A0F3BB-F8E3-4739-AB71-7704403C8FC8}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -109,14 +99,6 @@ Global {60FE92B2-095B-46C3-8FEC-8C2350E92884}.Debug|Any CPU.Build.0 = Debug|Any CPU {60FE92B2-095B-46C3-8FEC-8C2350E92884}.Release|Any CPU.ActiveCfg = Release|Any CPU {60FE92B2-095B-46C3-8FEC-8C2350E92884}.Release|Any CPU.Build.0 = Release|Any CPU - {AE88171D-FB16-4686-B23E-1C5F75DF7DC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AE88171D-FB16-4686-B23E-1C5F75DF7DC2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AE88171D-FB16-4686-B23E-1C5F75DF7DC2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AE88171D-FB16-4686-B23E-1C5F75DF7DC2}.Release|Any CPU.Build.0 = Release|Any CPU - {47131B79-A7E2-4A46-B9E8-98DDC0C20FCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {47131B79-A7E2-4A46-B9E8-98DDC0C20FCE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {47131B79-A7E2-4A46-B9E8-98DDC0C20FCE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {47131B79-A7E2-4A46-B9E8-98DDC0C20FCE}.Release|Any CPU.Build.0 = Release|Any CPU {2569CD34-41EF-42A3-AE63-BE01A0124D44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2569CD34-41EF-42A3-AE63-BE01A0124D44}.Debug|Any CPU.Build.0 = Debug|Any CPU {2569CD34-41EF-42A3-AE63-BE01A0124D44}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -145,5 +127,9 @@ Global {423ECCA1-DBC1-4947-8BD6-AAF3A8A55B37}.Debug|Any CPU.Build.0 = Debug|Any CPU {423ECCA1-DBC1-4947-8BD6-AAF3A8A55B37}.Release|Any CPU.ActiveCfg = Release|Any CPU {423ECCA1-DBC1-4947-8BD6-AAF3A8A55B37}.Release|Any CPU.Build.0 = Release|Any CPU + {850C0239-46E5-4B2B-BBE8-954C4E93EDB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {850C0239-46E5-4B2B-BBE8-954C4E93EDB4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {850C0239-46E5-4B2B-BBE8-954C4E93EDB4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {850C0239-46E5-4B2B-BBE8-954C4E93EDB4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/source/core/Coflo.Abstractions/Activities/Stores/IActivityDefinitionStore.cs b/source/core/Coflo.Abstractions/Activities/Stores/IActivityDefinitionStore.cs index 20dc52e..e0a841d 100644 --- a/source/core/Coflo.Abstractions/Activities/Stores/IActivityDefinitionStore.cs +++ b/source/core/Coflo.Abstractions/Activities/Stores/IActivityDefinitionStore.cs @@ -4,5 +4,5 @@ namespace Coflo.Abstractions.Activities.Stores; public interface IActivityDefinitionStore { - Task GetActivityDefinitionAsync(long activityDefinitionId); + Task GetActivityDefinitionAsync(long activityDefinitionId); } \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Caching/Contracts/ICacheProvider.cs b/source/core/Coflo.Abstractions/Caching/Contracts/ICacheProvider.cs new file mode 100644 index 0000000..0a84c5a --- /dev/null +++ b/source/core/Coflo.Abstractions/Caching/Contracts/ICacheProvider.cs @@ -0,0 +1,11 @@ +using Coflo.Abstractions.Workflows.Models; + +namespace Coflo.Abstractions.Caching.Contracts; + +public interface ICacheProvider +{ + Task Insert(string key, T value); + Task Get(string key); + Task Exists(string key); + Task Delete(string key); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Variables/Model/VariableDefinition.cs b/source/core/Coflo.Abstractions/Variables/Model/VariableDefinition.cs index 96ef972..7f02c38 100644 --- a/source/core/Coflo.Abstractions/Variables/Model/VariableDefinition.cs +++ b/source/core/Coflo.Abstractions/Variables/Model/VariableDefinition.cs @@ -4,13 +4,13 @@ namespace Coflo.Abstractions.Variables.Model; public class VariableDefinition { - public string Name { get; set; } = null!; - public VariableType VariableType { get; set; } = VariableType.String; + public string Name { get; set; } + public VariableType VariableType { get; set; } public bool IsArray { get; set; } public object? DefaultValue { get; private set; } public bool Nullable { get; set; } public bool Persist { get; set; } = true; - public VariableDefinition(string name, VariableType variableType, bool isArray, object? defaultValue) + public VariableDefinition(string name, VariableType variableType, bool isArray, object? defaultValue = null) { Name = name; VariableType = variableType; @@ -29,7 +29,7 @@ private bool SetDefaultValue(object? value) { switch (value) { - case null: + case null when Nullable: DefaultValue = null; return true; case string when VariableType == VariableType.String: diff --git a/source/core/Coflo.Core/Coflo.Core.csproj b/source/core/Coflo.Core/Coflo.Core.csproj index c9d3cfe..cb005f1 100644 --- a/source/core/Coflo.Core/Coflo.Core.csproj +++ b/source/core/Coflo.Core/Coflo.Core.csproj @@ -11,4 +11,12 @@ + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/source/core/Coflo.Core/Services/CapEventPublisher.cs b/source/core/Coflo.Core/Services/CapEventPublisher.cs new file mode 100644 index 0000000..eecad84 --- /dev/null +++ b/source/core/Coflo.Core/Services/CapEventPublisher.cs @@ -0,0 +1,19 @@ +using Coflo.Abstractions.Events; +using DotNetCore.CAP; +using Mediator; + +namespace Coflo.Core.Services; + +public class CapEventPublisher : IEventPublisher +{ + private readonly ICapPublisher _capPublisher; + + public CapEventPublisher(ICapPublisher capPublisher) + { + _capPublisher = capPublisher; + } + + + public Task PublishAsync(T @event) where T : IMessage + => _capPublisher.PublishAsync(nameof(@event), @event); +} \ No newline at end of file diff --git a/source/hosting/Coflo.Hosting.Api.GRPC/Class1.cs b/source/hosting/Coflo.Hosting.Api.GRPC/Class1.cs deleted file mode 100644 index 9132b5a..0000000 --- a/source/hosting/Coflo.Hosting.Api.GRPC/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Coflo.Hosting.Api.GRPC; - -public class Class1 -{ -} \ No newline at end of file diff --git a/source/hosting/Coflo.Hosting.Api.GRPC/Coflo.Hosting.Api.GRPC.csproj b/source/hosting/Coflo.Hosting.Api.GRPC/Coflo.Hosting.Api.GRPC.csproj deleted file mode 100644 index 6836c68..0000000 --- a/source/hosting/Coflo.Hosting.Api.GRPC/Coflo.Hosting.Api.GRPC.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - net7.0 - enable - enable - - - diff --git a/source/hosting/Coflo.Hosting.Api.Rest/Class1.cs b/source/hosting/Coflo.Hosting.Api.Rest/Class1.cs deleted file mode 100644 index 259b202..0000000 --- a/source/hosting/Coflo.Hosting.Api.Rest/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Coflo.Hosting.Api.Rest; - -public class Class1 -{ -} \ No newline at end of file diff --git a/source/hosting/Coflo.Hosting.Api.Rest/Coflo.Hosting.Api.Rest.csproj b/source/hosting/Coflo.Hosting.Api.Rest/Coflo.Hosting.Api.Rest.csproj deleted file mode 100644 index 6836c68..0000000 --- a/source/hosting/Coflo.Hosting.Api.Rest/Coflo.Hosting.Api.Rest.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - net7.0 - enable - enable - - - diff --git a/source/hosting/Coflo.Hosting.Orchestrator/CapControllers/WorkflowEventController.cs b/source/hosting/Coflo.Hosting.Orchestrator/CapControllers/WorkflowEventController.cs new file mode 100644 index 0000000..fa29f3a --- /dev/null +++ b/source/hosting/Coflo.Hosting.Orchestrator/CapControllers/WorkflowEventController.cs @@ -0,0 +1,56 @@ +using Coflo.Abstractions.Workflows.Commands; +using DotNetCore.CAP; +using Mediator; +using Microsoft.AspNetCore.Mvc; + +namespace Coflo.Hosting.Orchestrator.CapControllers; + +public class WorkflowEventController : ControllerBase +{ + private readonly IMediator _mediator; + + public WorkflowEventController(IMediator mediator) + { + _mediator = mediator; + } + + [NonAction] + [CapSubscribe(nameof(StartWorkflowExecutionCommand))] + public async Task StartWorkflowExecutionEvent(StartWorkflowExecutionCommand @event, + CancellationToken cancellationToken = default!) + { + await _mediator.Publish(@event, cancellationToken); + } + + [NonAction] + [CapSubscribe(nameof(WorkflowExecutionCompletedNotification))] + public async Task WorkflowExecutionCompletedEvent(WorkflowExecutionCompletedNotification @event, + CancellationToken cancellationToken = default!) + { + await _mediator.Publish(@event, cancellationToken); + } + + [NonAction] + [CapSubscribe(nameof(WorkflowExecutionFailedNotification))] + public async Task WorkflowExecutionFailedEvent(WorkflowExecutionFailedNotification @event, + CancellationToken cancellationToken = default!) + { + await _mediator.Publish(@event, cancellationToken); + } + + [NonAction] + [CapSubscribe(nameof(WorkflowExecutionTerminatedNotification))] + public async Task WorkflowExecutionTerminatedEvent(WorkflowExecutionTerminatedNotification @event, + CancellationToken cancellationToken = default!) + { + await _mediator.Publish(@event, cancellationToken); + } + + [NonAction] + [CapSubscribe(nameof(WorkflowExecutionResumedNotification))] + public async Task WorkflowExecutionResumedEvent(WorkflowExecutionResumedNotification @event, + CancellationToken cancellationToken = default!) + { + await _mediator.Publish(@event, cancellationToken); + } +} \ No newline at end of file diff --git a/source/hosting/Coflo.Hosting.Orchestrator/ServiceCollectionExtensions.cs b/source/hosting/Coflo.Hosting.Orchestrator/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..a6068e7 --- /dev/null +++ b/source/hosting/Coflo.Hosting.Orchestrator/ServiceCollectionExtensions.cs @@ -0,0 +1,16 @@ +using DotNetCore.CAP; +using Microsoft.Extensions.DependencyInjection; + +namespace Coflo.Hosting.Orchestrator; + +public static class ServiceCollectionExtensions +{ + public static IServiceCollection AddOrchestrator(this IServiceCollection services, Action capOptions) + { + services.AddMediator(); + services.AddCap(capOptions); + + + return services; + } +} \ No newline at end of file diff --git a/source/hosting/Coflo.Hosting.Worker/CapControllers/ActivityEventController.cs b/source/hosting/Coflo.Hosting.Worker/CapControllers/ActivityEventController.cs new file mode 100644 index 0000000..aec96e3 --- /dev/null +++ b/source/hosting/Coflo.Hosting.Worker/CapControllers/ActivityEventController.cs @@ -0,0 +1,23 @@ +using Coflo.Abstractions.Activities.Commands; +using DotNetCore.CAP; +using Mediator; +using Microsoft.AspNetCore.Mvc; + +namespace Coflo.Hosting.Worker.CapControllers; + +public class ActivityEventController : ControllerBase +{ + private readonly IMediator _mediator; + + public ActivityEventController(IMediator mediator) + { + _mediator = mediator; + } + + [NonAction] + [CapSubscribe(nameof(StartActivityCommand))] + public async Task StartActivityEvent(StartActivityCommand command, CancellationToken cancellationToken = default!) + { + await _mediator.Publish(command, cancellationToken); + } +} \ No newline at end of file diff --git a/source/hosting/Coflo.Hosting.Worker/ServiceCollectionExtensions.cs b/source/hosting/Coflo.Hosting.Worker/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..a90ce8a --- /dev/null +++ b/source/hosting/Coflo.Hosting.Worker/ServiceCollectionExtensions.cs @@ -0,0 +1,15 @@ +using DotNetCore.CAP; +using Microsoft.Extensions.DependencyInjection; + +namespace Coflo.Hosting.Worker; + +public static class ServiceCollectionExtensions +{ + public static IServiceCollection AddWorker(this IServiceCollection services, Action capOptions) + { + services.AddMediator(); + services.AddCap(capOptions); + + return services; + } +} \ No newline at end of file diff --git a/source/infrastructure/caching/Coflo.Infrastructure.Caching.Redis/Coflo.Infrastructure.Caching.Redis.csproj b/source/infrastructure/caching/Coflo.Infrastructure.Caching.Redis/Coflo.Infrastructure.Caching.Redis.csproj new file mode 100644 index 0000000..3f26510 --- /dev/null +++ b/source/infrastructure/caching/Coflo.Infrastructure.Caching.Redis/Coflo.Infrastructure.Caching.Redis.csproj @@ -0,0 +1,17 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + diff --git a/source/infrastructure/caching/Coflo.Infrastructure.Caching.Redis/RedisCacheProvider.cs b/source/infrastructure/caching/Coflo.Infrastructure.Caching.Redis/RedisCacheProvider.cs new file mode 100644 index 0000000..4e7d5a4 --- /dev/null +++ b/source/infrastructure/caching/Coflo.Infrastructure.Caching.Redis/RedisCacheProvider.cs @@ -0,0 +1,47 @@ +using System.Text.Json; +using Coflo.Abstractions.Caching.Contracts; +using Coflo.Abstractions.Workflows.Models; +using StackExchange.Redis; + +namespace Coflo.Infrastructure.Caching.Redis; + +public class RedisCacheProvider : ICacheProvider +{ + private readonly IConnectionMultiplexer _connectionMultiplexer; + private readonly IDatabase _database; + + private static JsonSerializerOptions _jsonSerializerOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + + public RedisCacheProvider(IConnectionMultiplexer connectionMultiplexer) + { + _connectionMultiplexer = connectionMultiplexer; + _database = _connectionMultiplexer.GetDatabase(); + } + + public Task Insert(string key, T value) + { + return _database.SetAddAsync(new RedisKey(key), + new RedisValue(JsonSerializer.Serialize(value, _jsonSerializerOptions))); + } + + public Task Get(string key) + { + var redisValue = _database.StringGet(new RedisKey(key)); + return redisValue.IsNullOrEmpty + ? default + : Task.FromResult(JsonSerializer.Deserialize(redisValue, _jsonSerializerOptions)); + } + + public Task Exists(string key) + { + return _database.KeyExistsAsync(new RedisKey(key)); + } + + public Task Delete(string key) + { + return _database.KeyDeleteAsync(new RedisKey(key)); + } +} \ No newline at end of file diff --git a/source/infrastructure/messaging/Coflo.Infrastructure.Messaging.RabbitMQ/Class1.cs b/source/infrastructure/messaging/Coflo.Infrastructure.Messaging.RabbitMQ/Class1.cs deleted file mode 100644 index 46849ce..0000000 --- a/source/infrastructure/messaging/Coflo.Infrastructure.Messaging.RabbitMQ/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Coflo.Infrastructure.Messaging.RabbitMQ; - -public class Class1 -{ -} \ No newline at end of file diff --git a/source/infrastructure/messaging/Coflo.Infrastructure.Messaging.RabbitMQ/Coflo.Infrastructure.Messaging.RabbitMQ.csproj b/source/infrastructure/messaging/Coflo.Infrastructure.Messaging.RabbitMQ/Coflo.Infrastructure.Messaging.RabbitMQ.csproj deleted file mode 100644 index 6836c68..0000000 --- a/source/infrastructure/messaging/Coflo.Infrastructure.Messaging.RabbitMQ/Coflo.Infrastructure.Messaging.RabbitMQ.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - net7.0 - enable - enable - - - diff --git a/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Coflo.Infrastructure.Persistance.MySQL.csproj b/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Coflo.Infrastructure.Persistance.MySQL.csproj index 6836c68..bf6ca87 100644 --- a/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Coflo.Infrastructure.Persistance.MySQL.csproj +++ b/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Coflo.Infrastructure.Persistance.MySQL.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlActivityDefinitionStore.cs b/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlActivityDefinitionStore.cs new file mode 100644 index 0000000..9f62360 --- /dev/null +++ b/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlActivityDefinitionStore.cs @@ -0,0 +1,21 @@ +using Ardalis.GuardClauses; +using Coflo.Abstractions.Activities.Models; +using Coflo.Abstractions.Activities.Stores; +using Coflo.Abstractions.Caching.Contracts; + +namespace Coflo.Infrastructure.Persistance.MySQL.Stores; + +public class MySqlActivityDefinitionStore : IActivityDefinitionStore +{ + private readonly ICacheProvider _cacheProvider; + + public MySqlActivityDefinitionStore(ICacheProvider cacheProvider) + { + _cacheProvider = cacheProvider; + } + + public Task GetActivityDefinitionAsync(long activityDefinitionId) + { + return _cacheProvider.Get($"activityDefinition-{activityDefinitionId}"); + } +} \ No newline at end of file diff --git a/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlWorkflowDefinitionStore.cs b/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlWorkflowDefinitionStore.cs new file mode 100644 index 0000000..e90e8dc --- /dev/null +++ b/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlWorkflowDefinitionStore.cs @@ -0,0 +1,50 @@ +using Coflo.Abstractions.Caching.Contracts; +using Coflo.Abstractions.Workflows.Models; +using Coflo.Abstractions.Workflows.Stores; + +namespace Coflo.Infrastructure.Persistance.MySQL.Stores; + +public class MySqlWorkflowDefinitionStore : IWorkflowDefinitionStore +{ + private readonly ICacheProvider _cacheProvider; + + public MySqlWorkflowDefinitionStore(ICacheProvider cacheProvider) + { + _cacheProvider = cacheProvider; + } + + public Task GetWorkflowDefinitionAsync(long workflowDefinitionId) + { + return _cacheProvider.Get($"workflowDefinition-{workflowDefinitionId}"); + } + + public Task GetWorkflowDefinitionVersionAsync(long workflowDefinitionId, long workflowVersionId) + { + return _cacheProvider.Get($"workflowDefinitionVersion-{workflowDefinitionId}-{workflowVersionId}"); + } + + public Task> GetWorkflowDefinitionVersionsAsync(long workflowDefinitionId) + { + throw new NotImplementedException(); + } + + public Task SaveWorkflowDefinitionAsync(WorkflowDefinition workflowDefinition) + { + throw new NotImplementedException(); + } + + public Task SaveWorkflowDefinitionVersionAsync(WorkflowDefinition workflowDefinition) + { + throw new NotImplementedException(); + } + + public Task DeleteWorkflowDefinitionAsync(long workflowDefinitionId) + { + throw new NotImplementedException(); + } + + public Task DeleteWorkflowDefinitionVersionAsync(long workflowDefinitionId, long workflowVersionId) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlWorkflowInstanceStore.cs b/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlWorkflowInstanceStore.cs new file mode 100644 index 0000000..9f20443 --- /dev/null +++ b/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlWorkflowInstanceStore.cs @@ -0,0 +1,27 @@ +using Coflo.Abstractions.Workflows.Models; +using Coflo.Abstractions.Workflows.Stores; + +namespace Coflo.Infrastructure.Persistance.MySQL.Stores; + +public class MySqlWorkflowInstanceStore : IWorkflowInstanceStore +{ + public Task GetWorkflowInstanceAsync(long workflowInstanceId) + { + throw new NotImplementedException(); + } + + public Task> GetWorkflowInstancesAsync(long workflowDefinitionId) + { + throw new NotImplementedException(); + } + + public Task SaveWorkflowInstanceAsync(WorkflowInstance workflowInstance, bool persist = false) + { + throw new NotImplementedException(); + } + + public Task DeleteWorkflowInstanceAsync(long workflowInstanceId) + { + throw new NotImplementedException(); + } +} \ No newline at end of file From 7eed0ec92e7a2fd26ed364a0042fdaff080fddd6 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 17 Apr 2023 13:47:44 +0100 Subject: [PATCH 10/10] added cassandra --- Coflo.sln | 14 +-- .../Stores/IActivityDefinitionStore.cs | 4 +- .../Caching/Contracts/ICacheProvider.cs | 7 +- .../Workflows/Models/WorkflowDefinition.cs | 1 + .../Models/WorkflowDefinitionVersion.cs | 2 + .../Workflows/Models/WorkflowInstance.cs | 3 +- .../WorkflowExecutionCompletedNotification.cs | 8 ++ .../WorkflowExecutionFailedNotification.cs | 8 ++ .../WorkflowExecutionResumedNotification.cs | 8 ++ ...WorkflowExecutionTerminatedNotification.cs | 8 ++ .../IWorkflowDefinitionRepository.cs | 13 +++ .../Stores/IWorkflowDefinitionStore.cs | 5 +- .../CapControllers/WorkflowEventController.cs | 1 + .../RedisCacheProvider.cs | 11 +-- ...lo.Infrastructure.Persistance.MySQL.csproj | 13 --- .../Stores/MySqlActivityDefinitionStore.cs | 21 ----- .../Stores/MySqlWorkflowDefinitionStore.cs | 50 ----------- .../Stores/MySqlWorkflowInstanceStore.cs | 27 ------ .../WorkflowDefinitionMapping.cs | 14 +++ .../WorkflowDefinitionVersionMapping.cs | 15 ++++ ...nfrastructure.Persistence.Cassandra.csproj | 24 ++++++ .../Factories/CassandraSessionFactory.cs | 23 +++++ .../Factories/ICassandraSessionFactory.cs | 9 ++ .../WorkflowDefinitionRepository.cs | 85 +++++++++++++++++++ 24 files changed, 244 insertions(+), 130 deletions(-) create mode 100644 source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowExecutionCompletedNotification.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowExecutionFailedNotification.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowExecutionResumedNotification.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowExecutionTerminatedNotification.cs create mode 100644 source/core/Coflo.Abstractions/Workflows/Repositories/IWorkflowDefinitionRepository.cs delete mode 100644 source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Coflo.Infrastructure.Persistance.MySQL.csproj delete mode 100644 source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlActivityDefinitionStore.cs delete mode 100644 source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlWorkflowDefinitionStore.cs delete mode 100644 source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlWorkflowInstanceStore.cs create mode 100644 source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/CassandraMappings/WorkflowDefinitionMapping.cs create mode 100644 source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/CassandraMappings/WorkflowDefinitionVersionMapping.cs create mode 100644 source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/Coflo.Infrastructure.Persistence.Cassandra.csproj create mode 100644 source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/Factories/CassandraSessionFactory.cs create mode 100644 source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/Factories/ICassandraSessionFactory.cs create mode 100644 source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/Repositories/WorkflowDefinitionRepository.cs diff --git a/Coflo.sln b/Coflo.sln index bd3c0b0..166947b 100644 --- a/Coflo.sln +++ b/Coflo.sln @@ -22,8 +22,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Infrastructure.Lockin EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "persistance", "persistance", "{B4807B40-63D6-4119-ACE5-8E65EBBFFFAD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Infrastructure.Persistance.MySQL", "source\infrastructure\persistance\Coflo.Infrastructure.Persistance.MySQL\Coflo.Infrastructure.Persistance.MySQL.csproj", "{C4FA592B-9A2E-4062-8746-D8684ECB545E}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "hosting", "hosting", "{DEAF9660-31F2-4E6B-9A77-93BA08D7D122}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Hosting.Worker", "source\hosting\Coflo.Hosting.Worker\Coflo.Hosting.Worker.csproj", "{60FE92B2-095B-46C3-8FEC-8C2350E92884}" @@ -54,6 +52,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "caching", "caching", "{6F96 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Infrastructure.Caching.Redis", "source\infrastructure\caching\Coflo.Infrastructure.Caching.Redis\Coflo.Infrastructure.Caching.Redis.csproj", "{850C0239-46E5-4B2B-BBE8-954C4E93EDB4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Coflo.Infrastructure.Persistence.Cassandra", "source\infrastructure\persistance\Coflo.Infrastructure.Persistence.Cassandra\Coflo.Infrastructure.Persistence.Cassandra.csproj", "{FFE529A5-CF9D-46F6-9682-A23ED32B6648}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -66,7 +66,6 @@ Global {B57221CE-ABE3-420A-8802-C3FF5FD03EAD} = {E9952EF9-60A2-441B-BAFB-C285D8ED4761} {F7A0F3BB-F8E3-4739-AB71-7704403C8FC8} = {B57221CE-ABE3-420A-8802-C3FF5FD03EAD} {B4807B40-63D6-4119-ACE5-8E65EBBFFFAD} = {E9952EF9-60A2-441B-BAFB-C285D8ED4761} - {C4FA592B-9A2E-4062-8746-D8684ECB545E} = {B4807B40-63D6-4119-ACE5-8E65EBBFFFAD} {DEAF9660-31F2-4E6B-9A77-93BA08D7D122} = {F514E571-76C3-477F-86F0-3C8CCF512015} {60FE92B2-095B-46C3-8FEC-8C2350E92884} = {DEAF9660-31F2-4E6B-9A77-93BA08D7D122} {2569CD34-41EF-42A3-AE63-BE01A0124D44} = {C46B2D26-B277-4EE0-9497-0473D52EE7A9} @@ -81,6 +80,7 @@ Global {423ECCA1-DBC1-4947-8BD6-AAF3A8A55B37} = {3DA27C4E-FE4A-4805-BDB7-61BDFFB9B30F} {6F96D4D7-A683-44F1-8BBD-03684B7DF5DD} = {E9952EF9-60A2-441B-BAFB-C285D8ED4761} {850C0239-46E5-4B2B-BBE8-954C4E93EDB4} = {6F96D4D7-A683-44F1-8BBD-03684B7DF5DD} + {FFE529A5-CF9D-46F6-9682-A23ED32B6648} = {B4807B40-63D6-4119-ACE5-8E65EBBFFFAD} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {28DE14D7-4F2E-40D9-B847-69866240023D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -91,10 +91,6 @@ Global {F7A0F3BB-F8E3-4739-AB71-7704403C8FC8}.Debug|Any CPU.Build.0 = Debug|Any CPU {F7A0F3BB-F8E3-4739-AB71-7704403C8FC8}.Release|Any CPU.ActiveCfg = Release|Any CPU {F7A0F3BB-F8E3-4739-AB71-7704403C8FC8}.Release|Any CPU.Build.0 = Release|Any CPU - {C4FA592B-9A2E-4062-8746-D8684ECB545E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C4FA592B-9A2E-4062-8746-D8684ECB545E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C4FA592B-9A2E-4062-8746-D8684ECB545E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C4FA592B-9A2E-4062-8746-D8684ECB545E}.Release|Any CPU.Build.0 = Release|Any CPU {60FE92B2-095B-46C3-8FEC-8C2350E92884}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {60FE92B2-095B-46C3-8FEC-8C2350E92884}.Debug|Any CPU.Build.0 = Debug|Any CPU {60FE92B2-095B-46C3-8FEC-8C2350E92884}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -131,5 +127,9 @@ Global {850C0239-46E5-4B2B-BBE8-954C4E93EDB4}.Debug|Any CPU.Build.0 = Debug|Any CPU {850C0239-46E5-4B2B-BBE8-954C4E93EDB4}.Release|Any CPU.ActiveCfg = Release|Any CPU {850C0239-46E5-4B2B-BBE8-954C4E93EDB4}.Release|Any CPU.Build.0 = Release|Any CPU + {FFE529A5-CF9D-46F6-9682-A23ED32B6648}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FFE529A5-CF9D-46F6-9682-A23ED32B6648}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FFE529A5-CF9D-46F6-9682-A23ED32B6648}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FFE529A5-CF9D-46F6-9682-A23ED32B6648}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/source/core/Coflo.Abstractions/Activities/Stores/IActivityDefinitionStore.cs b/source/core/Coflo.Abstractions/Activities/Stores/IActivityDefinitionStore.cs index e0a841d..3fbd076 100644 --- a/source/core/Coflo.Abstractions/Activities/Stores/IActivityDefinitionStore.cs +++ b/source/core/Coflo.Abstractions/Activities/Stores/IActivityDefinitionStore.cs @@ -1,8 +1,10 @@ using Coflo.Abstractions.Activities.Models; +using Coflo.Abstractions.Workflows.Models; namespace Coflo.Abstractions.Activities.Stores; public interface IActivityDefinitionStore { - Task GetActivityDefinitionAsync(long activityDefinitionId); + ValueTask GetActivityDefinitionAsync(long activityDefinitionId); + ValueTask GetWorkflowDefinitionVersionAsync(long workflowDefinitionVersionId); } \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Caching/Contracts/ICacheProvider.cs b/source/core/Coflo.Abstractions/Caching/Contracts/ICacheProvider.cs index 0a84c5a..8c988a2 100644 --- a/source/core/Coflo.Abstractions/Caching/Contracts/ICacheProvider.cs +++ b/source/core/Coflo.Abstractions/Caching/Contracts/ICacheProvider.cs @@ -1,11 +1,12 @@ -using Coflo.Abstractions.Workflows.Models; +using Coflo.Abstractions.Activities.Models; +using Coflo.Abstractions.Workflows.Models; namespace Coflo.Abstractions.Caching.Contracts; public interface ICacheProvider { Task Insert(string key, T value); - Task Get(string key); - Task Exists(string key); + ValueTask Get(string key); + ValueTask Exists(string key); Task Delete(string key); } \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinition.cs b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinition.cs index 1dc4748..4b3d2f3 100644 --- a/source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinition.cs +++ b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinition.cs @@ -8,6 +8,7 @@ namespace Coflo.Abstractions.Workflows.Models; public class WorkflowDefinition : IWorkflowDefinition { public long WorkflowDefinitionId { get; set; } + public long TenantId { get; set; } public string Name { get; set; } public ICollection Versions { get; set; } } \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinitionVersion.cs b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinitionVersion.cs index dccd2e8..4bd7d15 100644 --- a/source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinitionVersion.cs +++ b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinitionVersion.cs @@ -9,6 +9,8 @@ public class WorkflowDefinitionVersion : IWorkflowDefinitionVersion { public long WorkflowVersionId { get; set; } public long WorkflowDefinitionId { get; set; } + public long TenantId { get; set; } + public IWorkflowDefinition WorkflowDefinition { get; set; } public ICollection Connections { get; set; } diff --git a/source/core/Coflo.Abstractions/Workflows/Models/WorkflowInstance.cs b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowInstance.cs index 3dbad00..77e6687 100644 --- a/source/core/Coflo.Abstractions/Workflows/Models/WorkflowInstance.cs +++ b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowInstance.cs @@ -9,7 +9,8 @@ public class WorkflowInstance : IWorkflowInstance public long InstanceId { get; set; } public long WorkflowDefinitionId { get; set; } public long WorkflowVersionId { get; set; } - + public long TenantId { get; set; } + public IVariableCollection Variables { get; set; } public List WorkflowLogs { get; set; } diff --git a/source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowExecutionCompletedNotification.cs b/source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowExecutionCompletedNotification.cs new file mode 100644 index 0000000..98cc2e0 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowExecutionCompletedNotification.cs @@ -0,0 +1,8 @@ +using Mediator; + +namespace Coflo.Abstractions.Workflows.Notifications; + +public class WorkflowExecutionCompletedNotification : INotification +{ + +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowExecutionFailedNotification.cs b/source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowExecutionFailedNotification.cs new file mode 100644 index 0000000..c63e063 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowExecutionFailedNotification.cs @@ -0,0 +1,8 @@ +using Mediator; + +namespace Coflo.Abstractions.Workflows.Notifications; + +public class WorkflowExecutionFailedNotification : INotification +{ + +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowExecutionResumedNotification.cs b/source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowExecutionResumedNotification.cs new file mode 100644 index 0000000..674ccb2 --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowExecutionResumedNotification.cs @@ -0,0 +1,8 @@ +using Mediator; + +namespace Coflo.Abstractions.Workflows.Notifications; + +public class WorkflowExecutionResumedNotification : INotification +{ + +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowExecutionTerminatedNotification.cs b/source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowExecutionTerminatedNotification.cs new file mode 100644 index 0000000..16bfbac --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Notifications/WorkflowExecutionTerminatedNotification.cs @@ -0,0 +1,8 @@ +using Mediator; + +namespace Coflo.Abstractions.Workflows.Notifications; + +public class WorkflowExecutionTerminatedNotification : INotification +{ + +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Repositories/IWorkflowDefinitionRepository.cs b/source/core/Coflo.Abstractions/Workflows/Repositories/IWorkflowDefinitionRepository.cs new file mode 100644 index 0000000..a18812b --- /dev/null +++ b/source/core/Coflo.Abstractions/Workflows/Repositories/IWorkflowDefinitionRepository.cs @@ -0,0 +1,13 @@ +using Coflo.Abstractions.Workflows.Models; + +namespace Coflo.Infrastructure.Persistance.Cassandra.Repositories; + +public interface IWorkflowDefinitionRepository +{ + ValueTask GetWorkflowDefinition(long workflowDefinitionId); + ValueTask GetWorkflowDefinitionVersion(long workflowDefinitionId, + long workflowVersionId); + Task InsertWorkflowDefinition(WorkflowDefinition workflowDefinition); + Task InsertWorkflowDefinitionVersion(WorkflowDefinitionVersion workflowDefinitionVersion); + Task RemoveWorkflowDefinition(long workflowDefinitionId); +} \ No newline at end of file diff --git a/source/core/Coflo.Abstractions/Workflows/Stores/IWorkflowDefinitionStore.cs b/source/core/Coflo.Abstractions/Workflows/Stores/IWorkflowDefinitionStore.cs index 4daaabb..eabdf2d 100644 --- a/source/core/Coflo.Abstractions/Workflows/Stores/IWorkflowDefinitionStore.cs +++ b/source/core/Coflo.Abstractions/Workflows/Stores/IWorkflowDefinitionStore.cs @@ -4,8 +4,9 @@ namespace Coflo.Abstractions.Workflows.Stores; public interface IWorkflowDefinitionStore { - Task GetWorkflowDefinitionAsync(long workflowDefinitionId); - Task GetWorkflowDefinitionVersionAsync(long workflowDefinitionId, long workflowVersionId); + ValueTask GetWorkflowDefinitionAsync(long workflowDefinitionId); + ValueTask GetWorkflowDefinitionVersionAsync(long workflowDefinitionId, + long workflowVersionId); Task> GetWorkflowDefinitionVersionsAsync(long workflowDefinitionId); Task SaveWorkflowDefinitionAsync(WorkflowDefinition workflowDefinition); diff --git a/source/hosting/Coflo.Hosting.Orchestrator/CapControllers/WorkflowEventController.cs b/source/hosting/Coflo.Hosting.Orchestrator/CapControllers/WorkflowEventController.cs index fa29f3a..9643303 100644 --- a/source/hosting/Coflo.Hosting.Orchestrator/CapControllers/WorkflowEventController.cs +++ b/source/hosting/Coflo.Hosting.Orchestrator/CapControllers/WorkflowEventController.cs @@ -1,4 +1,5 @@ using Coflo.Abstractions.Workflows.Commands; +using Coflo.Abstractions.Workflows.Notifications; using DotNetCore.CAP; using Mediator; using Microsoft.AspNetCore.Mvc; diff --git a/source/infrastructure/caching/Coflo.Infrastructure.Caching.Redis/RedisCacheProvider.cs b/source/infrastructure/caching/Coflo.Infrastructure.Caching.Redis/RedisCacheProvider.cs index 4e7d5a4..c0aafc1 100644 --- a/source/infrastructure/caching/Coflo.Infrastructure.Caching.Redis/RedisCacheProvider.cs +++ b/source/infrastructure/caching/Coflo.Infrastructure.Caching.Redis/RedisCacheProvider.cs @@ -1,4 +1,5 @@ using System.Text.Json; +using Coflo.Abstractions.Activities.Models; using Coflo.Abstractions.Caching.Contracts; using Coflo.Abstractions.Workflows.Models; using StackExchange.Redis; @@ -27,17 +28,17 @@ public Task Insert(string key, T value) new RedisValue(JsonSerializer.Serialize(value, _jsonSerializerOptions))); } - public Task Get(string key) + public async ValueTask Get(string key) { - var redisValue = _database.StringGet(new RedisKey(key)); + var redisValue = await _database.StringGetAsync(new RedisKey(key)); return redisValue.IsNullOrEmpty ? default - : Task.FromResult(JsonSerializer.Deserialize(redisValue, _jsonSerializerOptions)); + : JsonSerializer.Deserialize(redisValue, _jsonSerializerOptions); } - public Task Exists(string key) + public async ValueTask Exists(string key) { - return _database.KeyExistsAsync(new RedisKey(key)); + return await _database.KeyExistsAsync(new RedisKey(key)); } public Task Delete(string key) diff --git a/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Coflo.Infrastructure.Persistance.MySQL.csproj b/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Coflo.Infrastructure.Persistance.MySQL.csproj deleted file mode 100644 index bf6ca87..0000000 --- a/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Coflo.Infrastructure.Persistance.MySQL.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - net7.0 - enable - enable - - - - - - - diff --git a/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlActivityDefinitionStore.cs b/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlActivityDefinitionStore.cs deleted file mode 100644 index 9f62360..0000000 --- a/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlActivityDefinitionStore.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Ardalis.GuardClauses; -using Coflo.Abstractions.Activities.Models; -using Coflo.Abstractions.Activities.Stores; -using Coflo.Abstractions.Caching.Contracts; - -namespace Coflo.Infrastructure.Persistance.MySQL.Stores; - -public class MySqlActivityDefinitionStore : IActivityDefinitionStore -{ - private readonly ICacheProvider _cacheProvider; - - public MySqlActivityDefinitionStore(ICacheProvider cacheProvider) - { - _cacheProvider = cacheProvider; - } - - public Task GetActivityDefinitionAsync(long activityDefinitionId) - { - return _cacheProvider.Get($"activityDefinition-{activityDefinitionId}"); - } -} \ No newline at end of file diff --git a/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlWorkflowDefinitionStore.cs b/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlWorkflowDefinitionStore.cs deleted file mode 100644 index e90e8dc..0000000 --- a/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlWorkflowDefinitionStore.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Coflo.Abstractions.Caching.Contracts; -using Coflo.Abstractions.Workflows.Models; -using Coflo.Abstractions.Workflows.Stores; - -namespace Coflo.Infrastructure.Persistance.MySQL.Stores; - -public class MySqlWorkflowDefinitionStore : IWorkflowDefinitionStore -{ - private readonly ICacheProvider _cacheProvider; - - public MySqlWorkflowDefinitionStore(ICacheProvider cacheProvider) - { - _cacheProvider = cacheProvider; - } - - public Task GetWorkflowDefinitionAsync(long workflowDefinitionId) - { - return _cacheProvider.Get($"workflowDefinition-{workflowDefinitionId}"); - } - - public Task GetWorkflowDefinitionVersionAsync(long workflowDefinitionId, long workflowVersionId) - { - return _cacheProvider.Get($"workflowDefinitionVersion-{workflowDefinitionId}-{workflowVersionId}"); - } - - public Task> GetWorkflowDefinitionVersionsAsync(long workflowDefinitionId) - { - throw new NotImplementedException(); - } - - public Task SaveWorkflowDefinitionAsync(WorkflowDefinition workflowDefinition) - { - throw new NotImplementedException(); - } - - public Task SaveWorkflowDefinitionVersionAsync(WorkflowDefinition workflowDefinition) - { - throw new NotImplementedException(); - } - - public Task DeleteWorkflowDefinitionAsync(long workflowDefinitionId) - { - throw new NotImplementedException(); - } - - public Task DeleteWorkflowDefinitionVersionAsync(long workflowDefinitionId, long workflowVersionId) - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlWorkflowInstanceStore.cs b/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlWorkflowInstanceStore.cs deleted file mode 100644 index 9f20443..0000000 --- a/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Stores/MySqlWorkflowInstanceStore.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Coflo.Abstractions.Workflows.Models; -using Coflo.Abstractions.Workflows.Stores; - -namespace Coflo.Infrastructure.Persistance.MySQL.Stores; - -public class MySqlWorkflowInstanceStore : IWorkflowInstanceStore -{ - public Task GetWorkflowInstanceAsync(long workflowInstanceId) - { - throw new NotImplementedException(); - } - - public Task> GetWorkflowInstancesAsync(long workflowDefinitionId) - { - throw new NotImplementedException(); - } - - public Task SaveWorkflowInstanceAsync(WorkflowInstance workflowInstance, bool persist = false) - { - throw new NotImplementedException(); - } - - public Task DeleteWorkflowInstanceAsync(long workflowInstanceId) - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/CassandraMappings/WorkflowDefinitionMapping.cs b/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/CassandraMappings/WorkflowDefinitionMapping.cs new file mode 100644 index 0000000..e6e4b93 --- /dev/null +++ b/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/CassandraMappings/WorkflowDefinitionMapping.cs @@ -0,0 +1,14 @@ +using Cassandra.Mapping; +using Coflo.Abstractions.Workflows.Models; + +namespace Coflo.Infrastructure.Persistance.Cassandra.CassandraMappings; + +public class WorkflowDefinitionMapping : Mappings +{ + public WorkflowDefinitionMapping() + { + For() + .PartitionKey(x => x.TenantId) + .Column(x => x.Versions, map => map.Ignore()); + } +} \ No newline at end of file diff --git a/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/CassandraMappings/WorkflowDefinitionVersionMapping.cs b/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/CassandraMappings/WorkflowDefinitionVersionMapping.cs new file mode 100644 index 0000000..e9c8d49 --- /dev/null +++ b/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/CassandraMappings/WorkflowDefinitionVersionMapping.cs @@ -0,0 +1,15 @@ +using Cassandra.Mapping; +using Coflo.Abstractions.Workflows.Models; + +namespace Coflo.Infrastructure.Persistance.Cassandra.CassandraMappings; + +public class WorkflowDefinitionVersionMapping : Mappings +{ + public WorkflowDefinitionVersionMapping() + { + For() + .TableName("workflow_definition_version") + .PartitionKey(x => x.TenantId) + .ClusteringKey(x => x.WorkflowDefinitionId); + } +} \ No newline at end of file diff --git a/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/Coflo.Infrastructure.Persistence.Cassandra.csproj b/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/Coflo.Infrastructure.Persistence.Cassandra.csproj new file mode 100644 index 0000000..27dbc32 --- /dev/null +++ b/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/Coflo.Infrastructure.Persistence.Cassandra.csproj @@ -0,0 +1,24 @@ + + + + net7.0 + enable + enable + Coflo.Infrastructure.Persistance.Cassandra + + + + + + + + + + + + + ICassandraSessionFactory.cs + + + + diff --git a/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/Factories/CassandraSessionFactory.cs b/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/Factories/CassandraSessionFactory.cs new file mode 100644 index 0000000..dcdb97b --- /dev/null +++ b/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/Factories/CassandraSessionFactory.cs @@ -0,0 +1,23 @@ +using Cassandra; + +namespace Coflo.Infrastructure.Persistance.Cassandra.Factories; + +internal class CassandraSessionFactory : ICassandraSessionFactory +{ + private readonly ICluster _cluster; + + public CassandraSessionFactory(ICluster cluster) + { + _cluster = cluster; + } + + public Task GetSessionAsync() + { + return _cluster.ConnectAsync(); + } + + public ISession GetSession() + { + return _cluster.Connect(); + } +} \ No newline at end of file diff --git a/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/Factories/ICassandraSessionFactory.cs b/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/Factories/ICassandraSessionFactory.cs new file mode 100644 index 0000000..ecc8c67 --- /dev/null +++ b/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/Factories/ICassandraSessionFactory.cs @@ -0,0 +1,9 @@ +using Cassandra; + +namespace Coflo.Infrastructure.Persistance.Cassandra.Factories; + +internal interface ICassandraSessionFactory +{ + Task GetSessionAsync(); + ISession GetSession(); +} \ No newline at end of file diff --git a/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/Repositories/WorkflowDefinitionRepository.cs b/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/Repositories/WorkflowDefinitionRepository.cs new file mode 100644 index 0000000..d9d994c --- /dev/null +++ b/source/infrastructure/persistance/Coflo.Infrastructure.Persistence.Cassandra/Repositories/WorkflowDefinitionRepository.cs @@ -0,0 +1,85 @@ +using Cassandra; +using Cassandra.Data.Linq; +using Coflo.Abstractions.Workflows.Models; +using Coflo.Infrastructure.Persistance.Cassandra.Factories; + +namespace Coflo.Infrastructure.Persistance.Cassandra.Repositories; + +internal class CassandraWorkflowDefinitionRepository : IWorkflowDefinitionRepository, IDisposable +{ + private readonly ISession _session; + private readonly Table _workflowDefinitionVersions; + private readonly Table _workflowDefinitions; + + public CassandraWorkflowDefinitionRepository(ICassandraSessionFactory cassandraSessionFactory) + { + _session = cassandraSessionFactory.GetSession(); + _workflowDefinitionVersions = _session.GetTable(); + _workflowDefinitions = _session.GetTable(); + } + + public async ValueTask GetWorkflowDefinition(long workflowDefinitionId) + { + var result = await _workflowDefinitions + .FirstOrDefault(x => x.WorkflowDefinitionId == workflowDefinitionId) + .ExecuteAsync(); + + return result; + } + + public async ValueTask GetWorkflowDefinitionVersion(long workflowDefinitionId, + long workflowVersionId) + { + var result = await _workflowDefinitionVersions + .FirstOrDefault(x => + x.WorkflowDefinitionId == workflowDefinitionId && x.WorkflowVersionId == workflowVersionId) + .ExecuteAsync(); + + return result; + } + + public async Task UpdateWorkflowDefinition(WorkflowDefinition workflowDefinition) + { + var workflowDef = + await _workflowDefinitions + .FirstOrDefault(x => x.WorkflowDefinitionId == workflowDefinition.WorkflowDefinitionId) + .ExecuteAsync(); + + if (workflowDef is null) + { + await InsertWorkflowDefinition(workflowDefinition); + + return; + } + + workflowDef.Name = workflowDefinition.Name; + + var t = _workflowDefinitions + .UpdateIf(x=> x.WorkflowDefinitionId == workflowDefinition.WorkflowDefinitionId); + } + + public async Task InsertWorkflowDefinition(WorkflowDefinition workflowDefinition) + { + await _workflowDefinitions + .Insert(workflowDefinition) + .ExecuteAsync(); + } + + public async Task InsertWorkflowDefinitionVersion(WorkflowDefinitionVersion workflowDefinitionVersion) + { + await _workflowDefinitionVersions + .Insert(workflowDefinitionVersion) + .ExecuteAsync(); + } + + public async Task RemoveWorkflowDefinition(long workflowDefinitionId) + { + await _workflowDefinitions.DeleteIf(x => x.WorkflowDefinitionId == workflowDefinitionId).ExecuteAsync(); + await _workflowDefinitionVersions.DeleteIf(x => x.WorkflowDefinitionId == workflowDefinitionId).ExecuteAsync(); + } + + public void Dispose() + { + _session.Dispose(); + } +} \ No newline at end of file