diff --git a/Elsa.sln b/Elsa.sln index 684499aae..6e05d2482 100644 --- a/Elsa.sln +++ b/Elsa.sln @@ -170,6 +170,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElsaDashboard.Backend", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElsaDashboard.WebAssembly", "src\dashboards\blazor\ElsaDashboard.WebAssembly\ElsaDashboard.WebAssembly.csproj", "{0AA2A003-C79E-4B43-803E-E1150254D2B0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Activities.Entity", "src\activities\Elsa.Activities.Entity\Elsa.Activities.Entity.csproj", "{A0A8431F-3A2F-433A-BB0D-EB9627DFA739}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.EntityChanged", "src\samples\console\Elsa.Samples.EntityChanged\Elsa.Samples.EntityChanged.csproj", "{A3149FE4-FD39-4DC2-AA9F-208C7216BC03}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -408,6 +412,14 @@ Global {0AA2A003-C79E-4B43-803E-E1150254D2B0}.Debug|Any CPU.Build.0 = Debug|Any CPU {0AA2A003-C79E-4B43-803E-E1150254D2B0}.Release|Any CPU.ActiveCfg = Release|Any CPU {0AA2A003-C79E-4B43-803E-E1150254D2B0}.Release|Any CPU.Build.0 = Release|Any CPU + {A0A8431F-3A2F-433A-BB0D-EB9627DFA739}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A0A8431F-3A2F-433A-BB0D-EB9627DFA739}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A0A8431F-3A2F-433A-BB0D-EB9627DFA739}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A0A8431F-3A2F-433A-BB0D-EB9627DFA739}.Release|Any CPU.Build.0 = Release|Any CPU + {A3149FE4-FD39-4DC2-AA9F-208C7216BC03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A3149FE4-FD39-4DC2-AA9F-208C7216BC03}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A3149FE4-FD39-4DC2-AA9F-208C7216BC03}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A3149FE4-FD39-4DC2-AA9F-208C7216BC03}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -488,6 +500,8 @@ Global {CCF8D59E-3672-4238-A900-CE879ABF02D2} = {E02BD53A-5B08-47A0-920E-DD537A6AFAAA} {B5EAB378-0002-47FD-A0C1-8931DB577006} = {E02BD53A-5B08-47A0-920E-DD537A6AFAAA} {0AA2A003-C79E-4B43-803E-E1150254D2B0} = {E02BD53A-5B08-47A0-920E-DD537A6AFAAA} + {A0A8431F-3A2F-433A-BB0D-EB9627DFA739} = {B43B546E-23F3-46E8-ACB7-D04F05CDA180} + {A3149FE4-FD39-4DC2-AA9F-208C7216BC03} = {FC9F520F-BA51-4AD2-BFEE-EF787798E734} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158} diff --git a/src/activities/Elsa.Activities.Entity/Activities/EntityChanged/EntityChanged.cs b/src/activities/Elsa.Activities.Entity/Activities/EntityChanged/EntityChanged.cs new file mode 100644 index 000000000..afb70ae05 --- /dev/null +++ b/src/activities/Elsa.Activities.Entity/Activities/EntityChanged/EntityChanged.cs @@ -0,0 +1,28 @@ +using Elsa.Activities.Entity.Models; +using Elsa.ActivityResults; +using Elsa.Attributes; +using Elsa.Design; +using Elsa.Services; + +// ReSharper disable once CheckNamespace +namespace Elsa.Activities.Entity +{ + [Trigger( + Category = "Entity", + DisplayName = "Entity Changed", + Description = "Trigger when Entity Changed", + Outcomes = new[] { OutcomeNames.Done } + )] + public class EntityChanged : Activity + { + [ActivityProperty(Type = ActivityPropertyTypes.Text, Hint = "Entity Name")] + public string? EntityName { get; set; } + + [ActivityProperty(Type = ActivityPropertyTypes.Text, Hint = "Action")] + [SelectOptions("Added", "Updated", "Deleted")] + public EntityChangedAction? Action { get; set; } + + protected override IActivityExecutionResult OnExecute() => Done(new EntityChangedDto(EntityName, Action)); + + } +} diff --git a/src/activities/Elsa.Activities.Entity/Activities/EntityChanged/EntityChangedActions.cs b/src/activities/Elsa.Activities.Entity/Activities/EntityChanged/EntityChangedActions.cs new file mode 100644 index 000000000..986d80d39 --- /dev/null +++ b/src/activities/Elsa.Activities.Entity/Activities/EntityChanged/EntityChangedActions.cs @@ -0,0 +1,10 @@ +// ReSharper disable once CheckNamespace +namespace Elsa.Activities.Entity +{ + public enum EntityChangedAction + { + Added, + Updated, + Deleted + } +} diff --git a/src/activities/Elsa.Activities.Entity/Activities/EntityChanged/EntityChangedBuilderExtensions.cs b/src/activities/Elsa.Activities.Entity/Activities/EntityChanged/EntityChangedBuilderExtensions.cs new file mode 100644 index 000000000..6d3a1bb6a --- /dev/null +++ b/src/activities/Elsa.Activities.Entity/Activities/EntityChanged/EntityChangedBuilderExtensions.cs @@ -0,0 +1,14 @@ +using System; + +using Elsa.Builders; + +// ReSharper disable once CheckNamespace +namespace Elsa.Activities.Entity +{ + public static class EntityChangedBuilderExtensions + { + private static IActivityBuilder EntityChanged(this IBuilder builder, Action> setup) => builder.Then(setup); + public static IActivityBuilder EntityChanged(this IBuilder builder, Type entity, EntityChangedAction action) => builder.EntityChanged(setup => setup.WithEntity(entity).WithAction(action)); + public static IActivityBuilder EntityChanged(this IBuilder builder, EntityChangedAction action) => builder.EntityChanged(setup => setup.WithEntity(typeof(TEntity)).WithAction(action)); + } +} diff --git a/src/activities/Elsa.Activities.Entity/Activities/EntityChanged/EntityChangedExtensions.cs b/src/activities/Elsa.Activities.Entity/Activities/EntityChanged/EntityChangedExtensions.cs new file mode 100644 index 000000000..2d6de0953 --- /dev/null +++ b/src/activities/Elsa.Activities.Entity/Activities/EntityChanged/EntityChangedExtensions.cs @@ -0,0 +1,15 @@ +// ReSharper disable once CheckNamespace +using System; + +using Elsa.Activities.Entity.Extensions; +using Elsa.Builders; + +namespace Elsa.Activities.Entity +{ + public static class EntityChangedExtensions + { + public static ISetupActivity WithEntity(this ISetupActivity activity) => activity.WithEntity(typeof(T)); + public static ISetupActivity WithEntity(this ISetupActivity activity, Type type) => activity.Set(x => x.EntityName, type.GetEntityName(false)); + public static ISetupActivity WithAction(this ISetupActivity activity, EntityChangedAction action) => activity.Set(x => x.Action, action); + } +} diff --git a/src/activities/Elsa.Activities.Entity/Attributes/EntityNameAttribute.cs b/src/activities/Elsa.Activities.Entity/Attributes/EntityNameAttribute.cs new file mode 100644 index 000000000..39cf605d9 --- /dev/null +++ b/src/activities/Elsa.Activities.Entity/Attributes/EntityNameAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Elsa.Activities.Entity.Attributes +{ + [AttributeUsage(AttributeTargets.Class)] + public class EntityNameAttribute : Attribute + { + public string Name { get; } + + public EntityNameAttribute(string name) + { + Name = name; + } + } +} diff --git a/src/activities/Elsa.Activities.Entity/Elsa.Activities.Entity.csproj b/src/activities/Elsa.Activities.Entity/Elsa.Activities.Entity.csproj new file mode 100644 index 000000000..2364801e3 --- /dev/null +++ b/src/activities/Elsa.Activities.Entity/Elsa.Activities.Entity.csproj @@ -0,0 +1,35 @@ + + + + netstandard2.0 + 1.0.0 + latest + Elsa Contributors + + Elsa is a set of workflow libraries and tools that enable lean and mean workflowing capabilities in any .NET Core application. + This package provides the following Entity activities: + + * EntityChanged + + 2020 + https://github.com/elsa-workflows/elsa-core + https://github.com/elsa-workflows/elsa-core + GitHub + elsa, workflows, entity + icon.png + enable + + + + + True + + + + + + + + + + diff --git a/src/activities/Elsa.Activities.Entity/Extensions/ServiceCollectionExtensions.cs b/src/activities/Elsa.Activities.Entity/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..c2418b68d --- /dev/null +++ b/src/activities/Elsa.Activities.Entity/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,14 @@ +using Elsa.Activities.Entity; +using Elsa.Activities.Entity.Triggers; + +// ReSharper disable once CheckNamespace +namespace Microsoft.Extensions.DependencyInjection +{ + public static class ServiceCollectionExtensions + { + public static IServiceCollection AddEntityActivities(this IServiceCollection services) => + services + .AddActivity() + .AddTriggerProvider(); + } +} diff --git a/src/activities/Elsa.Activities.Entity/Extensions/TypeExtensions.cs b/src/activities/Elsa.Activities.Entity/Extensions/TypeExtensions.cs new file mode 100644 index 000000000..53ce8ce48 --- /dev/null +++ b/src/activities/Elsa.Activities.Entity/Extensions/TypeExtensions.cs @@ -0,0 +1,17 @@ +using System; +using System.Reflection; + +using Elsa.Activities.Entity.Attributes; + +namespace Elsa.Activities.Entity.Extensions +{ + public static class TypeExtensions + { + public static string GetEntityName(this Type type, bool inherit = false) + { + var attribute = type.GetCustomAttribute(inherit); + + return attribute?.Name ?? type.FullName; + } + } +} diff --git a/src/activities/Elsa.Activities.Entity/Models/EntityChangedDto.cs b/src/activities/Elsa.Activities.Entity/Models/EntityChangedDto.cs new file mode 100644 index 000000000..6b794e770 --- /dev/null +++ b/src/activities/Elsa.Activities.Entity/Models/EntityChangedDto.cs @@ -0,0 +1,20 @@ + +namespace Elsa.Activities.Entity.Models +{ + public class EntityChangedDto + { + public string? EntityName { get; set; } + public EntityChangedAction? Action { get; set; } + + public EntityChangedDto() + { + + } + + public EntityChangedDto(string? entityName, EntityChangedAction? action) + { + EntityName = entityName; + Action = action; + } + } +} diff --git a/src/activities/Elsa.Activities.Entity/Triggers/EntityChangedTrigger.cs b/src/activities/Elsa.Activities.Entity/Triggers/EntityChangedTrigger.cs new file mode 100644 index 000000000..d63b415d7 --- /dev/null +++ b/src/activities/Elsa.Activities.Entity/Triggers/EntityChangedTrigger.cs @@ -0,0 +1,43 @@ +using System.Threading; +using System.Threading.Tasks; + +using Elsa.Triggers; + +namespace Elsa.Activities.Entity.Triggers +{ + public class EntityChangedTrigger : Trigger + { + public string? EntityName { get; set; } + + public EntityChangedAction? Action { get; set; } + + public string? ContextId { get; set; } + + public string? CorrelationId { get; set; } + + public EntityChangedTrigger() + { + + } + + public EntityChangedTrigger(string? entityName, EntityChangedAction? action, string? contextId, string? correlationId) + { + EntityName = entityName; + Action = action; + ContextId = contextId; + CorrelationId = correlationId; + } + + } + + public class EntityChangedTriggerProvider : TriggerProvider + { + public override async ValueTask GetTriggerAsync(TriggerProviderContext context, CancellationToken cancellationToken) => + new EntityChangedTrigger( + entityName: await context.Activity.GetPropertyValueAsync(x => x.EntityName, cancellationToken), + action: await context.Activity.GetPropertyValueAsync(x => x.Action, cancellationToken), + contextId: context.ActivityExecutionContext.WorkflowExecutionContext.WorkflowInstance.ContextId, + correlationId: context.ActivityExecutionContext.WorkflowExecutionContext.WorkflowInstance.CorrelationId + ); + } +} diff --git a/src/activities/Elsa.Activities.Entity/icon.png b/src/activities/Elsa.Activities.Entity/icon.png new file mode 100644 index 000000000..28dbaafbf Binary files /dev/null and b/src/activities/Elsa.Activities.Entity/icon.png differ diff --git a/src/samples/console/Elsa.Samples.EntityChanged/Elsa.Samples.EntityChanged.csproj b/src/samples/console/Elsa.Samples.EntityChanged/Elsa.Samples.EntityChanged.csproj new file mode 100644 index 000000000..1d3a70be6 --- /dev/null +++ b/src/samples/console/Elsa.Samples.EntityChanged/Elsa.Samples.EntityChanged.csproj @@ -0,0 +1,18 @@ + + + + Exe + net5.0 + false + + + + + + + + + + + + diff --git a/src/samples/console/Elsa.Samples.EntityChanged/Entity.cs b/src/samples/console/Elsa.Samples.EntityChanged/Entity.cs new file mode 100644 index 000000000..8e1ae4506 --- /dev/null +++ b/src/samples/console/Elsa.Samples.EntityChanged/Entity.cs @@ -0,0 +1,9 @@ +using Elsa.Activities.Entity.Attributes; + +namespace Elsa.Samples.EntityChanged +{ + [EntityName("Test-Entity")] + public class Entity + { + } +} diff --git a/src/samples/console/Elsa.Samples.EntityChanged/EntityChangedWorkflow.cs b/src/samples/console/Elsa.Samples.EntityChanged/EntityChangedWorkflow.cs new file mode 100644 index 000000000..2838cf3ca --- /dev/null +++ b/src/samples/console/Elsa.Samples.EntityChanged/EntityChangedWorkflow.cs @@ -0,0 +1,17 @@ +using Elsa.Activities.Entity; +using Elsa.Activities.Console; +using Elsa.Builders; +using Elsa.Activities.Entity.Models; + +namespace Elsa.Samples.EntityChanged +{ + public class EntityChangedWorkflow : IWorkflow + { + public void Build(IWorkflowBuilder workflow) + { + workflow + .EntityChanged(EntityChangedAction.Added) + .WriteLine(context => $"Entity '{((EntityChangedDto)context.Input).EntityName}' was '{((EntityChangedDto)context.Input).Action}'"); + } + } +} diff --git a/src/samples/console/Elsa.Samples.EntityChanged/Program.cs b/src/samples/console/Elsa.Samples.EntityChanged/Program.cs new file mode 100644 index 000000000..af897bd58 --- /dev/null +++ b/src/samples/console/Elsa.Samples.EntityChanged/Program.cs @@ -0,0 +1,46 @@ +using System.Threading.Tasks; + +using Elsa.Activities.Entity; +using Elsa.Activities.Entity.Triggers; +using Elsa.Activities.Entity.Extensions; +using Elsa.Services; +using Elsa.Triggers; + +using Microsoft.Extensions.DependencyInjection; +using System.Linq; + +namespace Elsa.Samples.EntityChanged +{ + /// + /// Demonstrates the use of declarative composite activities. + /// + class Program + { + static async Task Main() + { + // Create a service container with Elsa services. + var services = new ServiceCollection() + .AddElsa() + .AddConsoleActivities() + .AddEntityActivities() + .AddWorkflow() + .BuildServiceProvider(); + + // Run startup actions (not needed when registering Elsa with a Host). + var startupRunner = services.GetRequiredService(); + await startupRunner.StartupAsync(); + + var workflowSelector = services.GetRequiredService(); + + var workflowSelectionResult = (await workflowSelector.SelectWorkflowsAsync(evalute => + (evalute.Action.HasValue == false || evalute.Action == EntityChangedAction.Added) + && (evalute.EntityName == null || evalute.EntityName == typeof(Entity).GetEntityName()))).First(); + + var workflowRunner = services.GetRequiredService(); + + await workflowRunner.RunWorkflowAsync(workflowSelectionResult.WorkflowBlueprint, workflowSelectionResult.ActivityId); + + } + + } +} \ No newline at end of file