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:
diff --git a/Coflo.sln b/Coflo.sln
index bda815c..166947b 100644
--- a/Coflo.sln
+++ b/Coflo.sln
@@ -16,27 +16,17 @@ 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}"
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}"
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.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
@@ -44,6 +34,26 @@ 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
+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
+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
+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
+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
@@ -53,49 +63,38 @@ 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}
+ {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}
+ {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}
+ {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
{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
{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
{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
@@ -104,5 +103,33 @@ 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
+ {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
+ {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
+ {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
+ {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/README.md b/README.md
index 1250ff1..0dadc8b 100644
--- a/README.md
+++ b/README.md
@@ -5,26 +5,19 @@

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/infrastructure/messaging/Coflo.Infrastructure.Messaging.RabbitMQ/Coflo.Infrastructure.Messaging.RabbitMQ.csproj b/source/activities/Coflo.Activities.Primitives/Coflo.Activities.Primitives.csproj
similarity index 66%
rename from source/infrastructure/messaging/Coflo.Infrastructure.Messaging.RabbitMQ/Coflo.Infrastructure.Messaging.RabbitMQ.csproj
rename to source/activities/Coflo.Activities.Primitives/Coflo.Activities.Primitives.csproj
index 6836c68..2ef04c3 100644
--- a/source/infrastructure/messaging/Coflo.Infrastructure.Messaging.RabbitMQ/Coflo.Infrastructure.Messaging.RabbitMQ.csproj
+++ b/source/activities/Coflo.Activities.Primitives/Coflo.Activities.Primitives.csproj
@@ -6,4 +6,8 @@
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..f973647
--- /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/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/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..dffc493
--- /dev/null
+++ b/source/core/Coflo.Abstractions/Activities/Models/ActivityDefinition.cs
@@ -0,0 +1,18 @@
+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 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/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..7ca27e4
--- /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? VariableDefinition { 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..3fbd076
--- /dev/null
+++ b/source/core/Coflo.Abstractions/Activities/Stores/IActivityDefinitionStore.cs
@@ -0,0 +1,10 @@
+using Coflo.Abstractions.Activities.Models;
+using Coflo.Abstractions.Workflows.Models;
+
+namespace Coflo.Abstractions.Activities.Stores;
+
+public interface IActivityDefinitionStore
+{
+ 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
new file mode 100644
index 0000000..8c988a2
--- /dev/null
+++ b/source/core/Coflo.Abstractions/Caching/Contracts/ICacheProvider.cs
@@ -0,0 +1,12 @@
+using Coflo.Abstractions.Activities.Models;
+using Coflo.Abstractions.Workflows.Models;
+
+namespace Coflo.Abstractions.Caching.Contracts;
+
+public interface ICacheProvider
+{
+ Task Insert(string key, T value);
+ 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/Coflo.Abstractions.csproj b/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj
index 4b43e18..c9d98dc 100644
--- a/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj
+++ b/source/core/Coflo.Abstractions/Coflo.Abstractions.csproj
@@ -7,13 +7,14 @@
+
+
-
-
+
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/Connections/Contracts/IConnection.cs b/source/core/Coflo.Abstractions/Connections/Contracts/IConnection.cs
new file mode 100644
index 0000000..d8adb16
--- /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 interface IConnection
+{
+ public long ActivityId { get; set; }
+ public long TargetActivityId { get; set; }
+ 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/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/Evaluation/Contracts/IEvaluationContext.cs b/source/core/Coflo.Abstractions/Evaluation/Contracts/IEvaluationContext.cs
new file mode 100644
index 0000000..00aac26
--- /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
+{
+ IVariableCollection 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..87466b7
--- /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 IVariableCollection Variables { get; set; }
+ public string Condition { get; set; }
+
+ public EvaluationContext(IVariableCollection 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/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/Workflow/WorkflowDefinition.cs b/source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinition.cs
deleted file mode 100644
index afbbd94..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 Guid 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 00c89d3..0000000
--- a/source/core/Coflo.Abstractions/Models/Workflow/WorkflowDefinitionVersion.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Coflo.Abstractions.Contracts.Tenant;
-using NodaTime;
-
-namespace Coflo.Abstractions.Models.Workflow;
-
-public class WorkflowDefinitionVersion : ITenantScope
-{
- public Guid 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/Variables/Contracts/IVariableCollection.cs b/source/core/Coflo.Abstractions/Variables/Contracts/IVariableCollection.cs
new file mode 100644
index 0000000..743fa8a
--- /dev/null
+++ b/source/core/Coflo.Abstractions/Variables/Contracts/IVariableCollection.cs
@@ -0,0 +1,9 @@
+namespace Coflo.Abstractions.Variables.Model;
+
+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/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..7f02c38
--- /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; }
+ 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 = null)
+ {
+ 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 when Nullable:
+ 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/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..4b3d2f3
--- /dev/null
+++ b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinition.cs
@@ -0,0 +1,14 @@
+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 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
new file mode 100644
index 0000000..4bd7d15
--- /dev/null
+++ b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowDefinitionVersion.cs
@@ -0,0 +1,19 @@
+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 long TenantId { 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..77e6687
--- /dev/null
+++ b/source/core/Coflo.Abstractions/Workflows/Models/WorkflowInstance.cs
@@ -0,0 +1,20 @@
+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 long TenantId { 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/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/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/Services/IWorkflowExecutor.cs b/source/core/Coflo.Abstractions/Workflows/Services/IWorkflowExecutor.cs
new file mode 100644
index 0000000..8c1c4ae
--- /dev/null
+++ b/source/core/Coflo.Abstractions/Workflows/Services/IWorkflowExecutor.cs
@@ -0,0 +1,18 @@
+using Coflo.Abstractions.Variables.Model;
+using Coflo.Abstractions.Workflows.Notifications;
+
+namespace Coflo.Abstractions.Workflows.Services;
+
+public interface IWorkflowExecutor
+{
+ Task InitializeWorkflow(long workflowDefinitionId, long workflowVersionId,
+ 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..eabdf2d
--- /dev/null
+++ b/source/core/Coflo.Abstractions/Workflows/Stores/IWorkflowDefinitionStore.cs
@@ -0,0 +1,17 @@
+using Coflo.Abstractions.Workflows.Models;
+
+namespace Coflo.Abstractions.Workflows.Stores;
+
+public interface IWorkflowDefinitionStore
+{
+ ValueTask GetWorkflowDefinitionAsync(long workflowDefinitionId);
+ ValueTask 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.Core/Coflo.Core.csproj b/source/core/Coflo.Core/Coflo.Core.csproj
new file mode 100644
index 0000000..cb005f1
--- /dev/null
+++ b/source/core/Coflo.Core/Coflo.Core.csproj
@@ -0,0 +1,22 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+ 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/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/hosting/Coflo.Hosting.Api.GRPC/Coflo.Hosting.Api.GRPC.csproj b/source/hosting/Coflo.Core.Snowflake/Coflo.Core.Snowflake.csproj
similarity index 54%
rename from source/hosting/Coflo.Hosting.Api.GRPC/Coflo.Hosting.Api.GRPC.csproj
rename to source/hosting/Coflo.Core.Snowflake/Coflo.Core.Snowflake.csproj
index 6836c68..e74d845 100644
--- a/source/hosting/Coflo.Hosting.Api.GRPC/Coflo.Hosting.Api.GRPC.csproj
+++ b/source/hosting/Coflo.Core.Snowflake/Coflo.Core.Snowflake.csproj
@@ -6,4 +6,9 @@
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..968eeda
--- /dev/null
+++ b/source/hosting/Coflo.Core.Snowflake/Generators/IdGenerator.cs
@@ -0,0 +1,82 @@
+using Coflo.Core.Snowflake.Models;
+using Microsoft.Extensions.Configuration;
+using NodaTime;
+
+namespace Coflo.Core.Snowflake.Generators;
+
+public class IdGenerator : IIdGenerator
+{
+ 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 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();
+ }
+ 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();
+
+ 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..a398c58
--- /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/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.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..9643303
--- /dev/null
+++ b/source/hosting/Coflo.Hosting.Orchestrator/CapControllers/WorkflowEventController.cs
@@ -0,0 +1,57 @@
+using Coflo.Abstractions.Workflows.Commands;
+using Coflo.Abstractions.Workflows.Notifications;
+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/core/Coflo.SDK/Coflo.SDK.csproj b/source/hosting/Coflo.Hosting.Orchestrator/Coflo.Hosting.Orchestrator.csproj
similarity index 66%
rename from source/core/Coflo.SDK/Coflo.SDK.csproj
rename to source/hosting/Coflo.Hosting.Orchestrator/Coflo.Hosting.Orchestrator.csproj
index 6836c68..2ef04c3 100644
--- a/source/core/Coflo.SDK/Coflo.SDK.csproj
+++ b/source/hosting/Coflo.Hosting.Orchestrator/Coflo.Hosting.Orchestrator.csproj
@@ -6,4 +6,8 @@
enable
+
+
+
+
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.Orchestrator/Services/WorkflowExecutor.cs b/source/hosting/Coflo.Hosting.Orchestrator/Services/WorkflowExecutor.cs
new file mode 100644
index 0000000..e9a4d44
--- /dev/null
+++ b/source/hosting/Coflo.Hosting.Orchestrator/Services/WorkflowExecutor.cs
@@ -0,0 +1,139 @@
+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 time = _clock.GetCurrentInstant();
+
+ var instance = new WorkflowInstance
+ {
+ WorkflowDefinitionId = workflowDefinitionId,
+ WorkflowVersionId = workflowVersionId,
+ Variables = variables,
+ Status = WorkflowStatus.Created,
+ CreatedAt = time,
+ 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.Variables));
+
+ 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, workflowInstance.Variables));
+ }
+
+ public Task ActivityFailed(ActivityFailedNotification failedNotification)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task RunNextActivity(long workflowInstanceId, long activityDefinitionId)
+ {
+ throw new NotImplementedException();
+ }
+
+ public async Task WorkflowCompleted(long workflowInstanceId)
+ {
+ 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/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/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..a3d1967 100644
--- a/source/hosting/Coflo.Hosting.Worker/Coflo.Hosting.Worker.csproj
+++ b/source/hosting/Coflo.Hosting.Worker/Coflo.Hosting.Worker.csproj
@@ -6,4 +6,9 @@
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..b83c455
--- /dev/null
+++ b/source/hosting/Coflo.Hosting.Worker/Commands/StartActivityCommandHandler.cs
@@ -0,0 +1,113 @@
+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);
+ PopulateOutputVariablesToProperties(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.Where(x =>
+ activityDefinition.InputMappings.Any(y => y.ActivityInputField == x.Name) &&
+ x.GetCustomAttribute() != null))
+ {
+ var input = activityDefinition.InputMappings.FirstOrDefault(x => x.ActivityInputField == property.Name);
+
+ if (input?.VariableDefinition?.Name == null) continue;
+
+ var variable = variables[input.VariableDefinition.Name];
+
+ 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)
+ {
+ 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 (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");
+
+ var value = property.GetValue(activity);
+
+ variable.SetValue(value);
+
+ variables.AddOrUpdate(variable);
+ }
+ }
+}
\ 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..c0aafc1
--- /dev/null
+++ b/source/infrastructure/caching/Coflo.Infrastructure.Caching.Redis/RedisCacheProvider.cs
@@ -0,0 +1,48 @@
+using System.Text.Json;
+using Coflo.Abstractions.Activities.Models;
+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 async ValueTask Get(string key)
+ {
+ var redisValue = await _database.StringGetAsync(new RedisKey(key));
+ return redisValue.IsNullOrEmpty
+ ? default
+ : JsonSerializer.Deserialize(redisValue, _jsonSerializerOptions);
+ }
+
+ public async ValueTask Exists(string key)
+ {
+ return await _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/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/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 6836c68..0000000
--- a/source/infrastructure/persistance/Coflo.Infrastructure.Persistance.MySQL/Coflo.Infrastructure.Persistance.MySQL.csproj
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
- net7.0
- enable
- enable
-
-
-
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
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..0541b78
--- /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..062be60
--- /dev/null
+++ b/tests/core/Coflo.Core.Snowflake.Tests/IdGeneratorTests.cs
@@ -0,0 +1,87 @@
+using System.Globalization;
+using Coflo.Core.Snowflake.Generators;
+using FluentAssertions;
+using FluentAssertions.Extensions;
+using Microsoft.Extensions.Configuration;
+using NodaTime;
+using NodaTime.Testing;
+using Xunit.Abstractions;
+
+namespace Coflo.Core.Snowflake.Tests;
+
+public class IdGeneratorTests
+{
+ 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()));
+
+ var inMemorySettings = new Dictionary
+ {
+ { "MachineId", "1" }
+ };
+
+ var mockConfiguration = new ConfigurationBuilder()
+ .AddInMemoryCollection(inMemorySettings)
+ .Build();
+
+ _idGenerator = new IdGenerator(_fakeClock, mockConfiguration);
+ }
+
+ [Fact]
+ public async Task Assert_NextId_Returns_Correct_Id()
+ {
+ var result = await _idGenerator.NextId();
+
+ 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", _dateTimeFormat).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", _dateTimeFormat).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
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
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