diff --git a/Elsa.sln b/Elsa.sln index 0a70dbda2..f88e4d6ef 100644 --- a/Elsa.sln +++ b/Elsa.sln @@ -230,6 +230,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.AspNet.MongoDb EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Workflows.Api.Rpc", "src\modules\Elsa.Workflows.Api.Rpc\Elsa.Workflows.Api.Rpc.csproj", "{0A94D69A-61AC-4654-83AB-B24445628A99}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.AspNet.DynamicActivityProvider", "src\samples\aspnet\Elsa.Samples.AspNet.DynamicActivityProvider\Elsa.Samples.AspNet.DynamicActivityProvider.csproj", "{85E13383-7C39-4719-AAC0-0B357C3A97C7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -576,6 +578,10 @@ Global {0A94D69A-61AC-4654-83AB-B24445628A99}.Debug|Any CPU.Build.0 = Debug|Any CPU {0A94D69A-61AC-4654-83AB-B24445628A99}.Release|Any CPU.ActiveCfg = Release|Any CPU {0A94D69A-61AC-4654-83AB-B24445628A99}.Release|Any CPU.Build.0 = Release|Any CPU + {85E13383-7C39-4719-AAC0-0B357C3A97C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {85E13383-7C39-4719-AAC0-0B357C3A97C7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {85E13383-7C39-4719-AAC0-0B357C3A97C7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {85E13383-7C39-4719-AAC0-0B357C3A97C7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {155227F0-A33B-40AA-A4B4-06F813EB921B} = {61017E64-6D00-49CB-9E81-5002DC8F7D5F} @@ -680,5 +686,6 @@ Global {3A377A6A-F735-4010-9E00-72E8BEB8F1F2} = {9B4F139F-7D26-435C-A561-89E65A67A8E5} {DAACE93A-866E-428E-B2A9-AFAC79F9C7A4} = {56C2FFB8-EA54-45B5-A095-4A78142EB4B5} {0A94D69A-61AC-4654-83AB-B24445628A99} = {B08B4E00-C2AB-48F3-8389-449F42AEF179} + {85E13383-7C39-4719-AAC0-0B357C3A97C7} = {56C2FFB8-EA54-45B5-A095-4A78142EB4B5} EndGlobalSection EndGlobal diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/ActivityProviders/ApiActivityProvider.cs b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/ActivityProviders/ApiActivityProvider.cs new file mode 100644 index 000000000..fb17ff329 --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/ActivityProviders/ApiActivityProvider.cs @@ -0,0 +1,161 @@ +using System.Text; +using System.Text.Json; +using Elsa.Expressions.Models; +using Elsa.Extensions; +using Elsa.Http; +using Elsa.Samples.AspNet.DynamicActivityProvider.Models; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Contracts; +using Elsa.Workflows.Core.Models; +using Elsa.Workflows.Management.Extensions; +using Humanizer; + +namespace Elsa.Samples.AspNet.DynamicActivityProvider.ActivityProviders; + +/// +/// Provides activities based on API descriptions (see /Data/apis.json). +/// +public class ApiActivityProvider : IActivityProvider +{ + private readonly IActivityFactory _activityFactory; + private readonly IActivityDescriber _activityDescriber; + + public ApiActivityProvider(IActivityFactory activityFactory, IActivityDescriber activityDescriber) + { + _activityFactory = activityFactory; + _activityDescriber = activityDescriber; + } + + public async ValueTask> GetDescriptorsAsync(CancellationToken cancellationToken = default) + { + // Read JSON file. + var json = await File.ReadAllTextAsync("Data/apis.json", cancellationToken); + + // Parse JSON into a list of API definitions. + var options = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + + var apisDefinitions = JsonSerializer.Deserialize(json, options)!; + + // Create activity descriptors from API definitions. + var activities = ((await Task.WhenAll(apisDefinitions.Select(async x => await CreateActivityDescriptors(x, cancellationToken)))).SelectMany(x => x)).ToList(); + + // Return activity descriptors. + return activities; + } + + /// + /// Creates activity descriptors from an API definition's endpoints. + /// + /// The API definition containing the endpoints. + /// An optional cancellation token. + /// A list of activity descriptors. + private async Task> CreateActivityDescriptors(ApiDefinition api, CancellationToken cancellationToken = default) + { + var descriptorTasks = api.Endpoints.Select(async endpoint => await CreateActivityDescriptorFromEndpointAsync(api, endpoint, cancellationToken)).ToList(); + + return await Task.WhenAll(descriptorTasks); + } + + private async Task CreateActivityDescriptorFromEndpointAsync(ApiDefinition api, ApiEndpointDefinition endpoint, CancellationToken cancellationToken = default) + { + // Create a fully qualified type name for the activity ("{namespace}.{apiName}.{endpointName}"). + var fullTypeName = $"Demo.{api.Name}.{endpoint.Name}"; + + // ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract + // Justification: the JSON input may not have a parameters property. + var parameters = endpoint.Parameters ?? new List(); + + // Create inputs from endpoint parameters. + var inputs = parameters.Select(CreateInputDescriptor).ToList(); + + // Create inputs for the URL and method properties of the SendHttpRequest activity, which will be used as the implementation for executing instances of this activity. + // They are set in the constructor, and should not be visible to the user. + var urlInputDescriptor = await _activityDescriber.DescribeInputPropertyAsync>(x => x.Url, cancellationToken); + var urlInputReferenceId = Guid.NewGuid().ToString(); + var methodInputDescriptor = await _activityDescriber.DescribeInputPropertyAsync>(x => x.Method, cancellationToken); + var methodInputReferenceId = Guid.NewGuid().ToString(); + + urlInputDescriptor.IsBrowsable = false; + urlInputDescriptor.ValueGetter = activity => + { + var input = new Input(context => + { + // Replace path parameters with input values. + var pathBuilder = new StringBuilder(endpoint.Path); + + foreach (var parameter in parameters) + { + var inputDescriptor = inputs.FirstOrDefault(x => x.Name == parameter.Name); + var inputValue = inputDescriptor?.ValueGetter(activity) is Input inputValueInput ? context.Get(inputValueInput.MemoryBlockReference()) : default; + pathBuilder.Replace("{" + parameter.Name + "}", inputValue?.ToString()); + } + + return new(new Uri(api.BaseUrl, pathBuilder.ToString())); + }, urlInputReferenceId); + return input; + }; + + methodInputDescriptor.IsBrowsable = false; + methodInputDescriptor.ValueGetter = _ => new Input(endpoint.Method, methodInputReferenceId); + + inputs.Add(urlInputDescriptor); + inputs.Add(methodInputDescriptor); + + // Create an output descriptor based on the send HTTP request activity's ParsedContent property. + var outputDescriptor = await _activityDescriber.DescribeOutputProperty>(x => x.ParsedContent, cancellationToken); + + return new ActivityDescriptor + { + Kind = ActivityKind.Task, + Category = "Demo", + Description = endpoint.Description, + Name = endpoint.Name, + TypeName = fullTypeName, + Namespace = $"Api.{api.Name}", + DisplayName = endpoint.Name.Humanize(), + Inputs = inputs, + Outputs = new[] { outputDescriptor }, + Constructor = context => + { + // The constructor is called when an activity instance of this type is requested. + + // Create the activity instance. + var activity = _activityFactory.Create(context); + + // Customize the activity type name. + activity.Type = fullTypeName; + + // Configure the activity's URL and method properties. + activity.Url = new Input(new ReadReference(urlInputReferenceId)); + activity.Method = new(new ReadReference(methodInputReferenceId)); + + return activity; + } + }; + } + + /// + /// Creates an input descriptor from an API parameter definition. + /// + /// The API parameter definition. + private static InputDescriptor CreateInputDescriptor(ApiParameterDefinition parameter) + { + var inputDescriptor = new InputDescriptor + { + Description = parameter.Description, + DefaultValue = parameter.DefaultValue, + Type = parameter.Type == "number" ? typeof(int) : typeof(string), + Name = parameter.Name, + DisplayName = parameter.Name.Humanize(), + IsSynthetic = true, // This is a synthetic property, i.e. it is not part of the activity's .NET type. + IsWrapped = true, // This property is wrapped within an Input object. + UIHint = InputUIHints.SingleLine, + ValueGetter = activity => activity.SyntheticProperties.GetValueOrDefault(parameter.Name), + ValueSetter = (activity, value) => activity.SyntheticProperties[parameter.Name] = value!, + }; + return inputDescriptor; + } +} \ No newline at end of file diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Data/apis.json b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Data/apis.json new file mode 100644 index 000000000..a301fdc4d --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Data/apis.json @@ -0,0 +1,43 @@ +[{ + "name": "Reqres", + "baseUrl": "https://reqres.in", + "endpoints": [ + { + "name": "GetUsers", + "path": "/api/users", + "method": "GET", + "description": "Get all users" + }, + { + "name": "GetUser", + "path": "/api/users/{UserId}", + "method": "GET", + "description": "Get a user", + "params": [ + { + "name": "UserId", + "type": "number", + "description": "User id" + } + ] + }, + { + "name": "CreateUser", + "path": "/api/users", + "method": "POST", + "description": "Create a user", + "params": [ + { + "name": "UserName", + "type": "string", + "description": "User name" + }, + { + "name": "Job", + "type": "string", + "description": "User job" + } + ] + } + ] +}] \ No newline at end of file diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Elsa.Samples.AspNet.DynamicActivityProvider.csproj b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Elsa.Samples.AspNet.DynamicActivityProvider.csproj new file mode 100644 index 000000000..c7beee0d8 --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Elsa.Samples.AspNet.DynamicActivityProvider.csproj @@ -0,0 +1,30 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + + + + + Always + + + + + + + + diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiDefinition.cs b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiDefinition.cs new file mode 100644 index 000000000..8b4ce56a8 --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiDefinition.cs @@ -0,0 +1,3 @@ +namespace Elsa.Samples.AspNet.DynamicActivityProvider.Models; + +public record ApiDefinition(string Name, Uri BaseUrl, ICollection Endpoints); \ No newline at end of file diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiEndpointDefinition.cs b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiEndpointDefinition.cs new file mode 100644 index 000000000..cec308a08 --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiEndpointDefinition.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Elsa.Samples.AspNet.DynamicActivityProvider.Models; + +public record ApiEndpointDefinition( + string Name, + string Description, + string Path, + string Method, + [property: JsonPropertyName("params")] ICollection Parameters); \ No newline at end of file diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiParameterDefinition.cs b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiParameterDefinition.cs new file mode 100644 index 000000000..e1d24ac08 --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiParameterDefinition.cs @@ -0,0 +1,3 @@ +namespace Elsa.Samples.AspNet.DynamicActivityProvider.Models; + +public record ApiParameterDefinition(string Name, string Type, string Description, string? DefaultValue); \ No newline at end of file diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Program.cs b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Program.cs new file mode 100644 index 000000000..a9d40ff2d --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Program.cs @@ -0,0 +1,54 @@ +using Elsa.EntityFrameworkCore.Modules.Identity; +using Elsa.EntityFrameworkCore.Modules.Management; +using Elsa.EntityFrameworkCore.Modules.Runtime; +using Elsa.Extensions; +using Elsa.Samples.AspNet.DynamicActivityProvider.ActivityProviders; + +var builder = WebApplication.CreateBuilder(args); +var services = builder.Services; + +// Add Elsa services. +services.AddElsa(elsa => elsa + // Expose API endpoints. + .UseWorkflowsApi() + + .UseWorkflowManagement(management => management.UseEntityFrameworkCore()) + .UseWorkflowRuntime(runtime => runtime.UseEntityFrameworkCore()) + + // Configure identity so that we can create a default admin user. + .UseIdentity(identity => + { + identity.UseAdminUserProvider(); + identity.UseEntityFrameworkCore(); + identity.TokenOptions = options => + { + options.SigningKey = "secret-token-signing-key"; + options.AccessTokenLifetime = TimeSpan.FromDays(1); + }; + }) + + // HTTP workflows. + .UseHttp() + + // Use default authentication (JWT). + .UseDefaultAuthentication(auth => auth.UseAdminApiKey()) +); + +// Add custom activity provider. +services.AddActivityProvider(); + +services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin())); + +// Configure middleware pipeline. +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) + app.UseDeveloperExceptionPage(); + +// Configure the HTTP request pipeline. +app.UseAuthentication(); +app.UseAuthorization(); +app.UseCors(); +app.UseWorkflowsApi(); +app.UseWorkflows(); +app.Run(); \ No newline at end of file diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Properties/launchSettings.json b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Properties/launchSettings.json new file mode 100644 index 000000000..dbd9fccfb --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Properties/launchSettings.json @@ -0,0 +1,37 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:41572", + "sslPort": 44398 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5015", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7145;http://localhost:5015", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Readme.md b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Readme.md new file mode 100644 index 000000000..b4920accf --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Readme.md @@ -0,0 +1,28 @@ +# Dynamic Activity Providers + +This sample project demonstrates how to implement a dynamic activity provider. + +## Overview + +The key to providing activities dynamically to the activity registry is to implement the `IActivityProvider` interface. This interface defines a single method, `GetDescriptorsAsync`, which returns a list of activity descriptors. + +In this example, we are loading API endpoint definitions from a JSON file located in the `Data` folder and create an activity descriptor for each endpoint. +The activity descriptor is then used to create an activity type, which is then registered with the activity registry. + +This example could be applied to any other data source, such as a database or a web service, Open API specifications, GraphQL, etc. + +## Usage + +1. Run the sample project. +2. Open the workflow editor. +3. Notice the new activities in the toolbox. +4. Drag one of the new activities onto the canvas. +5. Notice the new activity in the canvas. +6. Run the workflow. +7. Notice the new activity being executed. + +## Notes + +This project provides sample workflows that you can import into the workflow editor. The workflows are located in the `Workflows` folder. +You can then use the workflow editor to inspect the workflows and see how the activities are used. +When imported, you can invoke the workflows using the sample-request.http file located in the root folder containing this project. \ No newline at end of file diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/create-user-workflow.json b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/create-user-workflow.json new file mode 100644 index 000000000..ad297d4ae --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/create-user-workflow.json @@ -0,0 +1,236 @@ +{ + "id": "0ad67e344f664e4ba40430933db6383d", + "definitionId": "2a29bd086ba047ad82c296f380b2b21d", + "name": "Create User", + "createdAt": "2023-07-13T14:06:07.932781+00:00", + "version": 9, + "variables": [], + "inputs": [], + "outputs": [], + "outcomes": [], + "customProperties": {}, + "isReadonly": false, + "isLatest": true, + "isPublished": true, + "options": { + "autoUpdateConsumingWorkflows": false + }, + "root": { + "type": "Elsa.Flowchart", + "version": 1, + "id": "Flowchart1", + "metadata": {}, + "customProperties": { + "NotFoundConnectionsKey": [], + "canStartWorkflow": false, + "runAsynchronously": false + }, + "start": "HttpEndpoint1", + "activities": [ + { + "path": { + "typeName": "String", + "expression": { + "type": "Literal", + "value": "/users" + }, + "memoryReference": { + "id": "540fb073-7da0-40e4-affd-7d12b83bd41b" + } + }, + "supportedMethods": { + "typeName": "String[]", + "expression": { + "type": "Object", + "value": "[\u0022POST\u0022]" + }, + "memoryReference": { + "id": "cd84bc03-5395-449b-96eb-27d5ad17ca53" + } + }, + "authorize": { + "typeName": "Boolean", + "expression": { + "type": "Literal", + "value": "False" + }, + "memoryReference": { + "id": "HttpEndpoint1:input-1" + } + }, + "policy": null, + "parsedContent": { + "typeName": "Object", + "memoryReference": { + "id": "0a9e4d85af2149119068e324e3e61e32" + } + }, + "routeData": null, + "queryStringData": null, + "headers": null, + "result": null, + "id": "HttpEndpoint1", + "name": null, + "type": "Elsa.HttpEndpoint", + "version": 1, + "customProperties": { + "canStartWorkflow": true, + "runAsynchronously": false + }, + "metadata": { + "designer": { + "position": { + "x": 120, + "y": 260 + } + } + } + }, + { + "statusCode": { + "typeName": "System.Net.HttpStatusCode, System.Net.Primitives", + "expression": { + "type": "Literal", + "value": "OK" + }, + "memoryReference": { + "id": "WriteHttpResponse1:input-1" + } + }, + "content": { + "typeName": "Object", + "expression": { + "type": "JavaScript", + "value": "getParsedContentFromCreateUser1()" + }, + "memoryReference": { + "id": "457df76f-3bdd-4e7e-ba8a-10ebed80281d" + } + }, + "contentType": { + "typeName": "String", + "expression": { + "type": "Literal", + "value": "application/json" + }, + "memoryReference": { + "id": "4673dc8b-e2ee-48c2-8b7a-5c3e2c3a7560" + } + }, + "responseHeaders": { + "typeName": "Elsa.Http.Models.HttpResponseHeaders, Elsa.Http", + "expression": { + "type": "Literal", + "value": "{}" + }, + "memoryReference": { + "id": "WriteHttpResponse1:input-2" + } + }, + "id": "WriteHttpResponse1", + "name": null, + "type": "Elsa.WriteHttpResponse", + "version": 1, + "customProperties": { + "canStartWorkflow": false, + "runAsynchronously": false + }, + "metadata": { + "designer": { + "position": { + "x": 640, + "y": 260 + } + } + } + }, + { + "expectedStatusCodes": [], + "unmatchedStatusCode": null, + "url": { + "typeName": "System.Uri, System.Private.Uri", + "memoryReference": { + "id": "44cb8368-b578-4b02-beb7-cbac55359ab6" + } + }, + "method": { + "typeName": "String", + "memoryReference": { + "id": "d39ebcb9-8943-4d76-8299-7761aef1f8e3" + } + }, + "content": null, + "contentType": null, + "authorization": null, + "requestHeaders": { + "typeName": "Elsa.Http.Models.HttpRequestHeaders, Elsa.Http", + "expression": { + "type": "Literal", + "value": "{}" + }, + "memoryReference": {} + }, + "parsedContent": null, + "result": null, + "id": "CreateUser1", + "name": null, + "type": "Demo.Reqres.CreateUser", + "version": 0, + "customProperties": { + "canStartWorkflow": false, + "runAsynchronously": false + }, + "metadata": { + "designer": { + "position": { + "x": 428, + "y": 255 + } + } + }, + "userName": { + "typeName": "String", + "expression": { + "type": "JavaScript", + "value": "getParsedContentFromHttpEndpoint1().user;" + }, + "memoryReference": { + "id": "14ebd00e-bd1a-4953-a933-d49439ed418c" + } + }, + "job": { + "typeName": "String", + "expression": { + "type": "JavaScript", + "value": "getParsedContentFromHttpEndpoint1().job;" + }, + "memoryReference": { + "id": "e92af470-3d83-4891-96b4-e83331d0896e" + } + } + } + ], + "connections": [ + { + "source": { + "activity": "HttpEndpoint1", + "port": "Done" + }, + "target": { + "activity": "CreateUser1", + "port": "In" + } + }, + { + "source": { + "activity": "CreateUser1", + "port": "Done" + }, + "target": { + "activity": "WriteHttpResponse1", + "port": "In" + } + } + ] + } +} \ No newline at end of file diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/get-all-users-workflow.json b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/get-all-users-workflow.json new file mode 100644 index 000000000..e4baa0daa --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/get-all-users-workflow.json @@ -0,0 +1 @@ +{"id":"906cd28a12454b06b71bb724cd5891d0","definitionId":"4b3bbe91862c42ba921e21f70b429d8e","name":"Get Users","createdAt":"2023-07-13T14:02:19.276895+00:00","version":20,"variables":[{"id":"c4c6fbc0926f40d7a93e61eb7668b40b","name":"Users","typeName":"Object","isArray":true,"storageDriverTypeName":"Elsa.Workflows.Core.Services.WorkflowStorageDriver, Elsa.Workflows.Core"}],"inputs":[],"outputs":[],"outcomes":[],"customProperties":{},"isReadonly":false,"isLatest":true,"isPublished":false,"options":{"autoUpdateConsumingWorkflows":false},"root":{"type":"Elsa.Flowchart","version":1,"id":"Flowchart1","metadata":{},"customProperties":{"NotFoundConnectionsKey":[],"canStartWorkflow":false,"runAsynchronously":false},"start":"HttpEndpoint1","activities":[{"path":{"typeName":"String","expression":{"type":"Literal","value":"users/all"},"memoryReference":{"id":"6f30b933-ed1c-47fb-9d13-7b50f425e806"}},"supportedMethods":{"typeName":"String[]","expression":{"type":"Object","value":"[\u0022GET\u0022]"},"memoryReference":{"id":"d7ee590e-02b4-485d-8730-cc8656c55a99"}},"authorize":{"typeName":"Boolean","expression":{"type":"Literal","value":"False"},"memoryReference":{"id":"HttpEndpoint1:input-1"}},"policy":{"typeName":"String","expression":{"type":"Literal"},"memoryReference":{"id":"HttpEndpoint1:input-2"}},"parsedContent":null,"routeData":null,"queryStringData":null,"headers":null,"result":null,"id":"HttpEndpoint1","name":null,"type":"Elsa.HttpEndpoint","version":1,"customProperties":{"canStartWorkflow":true,"runAsynchronously":false},"metadata":{"designer":{"position":{"x":120,"y":260}}}},{"statusCode":{"typeName":"System.Net.HttpStatusCode, System.Net.Primitives","expression":{"type":"Literal","value":"OK"},"memoryReference":{"id":"WriteHttpResponse1:input-1"}},"content":{"typeName":"Object","expression":{"type":"JavaScript","value":"const users = getUsers();\nreturn users;"},"memoryReference":{"id":"09552413-74b1-4156-86a3-6679feeec83e"}},"contentType":{"typeName":"String","expression":{"type":"Literal","value":"application/json"},"memoryReference":{"id":"a7b8ff37-34a3-4400-88b2-026f19de8901"}},"responseHeaders":{"typeName":"Elsa.Http.Models.HttpResponseHeaders, Elsa.Http","expression":{"type":"Literal","value":"{}"},"memoryReference":{"id":"WriteHttpResponse1:input-2"}},"id":"WriteHttpResponse1","name":null,"type":"Elsa.WriteHttpResponse","version":1,"customProperties":{"canStartWorkflow":false,"runAsynchronously":false},"metadata":{"designer":{"position":{"x":920,"y":260}}}},{"variable":{"id":"c4c6fbc0926f40d7a93e61eb7668b40b","name":"Users","typeName":"Object","value":"","storageDriverTypeName":"Elsa.Workflows.Core.Services.WorkflowStorageDriver, Elsa.Workflows.Core"},"value":{"typeName":"Object","expression":{"type":"JavaScript","value":"getParsedContentFromGetUsers1()"},"memoryReference":{"id":"b1f4ed34-25d8-42f4-a59b-03541b11bfa6"}},"id":"SetVariable1","name":null,"type":"Elsa.SetVariable","version":1,"customProperties":{"canStartWorkflow":false,"runAsynchronously":false},"metadata":{"designer":{"position":{"x":660,"y":260}},"displayText":"Set Users"}},{"expectedStatusCodes":[],"unmatchedStatusCode":null,"url":{"typeName":"System.Uri, System.Private.Uri","memoryReference":{"id":"9ba5ad9b-b88a-45de-8e08-aec043e22a10"}},"method":{"typeName":"String","memoryReference":{"id":"ab690185-cda3-44e6-8aaa-bb1c2a2e7941"}},"content":null,"contentType":null,"authorization":null,"requestHeaders":{"typeName":"Elsa.Http.Models.HttpRequestHeaders, Elsa.Http","expression":{"type":"Literal","value":"{}"},"memoryReference":{}},"parsedContent":null,"result":null,"id":"GetUsers1","name":null,"type":"Demo.Reqres.GetUsers","version":0,"customProperties":{"canStartWorkflow":false,"runAsynchronously":false},"metadata":{"designer":{"position":{"x":460,"y":260}}}}],"connections":[{"source":{"activity":"HttpEndpoint1","port":"Done"},"target":{"activity":"GetUsers1","port":"In"}},{"source":{"activity":"SetVariable1","port":"Done"},"target":{"activity":"WriteHttpResponse1","port":"In"}},{"source":{"activity":"GetUsers1","port":"Done"},"target":{"activity":"SetVariable1","port":"In"}}]}} \ No newline at end of file diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/get-user-workflow.json b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/get-user-workflow.json new file mode 100644 index 000000000..0e06e5525 --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/get-user-workflow.json @@ -0,0 +1 @@ +{"id":"cfd60b01f85148b29e99b72d8c91754f","definitionId":"91390522c8674589bf9383e9c8e05447","name":"Get User","createdAt":"2023-07-13T14:10:52.554187+00:00","version":9,"variables":[{"id":"be5481f1f8f64df39012282404fbe7c7","name":"User","typeName":"Object","isArray":false,"value":"","storageDriverTypeName":"Elsa.Workflows.Core.Services.WorkflowStorageDriver, Elsa.Workflows.Core"},{"id":"abd12e6b760840e38e7e79e8430d33db","name":"UserId","typeName":"Int32","isArray":false,"value":"0","storageDriverTypeName":"Elsa.Workflows.Core.Services.WorkflowStorageDriver, Elsa.Workflows.Core"}],"inputs":[],"outputs":[],"outcomes":[],"customProperties":{},"isReadonly":false,"isLatest":true,"isPublished":true,"options":{"autoUpdateConsumingWorkflows":false},"root":{"type":"Elsa.Flowchart","version":1,"id":"Flowchart1","metadata":{},"customProperties":{"NotFoundConnectionsKey":[],"canStartWorkflow":false,"runAsynchronously":false},"start":"HttpEndpoint1","activities":[{"path":{"typeName":"String","expression":{"type":"Literal","value":"users/{userId}"},"memoryReference":{"id":"36cc8af8-3b83-49fd-aca2-1b8ba40583e8"}},"supportedMethods":{"typeName":"String[]","expression":{"type":"Object","value":"[\u0022GET\u0022]"},"memoryReference":{"id":"7389a64a-0fdb-4b8b-af55-72bad1eb72c9"}},"authorize":{"typeName":"Boolean","expression":{"type":"Literal","value":"False"},"memoryReference":{"id":"HttpEndpoint1:input-1"}},"policy":null,"parsedContent":null,"routeData":{"typeName":"ObjectDictionary","memoryReference":{"id":"ed6d5b4f6e794477837ee29e746e8ccb"}},"queryStringData":null,"headers":null,"result":null,"id":"HttpEndpoint1","name":null,"type":"Elsa.HttpEndpoint","version":1,"customProperties":{"canStartWorkflow":true,"runAsynchronously":false},"metadata":{"designer":{"position":{"x":260,"y":220}}}},{"variable":{"id":"abd12e6b760840e38e7e79e8430d33db","name":"UserId","typeName":"Int32","value":"0","storageDriverTypeName":"Elsa.Workflows.Core.Services.WorkflowStorageDriver, Elsa.Workflows.Core"},"value":{"typeName":"Object","expression":{"type":"JavaScript","value":"getRouteDataFromHttpEndpoint1()[\u0022userid\u0022]"},"memoryReference":{"id":"4ae05d95-e23f-4158-b53f-1043728d2ad1"}},"id":"SetVariable1","name":null,"type":"Elsa.SetVariable","version":1,"customProperties":{"canStartWorkflow":false,"runAsynchronously":false},"metadata":{"designer":{"position":{"x":520,"y":220}},"displayText":"Set UserId"}},{"expectedStatusCodes":[],"unmatchedStatusCode":null,"url":{"typeName":"System.Uri, System.Private.Uri","memoryReference":{"id":"31406d9b-528a-4149-aed6-4dacfd6d566a"}},"method":{"typeName":"String","memoryReference":{"id":"91cbdddc-1df1-42ed-b63a-8ed7cfae115a"}},"content":null,"contentType":null,"authorization":null,"requestHeaders":{"typeName":"Elsa.Http.Models.HttpRequestHeaders, Elsa.Http","expression":{"type":"Literal","value":"{}"},"memoryReference":{}},"parsedContent":{"typeName":"Object","memoryReference":{"id":"be5481f1f8f64df39012282404fbe7c7"}},"result":null,"id":"GetUser1","name":null,"type":"Demo.Reqres.GetUser","version":0,"customProperties":{"canStartWorkflow":false,"runAsynchronously":false},"metadata":{"designer":{"position":{"x":760,"y":220}}},"userId":{"typeName":"Int32","expression":{"type":"JavaScript","value":"getUserId()"},"memoryReference":{"id":"66cf67ac-5c36-48cd-8a3a-d24fb831e1b0"}}},{"statusCode":{"typeName":"System.Net.HttpStatusCode, System.Net.Primitives","expression":{"type":"Literal","value":"OK"},"memoryReference":{"id":"WriteHttpResponse1:input-1"}},"content":{"typeName":"Object","expression":{"type":"JavaScript","value":"getUser()"},"memoryReference":{"id":"36f78ecf-9b4c-4245-b1af-a44d9730b52e"}},"contentType":{"typeName":"String","expression":{"type":"Literal","value":"application/json"},"memoryReference":{"id":"5146a788-d413-4ea4-9ac7-99fcf717bb33"}},"responseHeaders":{"typeName":"Elsa.Http.Models.HttpResponseHeaders, Elsa.Http","expression":{"type":"Literal","value":"{}"},"memoryReference":{"id":"WriteHttpResponse1:input-2"}},"id":"WriteHttpResponse1","name":null,"type":"Elsa.WriteHttpResponse","version":1,"customProperties":{"canStartWorkflow":false,"runAsynchronously":false},"metadata":{"designer":{"position":{"x":940,"y":220}}}}],"connections":[{"source":{"activity":"HttpEndpoint1","port":"Done"},"target":{"activity":"SetVariable1","port":"In"}},{"source":{"activity":"SetVariable1","port":"Done"},"target":{"activity":"GetUser1","port":"In"}},{"source":{"activity":"GetUser1","port":"Done"},"target":{"activity":"WriteHttpResponse1","port":"In"}}]}} \ No newline at end of file diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/appsettings.Development.json b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/appsettings.Development.json new file mode 100644 index 000000000..0c208ae91 --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/appsettings.json b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/appsettings.json new file mode 100644 index 000000000..10f68b8c8 --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/sample-requests.http b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/sample-requests.http new file mode 100644 index 000000000..84b2f1c7b --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/sample-requests.http @@ -0,0 +1,17 @@ +### Create a new user +POST https://localhost:5001/workflows/users +Content-Type: application/json +Accept: application/json + +{ + "name": "Bob", + "job": "Postman" +} + +### Get all users +GET https://localhost:5001/workflows/users/all +Accept: application/json + +### Get a user +GET https://localhost:5001/workflows/users/2 +Accept: application/json