Add JSON workflow provider (#3955)
* Refactor import models * Update API endpoints * Add console sample to run workflow from JSON * Add FluentStorage provider for JSON workflows + sample
This commit is contained in:
parent
799e9e02ed
commit
0ea463fdd0
21
Elsa.sln
21
Elsa.sln
|
|
@ -188,6 +188,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.Onboarding.Web
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.Onboarding.WorkflowServer", "src\samples\aspnet\Elsa.Samples.Onboarding.WorkflowServer\Elsa.Samples.Onboarding.WorkflowServer.csproj", "{199FC97A-682B-4C7B-A560-1032B16AEAC4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.JsonWorkflows", "src\samples\console\Elsa.Samples.JsonWorkflows\Elsa.Samples.JsonWorkflows.csproj", "{4FA6955C-6860-493F-ABD4-CE327A33EEA3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.JsonWorkflowProvider", "src\samples\aspnet\Elsa.Samples.JsonWorkflowProvider\Elsa.Samples.JsonWorkflowProvider.csproj", "{C5043453-5FB8-4796-9A80-C4C766F2CB62}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.WorkflowProviders.FluentStorage", "src\modules\Elsa.WorkflowProviders.FluentStorage\Elsa.WorkflowProviders.FluentStorage.csproj", "{044C3108-FE79-460A-9C31-A03C30228836}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -474,6 +480,18 @@ Global
|
|||
{199FC97A-682B-4C7B-A560-1032B16AEAC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{199FC97A-682B-4C7B-A560-1032B16AEAC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{199FC97A-682B-4C7B-A560-1032B16AEAC4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4FA6955C-6860-493F-ABD4-CE327A33EEA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4FA6955C-6860-493F-ABD4-CE327A33EEA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4FA6955C-6860-493F-ABD4-CE327A33EEA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4FA6955C-6860-493F-ABD4-CE327A33EEA3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C5043453-5FB8-4796-9A80-C4C766F2CB62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C5043453-5FB8-4796-9A80-C4C766F2CB62}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C5043453-5FB8-4796-9A80-C4C766F2CB62}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C5043453-5FB8-4796-9A80-C4C766F2CB62}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{044C3108-FE79-460A-9C31-A03C30228836}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{044C3108-FE79-460A-9C31-A03C30228836}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{044C3108-FE79-460A-9C31-A03C30228836}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{044C3108-FE79-460A-9C31-A03C30228836}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{155227F0-A33B-40AA-A4B4-06F813EB921B} = {61017E64-6D00-49CB-9E81-5002DC8F7D5F}
|
||||
|
|
@ -557,5 +575,8 @@ Global
|
|||
{FAF438E1-B771-418B-83DD-A0F959C82D97} = {873BFC3E-63C2-4495-A503-5EC05DCD84E4}
|
||||
{D64F1240-0110-42E1-AE59-E95AEC8A2900} = {56C2FFB8-EA54-45B5-A095-4A78142EB4B5}
|
||||
{199FC97A-682B-4C7B-A560-1032B16AEAC4} = {56C2FFB8-EA54-45B5-A095-4A78142EB4B5}
|
||||
{4FA6955C-6860-493F-ABD4-CE327A33EEA3} = {873BFC3E-63C2-4495-A503-5EC05DCD84E4}
|
||||
{C5043453-5FB8-4796-9A80-C4C766F2CB62} = {56C2FFB8-EA54-45B5-A095-4A78142EB4B5}
|
||||
{044C3108-FE79-460A-9C31-A03C30228836} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
|||
|
|
@ -38,9 +38,14 @@ public static class ServiceProviderExtensions
|
|||
{
|
||||
var json = await File.ReadAllTextAsync(fileName);
|
||||
var serializer = services.GetRequiredService<IActivitySerializer>();
|
||||
var workflowDefinitionRequest = serializer.Deserialize<SaveWorkflowDefinitionRequest>(json);
|
||||
var model = serializer.Deserialize<WorkflowDefinitionModel>(json);
|
||||
|
||||
workflowDefinitionRequest.Publish = true;
|
||||
var workflowDefinitionRequest = new SaveWorkflowDefinitionRequest
|
||||
{
|
||||
Model = model,
|
||||
Publish = true
|
||||
};
|
||||
|
||||
var workflowDefinitionImporter = services.GetRequiredService<IWorkflowDefinitionImporter>();
|
||||
return await workflowDefinitionImporter.ImportAsync(workflowDefinitionRequest);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export class WorkflowDefinitionsApi {
|
|||
//TODO: Written as a workaround for different server and client models.
|
||||
//To be deleted after the port model on backend is updated.
|
||||
const requestClone = cloneDeep(request);
|
||||
removeGuidsFromPortNames(requestClone.root);
|
||||
removeGuidsFromPortNames(requestClone.model.root);
|
||||
|
||||
const httpClient = await this.getHttpClient();
|
||||
const response = await httpClient.post<WorkflowDefinition>('workflow-definitions', requestClone);
|
||||
|
|
@ -179,19 +179,8 @@ export class WorkflowDefinitionsApi {
|
|||
|
||||
|
||||
export interface SaveWorkflowDefinitionRequest {
|
||||
definitionId: string;
|
||||
version?: number;
|
||||
name?: string;
|
||||
description?: string;
|
||||
model: WorkflowDefinition;
|
||||
publish: boolean;
|
||||
root?: Activity;
|
||||
variables?: Array<Variable>;
|
||||
inputs?: Array<InputDefinition>;
|
||||
outputs?: Array<InputDefinition>;
|
||||
outcomes?: Array<string>;
|
||||
options?: WorkflowOptions;
|
||||
usableAsActivity?: boolean;
|
||||
customProperties?: Map<string, any>;
|
||||
}
|
||||
|
||||
export interface BaseManyWorkflowDefinitionRequest {
|
||||
|
|
|
|||
|
|
@ -16,19 +16,8 @@ export class WorkflowDefinitionManager {
|
|||
|
||||
public saveWorkflow = async (definition: WorkflowDefinition, publish: boolean): Promise<WorkflowDefinition> => {
|
||||
const request: SaveWorkflowDefinitionRequest = {
|
||||
definitionId: definition.definitionId,
|
||||
version: definition.version,
|
||||
name: definition.name,
|
||||
description: definition.description,
|
||||
publish: publish,
|
||||
root: definition.root,
|
||||
variables: definition.variables,
|
||||
customProperties: definition.customProperties,
|
||||
inputs: definition.inputs,
|
||||
outputs: definition.outputs,
|
||||
outcomes: definition.outcomes,
|
||||
options: definition.options,
|
||||
usableAsActivity: definition.usableAsActivity
|
||||
model: definition,
|
||||
publish: publish
|
||||
};
|
||||
|
||||
return await this.api.post(request);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using Elsa.Common.Models;
|
|||
using Elsa.Http.Contracts;
|
||||
using Elsa.Workflows.Core.Helpers;
|
||||
using Elsa.Workflows.Core.State;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using Elsa.ProtoActor.Mappers;
|
|||
using Elsa.ProtoActor.Protos;
|
||||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Core.State;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
using Proto;
|
||||
using Proto.Cluster;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Elsa.ProtoActor.Mappers;
|
|||
using Elsa.ProtoActor.Protos;
|
||||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Core.State;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
using Elsa.Workflows.Runtime.Entities;
|
||||
using Proto.Cluster;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
using FluentStorage.Blobs;
|
||||
|
||||
namespace Elsa.WorkflowProviders.FluentStorage.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// A provider of <see cref="IBlobStorage"/>. The point of this interface is to provide a wrapper for actual <see cref="IBlobStorage"/> implementations.
|
||||
/// This prevents collisions when the application uses multiple <see cref="IBlobStorage"/> implementations.
|
||||
/// </summary>
|
||||
public interface IBlobStorageProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IBlobStorage"/>.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IBlobStorage"/>.</returns>
|
||||
IBlobStorage GetBlobStorage();
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\..\common.props" />
|
||||
<Import Project="..\..\..\configureawait.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||
<Description>
|
||||
Provides a workflow definition provider that loads workflow definitions from files.
|
||||
</Description>
|
||||
<PackageTags>elsa module fluentstorage json blob</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Elsa.Workflows.Runtime\Elsa.Workflows.Runtime.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentStorage" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
using Elsa.Features.Services;
|
||||
using Elsa.WorkflowProviders.FluentStorage.Features;
|
||||
using FluentStorage.Blobs;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="IModule"/> to add the fluent storage workflow definition provider.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public static class ModuleExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the fluent storage workflow definition provider.
|
||||
/// </summary>
|
||||
/// <param name="module">The module.</param>
|
||||
/// <param name="blobStorage">A callback that creates an <see cref="IBlobStorage"/>.</param>
|
||||
/// <returns>The module.</returns>
|
||||
public static IModule UseFluentStorageProvider(this IModule module, Func<IServiceProvider, IBlobStorage> blobStorage)
|
||||
{
|
||||
return module.UseFluentStorageProvider(feature => feature.BlobStorage = blobStorage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the fluent storage workflow definition provider.
|
||||
/// </summary>
|
||||
/// <param name="module">The module.</param>
|
||||
/// <param name="configure">The configuration delegate.</param>
|
||||
/// <returns>The module.</returns>
|
||||
public static IModule UseFluentStorageProvider(this IModule module, Action<FluentStorageFeature>? configure = default)
|
||||
{
|
||||
module.Use(configure);
|
||||
return module;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
using System.Reflection;
|
||||
using Elsa.Features.Abstractions;
|
||||
using Elsa.Features.Attributes;
|
||||
using Elsa.Features.Services;
|
||||
using Elsa.WorkflowProviders.FluentStorage.Contracts;
|
||||
using Elsa.WorkflowProviders.FluentStorage.Services;
|
||||
using Elsa.Workflows.Management.Features;
|
||||
using FluentStorage;
|
||||
using FluentStorage.Blobs;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.WorkflowProviders.FluentStorage.Features;
|
||||
|
||||
/// <summary>
|
||||
/// A feature that enables the FluentStorage workflow definition provider.
|
||||
/// </summary>
|
||||
[DependsOn(typeof(WorkflowManagementFeature))]
|
||||
[PublicAPI]
|
||||
public class FluentStorageFeature : FeatureBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public FluentStorageFeature(IModule module) : base(module)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The blob storage to use.
|
||||
/// </summary>
|
||||
public Func<IServiceProvider, IBlobStorage> BlobStorage { get; set; } = _ => StorageFactory.Blobs.DirectoryFiles(GetDefaultWorkflowsDirectory());
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Apply()
|
||||
{
|
||||
Services.AddSingleton<IBlobStorageProvider>(sp => new BlobStorageProvider(BlobStorage(sp)));
|
||||
Services.AddWorkflowDefinitionProvider<FluentStorageWorkflowDefinitionProvider>();
|
||||
}
|
||||
|
||||
private static string GetDefaultWorkflowsDirectory()
|
||||
{
|
||||
var entryAssemblyDir = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)!;
|
||||
var directory = Path.Combine(entryAssemblyDir, "Workflows");
|
||||
return directory;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<ConfigureAwait />
|
||||
</Weavers>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
using Elsa.WorkflowProviders.FluentStorage.Contracts;
|
||||
using FluentStorage.Blobs;
|
||||
|
||||
namespace Elsa.WorkflowProviders.FluentStorage.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A provider of <see cref="IBlobStorage"/>.
|
||||
/// </summary>
|
||||
public class BlobStorageProvider : IBlobStorageProvider
|
||||
{
|
||||
private readonly IBlobStorage _blobStorage;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BlobStorageProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="blobStorage">The <see cref="IBlobStorage"/>.</param>
|
||||
public BlobStorageProvider(IBlobStorage blobStorage)
|
||||
{
|
||||
_blobStorage = blobStorage;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IBlobStorage GetBlobStorage() => _blobStorage;
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
using Elsa.Common.Contracts;
|
||||
using Elsa.WorkflowProviders.FluentStorage.Contracts;
|
||||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
using Elsa.Workflows.Management.Mappers;
|
||||
using Elsa.Workflows.Management.Materializers;
|
||||
using Elsa.Workflows.Management.Models;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
using Elsa.Workflows.Runtime.Models;
|
||||
using FluentStorage.Blobs;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Elsa.WorkflowProviders.FluentStorage.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A workflow definition provider that loads workflow definitions from a storage using FluentStorage (See https://github.com/robinrodricks/FluentStorage).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public class FluentStorageWorkflowDefinitionProvider : IWorkflowDefinitionProvider
|
||||
{
|
||||
private readonly IBlobStorageProvider _blobStorageProvider;
|
||||
private readonly IActivitySerializer _activitySerializer;
|
||||
private readonly ISystemClock _systemClock;
|
||||
private readonly WorkflowDefinitionMapper _workflowDefinitionMapper;
|
||||
private readonly VariableDefinitionMapper _variableDefinitionMapper;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FluentStorageWorkflowDefinitionProvider"/> class.
|
||||
/// </summary>
|
||||
public FluentStorageWorkflowDefinitionProvider(
|
||||
IBlobStorageProvider blobStorageProvider,
|
||||
IActivitySerializer activitySerializer,
|
||||
ISystemClock systemClock,
|
||||
WorkflowDefinitionMapper workflowDefinitionMapper,
|
||||
VariableDefinitionMapper variableDefinitionMapper)
|
||||
{
|
||||
_blobStorageProvider = blobStorageProvider;
|
||||
_activitySerializer = activitySerializer;
|
||||
_systemClock = systemClock;
|
||||
_workflowDefinitionMapper = workflowDefinitionMapper;
|
||||
_variableDefinitionMapper = variableDefinitionMapper;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => "FluentStorage";
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask<IEnumerable<WorkflowDefinitionResult>> GetWorkflowDefinitionsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var options = new ListOptions
|
||||
{
|
||||
Recurse = true,
|
||||
};
|
||||
|
||||
var blobStorage = _blobStorageProvider.GetBlobStorage();
|
||||
var blobs = await blobStorage.ListFilesAsync(options, cancellationToken);
|
||||
var results = new List<WorkflowDefinitionResult>();
|
||||
|
||||
foreach (var blob in blobs)
|
||||
{
|
||||
var result = await ReadWorkflowDefinitionAsync(blob, cancellationToken);
|
||||
results.Add(result);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private async Task<WorkflowDefinitionResult> ReadWorkflowDefinitionAsync(Blob blob, CancellationToken cancellationToken)
|
||||
{
|
||||
var blobStorage = _blobStorageProvider.GetBlobStorage();
|
||||
var workflowJson = await blobStorage.ReadTextAsync(blob.FullPath, cancellationToken: cancellationToken);
|
||||
var workflowDefinitionModel = _activitySerializer.Deserialize<WorkflowDefinitionModel>(workflowJson);
|
||||
var variables = _variableDefinitionMapper.Map(workflowDefinitionModel.Variables).ToList();
|
||||
var rootJson = _activitySerializer.Serialize(workflowDefinitionModel.Root!);
|
||||
|
||||
var definition = new WorkflowDefinition
|
||||
{
|
||||
Id = workflowDefinitionModel.Id,
|
||||
DefinitionId = workflowDefinitionModel.DefinitionId,
|
||||
Version = workflowDefinitionModel.Version,
|
||||
Name = workflowDefinitionModel.Name,
|
||||
Description = workflowDefinitionModel.Description,
|
||||
CustomProperties = workflowDefinitionModel.CustomProperties ?? new Dictionary<string, object>(),
|
||||
Variables = variables,
|
||||
IsLatest = workflowDefinitionModel.IsLatest,
|
||||
IsPublished = workflowDefinitionModel.IsPublished,
|
||||
CreatedAt = workflowDefinitionModel.CreatedAt == default ? _systemClock.UtcNow : workflowDefinitionModel.CreatedAt,
|
||||
MaterializerName = JsonWorkflowMaterializer.MaterializerName,
|
||||
StringData = rootJson
|
||||
};
|
||||
|
||||
var workflow = _workflowDefinitionMapper.Map(definition);
|
||||
|
||||
return new WorkflowDefinitionResult(definition, workflow);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ using System.Text;
|
|||
using Elsa.Common.Models;
|
||||
using Elsa.JavaScript.TypeDefinitions.Contracts;
|
||||
using Elsa.JavaScript.TypeDefinitions.Models;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
using FastEndpoints;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
using System.Text.Json;
|
||||
using Elsa.Abstractions;
|
||||
using Elsa.Common.Models;
|
||||
using Elsa.Workflows.Api.Models;
|
||||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Core.Serialization.Converters;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Management.Mappers;
|
||||
using Elsa.Workflows.Management.Models;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
using Humanizer;
|
||||
|
||||
|
|
@ -71,6 +71,7 @@ public class Export : ElsaEndpoint<Request>
|
|||
definition.UsableAsActivity,
|
||||
definition.IsLatest,
|
||||
definition.IsPublished,
|
||||
definition.Options,
|
||||
workflow.Root);
|
||||
|
||||
var serializerOptions = _serializer.CreateOptions();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
using Elsa.Abstractions;
|
||||
using Elsa.Common.Entities;
|
||||
using Elsa.Common.Models;
|
||||
using Elsa.Workflows.Api.Mappers;
|
||||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Core.Serialization.Converters;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Management.Mappers;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.Get;
|
||||
|
|
@ -13,11 +13,13 @@ internal class Get : ElsaEndpoint<Request>
|
|||
{
|
||||
private readonly IWorkflowDefinitionStore _store;
|
||||
private readonly IApiSerializer _apiSerializer;
|
||||
private readonly WorkflowDefinitionMapper _mapper;
|
||||
|
||||
public Get(IWorkflowDefinitionStore store, IApiSerializer apiSerializer)
|
||||
public Get(IWorkflowDefinitionStore store, IApiSerializer apiSerializer, WorkflowDefinitionMapper mapper)
|
||||
{
|
||||
_store = store;
|
||||
_apiSerializer = apiSerializer;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public override void Configure()
|
||||
|
|
@ -44,15 +46,14 @@ internal class Get : ElsaEndpoint<Request>
|
|||
await SendNotFoundAsync(cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
||||
var mapper = new WorkflowDefinitionMapper();
|
||||
var response = await mapper.FromEntityAsync(definition, cancellationToken);
|
||||
|
||||
var model = await _mapper.MapAsync(definition, cancellationToken);
|
||||
var serializerOptions = _apiSerializer.CreateOptions();
|
||||
|
||||
// If the root of composite activities is not requested, exclude them from being serialized.
|
||||
if (!request.IncludeCompositeRoot)
|
||||
serializerOptions.Converters.Add(new JsonIgnoreCompositeRootConverterFactory());
|
||||
|
||||
await HttpContext.Response.WriteAsJsonAsync(response, serializerOptions, cancellationToken);
|
||||
await HttpContext.Response.WriteAsJsonAsync(model, serializerOptions, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +1,30 @@
|
|||
using Elsa.Abstractions;
|
||||
using Elsa.Workflows.Api.Models;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Management.Mappers;
|
||||
using Elsa.Workflows.Management.Models;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.Import;
|
||||
|
||||
/// <summary>
|
||||
/// Imports a JSON file containing a workflow definition.
|
||||
/// </summary>
|
||||
internal class Import : ElsaEndpoint<SaveWorkflowDefinitionRequest, WorkflowDefinitionResponse>
|
||||
[PublicAPI]
|
||||
internal class Import : ElsaEndpoint<WorkflowDefinitionModel, WorkflowDefinitionModel>
|
||||
{
|
||||
private readonly IWorkflowDefinitionService _workflowDefinitionService;
|
||||
private readonly IWorkflowDefinitionImporter _workflowDefinitionImporter;
|
||||
private readonly WorkflowDefinitionMapper _workflowDefinitionMapper;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Import(
|
||||
IWorkflowDefinitionService workflowDefinitionService,
|
||||
IWorkflowDefinitionImporter workflowDefinitionImporter)
|
||||
IWorkflowDefinitionImporter workflowDefinitionImporter,
|
||||
WorkflowDefinitionMapper workflowDefinitionMapper)
|
||||
{
|
||||
_workflowDefinitionService = workflowDefinitionService;
|
||||
_workflowDefinitionImporter = workflowDefinitionImporter;
|
||||
_workflowDefinitionMapper = workflowDefinitionMapper;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -32,38 +36,25 @@ internal class Import : ElsaEndpoint<SaveWorkflowDefinitionRequest, WorkflowDefi
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task HandleAsync(SaveWorkflowDefinitionRequest request, CancellationToken cancellationToken)
|
||||
public override async Task HandleAsync(WorkflowDefinitionModel model, CancellationToken cancellationToken)
|
||||
{
|
||||
var definitionId = request.DefinitionId;
|
||||
var definitionId = model.DefinitionId;
|
||||
var isNew = string.IsNullOrWhiteSpace(definitionId);
|
||||
|
||||
// Import workflow
|
||||
var draft = await _workflowDefinitionImporter.ImportAsync(request, cancellationToken);
|
||||
var saveWorkflowRequest = new SaveWorkflowDefinitionRequest
|
||||
{
|
||||
Model = model,
|
||||
Publish = false,
|
||||
};
|
||||
var draft = await _workflowDefinitionImporter.ImportAsync(saveWorkflowRequest, cancellationToken);
|
||||
|
||||
// Materialize the workflow definition for serialization.
|
||||
var workflow = await _workflowDefinitionService.MaterializeWorkflowAsync(draft, cancellationToken);
|
||||
|
||||
var response = new WorkflowDefinitionResponse(
|
||||
draft.Id,
|
||||
draft.DefinitionId,
|
||||
draft.Name,
|
||||
draft.Description,
|
||||
draft.CreatedAt,
|
||||
draft.Version,
|
||||
request.Variables ?? new List<VariableDefinition>(),
|
||||
draft.Inputs,
|
||||
draft.Outputs,
|
||||
draft.Outcomes,
|
||||
draft.CustomProperties,
|
||||
draft.IsLatest,
|
||||
draft.IsPublished,
|
||||
draft.UsableAsActivity,
|
||||
workflow.Root,
|
||||
draft.Options);
|
||||
// Map the workflow definition for serialization.
|
||||
var updatedModel = await _workflowDefinitionMapper.MapAsync(draft, cancellationToken);
|
||||
|
||||
if (isNew)
|
||||
await SendCreatedAtAsync<Get.Get>(new { DefinitionId = definitionId }, response, cancellation: cancellationToken);
|
||||
await SendCreatedAtAsync<Get.Get>(new { DefinitionId = definitionId }, updatedModel, cancellation: cancellationToken);
|
||||
else
|
||||
await SendOkAsync(response, cancellationToken);
|
||||
await SendOkAsync(updatedModel, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
using Elsa.Abstractions;
|
||||
using Elsa.Common.Models;
|
||||
using Elsa.Workflows.Api.Models;
|
||||
using Elsa.Workflows.Core.Activities;
|
||||
using Elsa.Workflows.Management.Mappers;
|
||||
using Elsa.Workflows.Management.Materializers;
|
||||
|
|
@ -8,7 +7,6 @@ using Elsa.Workflows.Management.Models;
|
|||
using JetBrains.Annotations;
|
||||
using Medallion.Threading;
|
||||
using System.Text.Json;
|
||||
using Elsa.Workflows.Api.Mappers;
|
||||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Core.Serialization.Converters;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
|
|
@ -17,22 +15,25 @@ using Microsoft.AspNetCore.Http;
|
|||
namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.Post;
|
||||
|
||||
[PublicAPI]
|
||||
internal class Post : ElsaEndpoint<SaveWorkflowDefinitionRequest, WorkflowDefinitionResponse, WorkflowDefinitionMapper>
|
||||
internal class Post : ElsaEndpoint<SaveWorkflowDefinitionRequest, WorkflowDefinitionModel>
|
||||
{
|
||||
private readonly IApiSerializer _serializer;
|
||||
private readonly IWorkflowDefinitionPublisher _workflowDefinitionPublisher;
|
||||
private readonly VariableDefinitionMapper _variableDefinitionMapper;
|
||||
private readonly WorkflowDefinitionMapper _workflowDefinitionMapper;
|
||||
private readonly IDistributedLockProvider _distributedLockProvider;
|
||||
|
||||
public Post(
|
||||
IApiSerializer serializer,
|
||||
IWorkflowDefinitionPublisher workflowDefinitionPublisher,
|
||||
VariableDefinitionMapper variableDefinitionMapper,
|
||||
WorkflowDefinitionMapper workflowDefinitionMapper,
|
||||
IDistributedLockProvider distributedLockProvider)
|
||||
{
|
||||
_serializer = serializer;
|
||||
_workflowDefinitionPublisher = workflowDefinitionPublisher;
|
||||
_variableDefinitionMapper = variableDefinitionMapper;
|
||||
_workflowDefinitionMapper = workflowDefinitionMapper;
|
||||
_distributedLockProvider = distributedLockProvider;
|
||||
}
|
||||
|
||||
|
|
@ -44,7 +45,8 @@ internal class Post : ElsaEndpoint<SaveWorkflowDefinitionRequest, WorkflowDefini
|
|||
|
||||
public override async Task HandleAsync(SaveWorkflowDefinitionRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
var definitionId = request.DefinitionId;
|
||||
var model = request.Model;
|
||||
var definitionId = model.DefinitionId;
|
||||
var resourceName = $"{GetType().FullName}:{(!string.IsNullOrWhiteSpace(definitionId) ? definitionId : Guid.NewGuid().ToString())}";
|
||||
|
||||
await using var handle = await _distributedLockProvider.AcquireLockAsync(resourceName, TimeSpan.FromMinutes(1), cancellationToken);
|
||||
|
|
@ -65,32 +67,32 @@ internal class Post : ElsaEndpoint<SaveWorkflowDefinitionRequest, WorkflowDefini
|
|||
}
|
||||
|
||||
// Update the draft with the received model.
|
||||
var root = request.Root ?? new Sequence();
|
||||
var root = model.Root ?? new Sequence();
|
||||
var serializerOptions = _serializer.CreateOptions();
|
||||
|
||||
// Ignore the root activity when serializing the workflow definition.
|
||||
serializerOptions.Converters.Add(new JsonIgnoreCompositeRootConverterFactory());
|
||||
|
||||
var stringData = JsonSerializer.Serialize(root, serializerOptions);
|
||||
var variables = _variableDefinitionMapper.Map(request.Variables).ToList();
|
||||
var inputs = request.Inputs ?? new List<InputDefinition>();
|
||||
var outputs = request.Outputs ?? new List<OutputDefinition>();
|
||||
var outcomes = request.Outcomes ?? new List<string>();
|
||||
var variables = _variableDefinitionMapper.Map(model.Variables).ToList();
|
||||
var inputs = model.Inputs ?? new List<InputDefinition>();
|
||||
var outputs = model.Outputs ?? new List<OutputDefinition>();
|
||||
var outcomes = model.Outcomes ?? new List<string>();
|
||||
|
||||
draft!.StringData = stringData;
|
||||
draft.MaterializerName = JsonWorkflowMaterializer.MaterializerName;
|
||||
draft.Name = request.Name?.Trim();
|
||||
draft.Description = request.Description?.Trim();
|
||||
draft.CustomProperties = request.CustomProperties ?? new Dictionary<string, object>();
|
||||
draft.Name = model.Name?.Trim();
|
||||
draft.Description = model.Description?.Trim();
|
||||
draft.CustomProperties = model.CustomProperties ?? new Dictionary<string, object>();
|
||||
draft.Variables = variables;
|
||||
draft.Inputs = inputs;
|
||||
draft.Outputs = outputs;
|
||||
draft.Outcomes = outcomes;
|
||||
draft.Options = request.Options;
|
||||
draft.UsableAsActivity = request.UsableAsActivity;
|
||||
draft.Options = model.Options;
|
||||
draft.UsableAsActivity = model.UsableAsActivity;
|
||||
draft = request.Publish ? await _workflowDefinitionPublisher.PublishAsync(draft, cancellationToken) : await _workflowDefinitionPublisher.SaveDraftAsync(draft, cancellationToken);
|
||||
|
||||
var response = await Map.FromEntityAsync(draft, cancellationToken);
|
||||
var response = await _workflowDefinitionMapper.MapAsync(draft, cancellationToken);
|
||||
|
||||
if (isNew)
|
||||
await SendCreatedAtAsync<Get.Get>(new { definitionId }, response, cancellation: cancellationToken);
|
||||
|
|
|
|||
|
|
@ -1,27 +1,29 @@
|
|||
using Elsa.Abstractions;
|
||||
using Elsa.Common.Models;
|
||||
using Elsa.Workflows.Api.Mappers;
|
||||
using Elsa.Workflows.Api.Models;
|
||||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Core.Serialization.Converters;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Management.Mappers;
|
||||
using Elsa.Workflows.Management.Models;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.Publish;
|
||||
|
||||
[PublicAPI]
|
||||
internal class Publish : ElsaEndpoint<Request, WorkflowDefinitionResponse, WorkflowDefinitionMapper>
|
||||
internal class Publish : ElsaEndpoint<Request, WorkflowDefinitionModel>
|
||||
{
|
||||
private readonly IWorkflowDefinitionStore _store;
|
||||
private readonly IWorkflowDefinitionPublisher _workflowDefinitionPublisher;
|
||||
private readonly IApiSerializer _serializer;
|
||||
private readonly WorkflowDefinitionMapper _workflowDefinitionMapper;
|
||||
|
||||
public Publish(IWorkflowDefinitionStore store, IWorkflowDefinitionPublisher workflowDefinitionPublisher, IApiSerializer serializer)
|
||||
public Publish(IWorkflowDefinitionStore store, IWorkflowDefinitionPublisher workflowDefinitionPublisher, IApiSerializer serializer, WorkflowDefinitionMapper workflowDefinitionMapper)
|
||||
{
|
||||
_store = store;
|
||||
_workflowDefinitionPublisher = workflowDefinitionPublisher;
|
||||
_serializer = serializer;
|
||||
_workflowDefinitionMapper = workflowDefinitionMapper;
|
||||
}
|
||||
|
||||
public override void Configure()
|
||||
|
|
@ -55,7 +57,7 @@ internal class Publish : ElsaEndpoint<Request, WorkflowDefinitionResponse, Workf
|
|||
|
||||
await _workflowDefinitionPublisher.PublishAsync(definition, cancellationToken);
|
||||
|
||||
var response = await Map.FromEntityAsync(definition, cancellationToken);
|
||||
var response = await _workflowDefinitionMapper.MapAsync(definition, cancellationToken);
|
||||
|
||||
// We do not want to include composite root activities in the response.
|
||||
var serializerOptions = _serializer.CreateOptions();
|
||||
|
|
|
|||
|
|
@ -1,22 +1,24 @@
|
|||
using Elsa.Abstractions;
|
||||
using Elsa.Common.Models;
|
||||
using Elsa.Workflows.Api.Mappers;
|
||||
using Elsa.Workflows.Api.Models;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Management.Mappers;
|
||||
using Elsa.Workflows.Management.Models;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.Retract;
|
||||
|
||||
[PublicAPI]
|
||||
internal class Retract : ElsaEndpoint<Request, WorkflowDefinitionResponse, WorkflowDefinitionMapper>
|
||||
internal class Retract : ElsaEndpoint<Request, WorkflowDefinitionModel>
|
||||
{
|
||||
private readonly IWorkflowDefinitionStore _store;
|
||||
private readonly IWorkflowDefinitionPublisher _workflowDefinitionPublisher;
|
||||
private readonly WorkflowDefinitionMapper _workflowDefinitionMapper;
|
||||
|
||||
public Retract(IWorkflowDefinitionStore store, IWorkflowDefinitionPublisher workflowDefinitionPublisher)
|
||||
public Retract(IWorkflowDefinitionStore store, IWorkflowDefinitionPublisher workflowDefinitionPublisher, WorkflowDefinitionMapper workflowDefinitionMapper)
|
||||
{
|
||||
_store = store;
|
||||
_workflowDefinitionPublisher = workflowDefinitionPublisher;
|
||||
_workflowDefinitionMapper = workflowDefinitionMapper;
|
||||
}
|
||||
|
||||
public override void Configure()
|
||||
|
|
@ -49,7 +51,7 @@ internal class Retract : ElsaEndpoint<Request, WorkflowDefinitionResponse, Workf
|
|||
}
|
||||
|
||||
await _workflowDefinitionPublisher.RetractAsync(definition, cancellationToken);
|
||||
var response = await Map.FromEntityAsync(definition, cancellationToken);
|
||||
var response = await _workflowDefinitionMapper.MapAsync(definition, cancellationToken);
|
||||
await SendOkAsync(response, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ using Elsa.JavaScript.Features;
|
|||
using Elsa.Workflows.Api.Serialization;
|
||||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Management.Features;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.Api.Features;
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
using Elsa.Workflows.Api.Models;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
using Elsa.Workflows.Management.Mappers;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
using FastEndpoints;
|
||||
|
||||
namespace Elsa.Workflows.Api.Mappers;
|
||||
|
||||
internal class WorkflowDefinitionMapper : ResponseMapper<WorkflowDefinitionResponse, WorkflowDefinition>
|
||||
{
|
||||
public override async Task<WorkflowDefinitionResponse> FromEntityAsync(WorkflowDefinition entity, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var workflowDefinitionService = Resolve<IWorkflowDefinitionService>();
|
||||
var variableDefinitionMapper = Resolve<VariableDefinitionMapper>();
|
||||
var workflow = await workflowDefinitionService.MaterializeWorkflowAsync(entity, cancellationToken);
|
||||
var variables = variableDefinitionMapper.Map(workflow.Variables).ToList();
|
||||
|
||||
return new(
|
||||
entity.Id,
|
||||
entity.DefinitionId,
|
||||
entity.Name,
|
||||
entity.Description,
|
||||
entity.CreatedAt,
|
||||
entity.Version,
|
||||
variables,
|
||||
entity.Inputs,
|
||||
entity.Outputs,
|
||||
entity.Outcomes,
|
||||
entity.CustomProperties,
|
||||
entity.IsLatest,
|
||||
entity.IsPublished,
|
||||
entity.UsableAsActivity,
|
||||
workflow.Root,
|
||||
workflow.Options);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Management.Models;
|
||||
|
||||
namespace Elsa.Workflows.Api.Models;
|
||||
|
||||
internal record WorkflowDefinitionModel(
|
||||
string Id,
|
||||
string DefinitionId,
|
||||
string? Name,
|
||||
string? Description,
|
||||
DateTimeOffset CreatedAt,
|
||||
int Version,
|
||||
ICollection<VariableDefinition> Variables,
|
||||
ICollection<InputDefinition> Inputs,
|
||||
ICollection<OutputDefinition> Outputs,
|
||||
ICollection<string> Outcomes,
|
||||
IDictionary<string, object> Metadata,
|
||||
bool? UsableAsActivity,
|
||||
bool IsLatest,
|
||||
bool IsPublished,
|
||||
IActivity Root
|
||||
);
|
||||
|
|
@ -1,61 +1,61 @@
|
|||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Core.Models;
|
||||
using Elsa.Workflows.Management.Models;
|
||||
|
||||
namespace Elsa.Workflows.Api.Models;
|
||||
|
||||
internal class WorkflowDefinitionResponse
|
||||
{
|
||||
public WorkflowDefinitionResponse(
|
||||
string id,
|
||||
string definitionId,
|
||||
string? name,
|
||||
string? description,
|
||||
DateTimeOffset createdAt,
|
||||
int version,
|
||||
ICollection<VariableDefinition> variables,
|
||||
ICollection<InputDefinition> inputs,
|
||||
ICollection<OutputDefinition> outputs,
|
||||
ICollection<string> outcomes,
|
||||
IDictionary<string, object> customProperties,
|
||||
bool isLatest,
|
||||
bool isPublished,
|
||||
bool? usableAsActivity,
|
||||
IActivity root,
|
||||
WorkflowOptions? options)
|
||||
{
|
||||
Id = id;
|
||||
DefinitionId = definitionId;
|
||||
Name = name;
|
||||
Description = description;
|
||||
CreatedAt = createdAt;
|
||||
Version = version;
|
||||
Variables = variables;
|
||||
Inputs = inputs;
|
||||
Outputs = outputs;
|
||||
Outcomes = outcomes;
|
||||
CustomProperties = customProperties;
|
||||
IsLatest = isLatest;
|
||||
IsPublished = isPublished;
|
||||
UsableAsActivity = usableAsActivity;
|
||||
Root = root;
|
||||
Options = options;
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
public string DefinitionId { get; }
|
||||
public string? Name { get; }
|
||||
public string? Description { get; }
|
||||
public DateTimeOffset CreatedAt { get; }
|
||||
public int Version { get; }
|
||||
public ICollection<VariableDefinition> Variables { get; }
|
||||
public ICollection<InputDefinition> Inputs { get; }
|
||||
public ICollection<OutputDefinition> Outputs { get; }
|
||||
public ICollection<string> Outcomes { get; }
|
||||
public IDictionary<string, object> CustomProperties { get; }
|
||||
public bool IsLatest { get; }
|
||||
public bool IsPublished { get; }
|
||||
public bool? UsableAsActivity { get; }
|
||||
public IActivity Root { get; }
|
||||
public WorkflowOptions? Options { get; set; }
|
||||
}
|
||||
// using Elsa.Workflows.Core.Contracts;
|
||||
// using Elsa.Workflows.Core.Models;
|
||||
// using Elsa.Workflows.Management.Models;
|
||||
//
|
||||
// namespace Elsa.Workflows.Api.Models;
|
||||
//
|
||||
// internal class WorkflowDefinitionResponse
|
||||
// {
|
||||
// public WorkflowDefinitionResponse(
|
||||
// string id,
|
||||
// string definitionId,
|
||||
// string? name,
|
||||
// string? description,
|
||||
// DateTimeOffset createdAt,
|
||||
// int version,
|
||||
// ICollection<VariableDefinition> variables,
|
||||
// ICollection<InputDefinition> inputs,
|
||||
// ICollection<OutputDefinition> outputs,
|
||||
// ICollection<string> outcomes,
|
||||
// IDictionary<string, object> customProperties,
|
||||
// bool isLatest,
|
||||
// bool isPublished,
|
||||
// bool? usableAsActivity,
|
||||
// IActivity root,
|
||||
// WorkflowOptions? options)
|
||||
// {
|
||||
// Id = id;
|
||||
// DefinitionId = definitionId;
|
||||
// Name = name;
|
||||
// Description = description;
|
||||
// CreatedAt = createdAt;
|
||||
// Version = version;
|
||||
// Variables = variables;
|
||||
// Inputs = inputs;
|
||||
// Outputs = outputs;
|
||||
// Outcomes = outcomes;
|
||||
// CustomProperties = customProperties;
|
||||
// IsLatest = isLatest;
|
||||
// IsPublished = isPublished;
|
||||
// UsableAsActivity = usableAsActivity;
|
||||
// Root = root;
|
||||
// Options = options;
|
||||
// }
|
||||
//
|
||||
// public string Id { get; }
|
||||
// public string DefinitionId { get; }
|
||||
// public string? Name { get; }
|
||||
// public string? Description { get; }
|
||||
// public DateTimeOffset CreatedAt { get; }
|
||||
// public int Version { get; }
|
||||
// public ICollection<VariableDefinition> Variables { get; }
|
||||
// public ICollection<InputDefinition> Inputs { get; }
|
||||
// public ICollection<OutputDefinition> Outputs { get; }
|
||||
// public ICollection<string> Outcomes { get; }
|
||||
// public IDictionary<string, object> CustomProperties { get; }
|
||||
// public bool IsLatest { get; }
|
||||
// public bool IsPublished { get; }
|
||||
// public bool? UsableAsActivity { get; }
|
||||
// public IActivity Root { get; }
|
||||
// public WorkflowOptions? Options { get; set; }
|
||||
// }
|
||||
|
|
@ -11,7 +11,7 @@ namespace Elsa.Workflows.Management.Contracts
|
|||
/// <summary>
|
||||
/// Imports a workflow definition.
|
||||
/// </summary>
|
||||
/// <param name="request">A DTO representing the workflow definition to import.</param>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The imported workflow definition.</returns>
|
||||
Task<WorkflowDefinition> ImportAsync(SaveWorkflowDefinitionRequest request, CancellationToken cancellationToken = default);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ using Elsa.Common.Models;
|
|||
using Elsa.Workflows.Core.Models;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
|
||||
namespace Elsa.Workflows.Runtime.Contracts;
|
||||
namespace Elsa.Workflows.Management.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Manages materialization of <see cref="WorkflowDefinition"/> to <see cref="Workflow"/> objects.
|
||||
|
|
@ -148,6 +148,7 @@ public class WorkflowManagementFeature : FeatureBase
|
|||
.AddSingleton<IWorkflowMaterializer, JsonWorkflowMaterializer>()
|
||||
.AddSingleton<IActivityPortResolver, WorkflowDefinitionActivityPortResolver>()
|
||||
.AddActivityProvider<WorkflowDefinitionActivityProvider>()
|
||||
.AddSingleton<WorkflowDefinitionMapper>()
|
||||
.AddSingleton<VariableDefinitionMapper>()
|
||||
;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Core.Models;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
using Elsa.Workflows.Management.Models;
|
||||
|
||||
namespace Elsa.Workflows.Management.Mappers;
|
||||
|
||||
/// <summary>
|
||||
/// Maps <see cref="WorkflowDefinition"/> to and from <see cref="Workflow"/> and <see cref="WorkflowDefinitionModel"/>.
|
||||
/// </summary>
|
||||
public class WorkflowDefinitionMapper
|
||||
{
|
||||
private readonly IActivitySerializer _activitySerializer;
|
||||
private readonly IWorkflowDefinitionService _workflowDefinitionService;
|
||||
private readonly VariableDefinitionMapper _variableDefinitionMapper;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WorkflowDefinitionMapper"/> class.
|
||||
/// </summary>
|
||||
public WorkflowDefinitionMapper(IActivitySerializer activitySerializer, IWorkflowDefinitionService workflowDefinitionService, VariableDefinitionMapper variableDefinitionMapper)
|
||||
{
|
||||
_activitySerializer = activitySerializer;
|
||||
_workflowDefinitionService = workflowDefinitionService;
|
||||
_variableDefinitionMapper = variableDefinitionMapper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="WorkflowDefinition"/> to a <see cref="Workflow"/>.
|
||||
/// </summary>
|
||||
/// <param name="source">The source <see cref="WorkflowDefinition"/>.</param>
|
||||
/// <returns>The mapped <see cref="Workflow"/>.</returns>
|
||||
public Workflow Map(WorkflowDefinition source)
|
||||
{
|
||||
var root = _activitySerializer.Deserialize(source.StringData!);
|
||||
|
||||
return new(
|
||||
new WorkflowIdentity(source.DefinitionId, source.Version, source.Id),
|
||||
new WorkflowPublication(source.IsLatest, source.IsPublished),
|
||||
new WorkflowMetadata(source.Name, source.Description, source.CreatedAt),
|
||||
source.Options,
|
||||
root,
|
||||
source.Variables,
|
||||
source.CustomProperties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="WorkflowDefinition"/> to a <see cref="Workflow"/>.
|
||||
/// </summary>
|
||||
/// <param name="workflowDefinition">The source <see cref="WorkflowDefinition"/>.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The mapped <see cref="Workflow"/>.</returns>
|
||||
public async Task<WorkflowDefinitionModel> MapAsync(WorkflowDefinition workflowDefinition, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var workflow = await _workflowDefinitionService.MaterializeWorkflowAsync(workflowDefinition, cancellationToken);
|
||||
var variables = _variableDefinitionMapper.Map(workflow.Variables).ToList();
|
||||
|
||||
return new(
|
||||
workflowDefinition.Id,
|
||||
workflowDefinition.DefinitionId,
|
||||
workflowDefinition.Name,
|
||||
workflowDefinition.Description,
|
||||
workflowDefinition.CreatedAt,
|
||||
workflowDefinition.Version,
|
||||
variables,
|
||||
workflowDefinition.Inputs,
|
||||
workflowDefinition.Outputs,
|
||||
workflowDefinition.Outcomes,
|
||||
workflowDefinition.CustomProperties,
|
||||
workflowDefinition.UsableAsActivity,
|
||||
workflowDefinition.IsLatest,
|
||||
workflowDefinition.IsPublished,
|
||||
workflow.Options,
|
||||
workflow.Root);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Core.Models;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
using Elsa.Workflows.Management.Mappers;
|
||||
|
||||
namespace Elsa.Workflows.Management.Materializers;
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ namespace Elsa.Workflows.Management.Materializers;
|
|||
/// </summary>
|
||||
public class JsonWorkflowMaterializer : IWorkflowMaterializer
|
||||
{
|
||||
private readonly IActivitySerializer _activitySerializer;
|
||||
private readonly WorkflowDefinitionMapper _workflowDefinitionMapper;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the materializer.
|
||||
|
|
@ -23,9 +23,9 @@ public class JsonWorkflowMaterializer : IWorkflowMaterializer
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonWorkflowMaterializer"/> class.
|
||||
/// </summary>
|
||||
public JsonWorkflowMaterializer(IActivitySerializer activitySerializer)
|
||||
public JsonWorkflowMaterializer(WorkflowDefinitionMapper workflowDefinitionMapper)
|
||||
{
|
||||
_activitySerializer = activitySerializer;
|
||||
_workflowDefinitionMapper = workflowDefinitionMapper;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -35,17 +35,5 @@ public class JsonWorkflowMaterializer : IWorkflowMaterializer
|
|||
return new ValueTask<Workflow>(workflow);
|
||||
}
|
||||
|
||||
private Workflow ToWorkflow(WorkflowDefinition definition)
|
||||
{
|
||||
var root = _activitySerializer.Deserialize(definition.StringData!);
|
||||
|
||||
return new(
|
||||
new WorkflowIdentity(definition.DefinitionId, definition.Version, definition.Id),
|
||||
new WorkflowPublication(definition.IsLatest, definition.IsPublished),
|
||||
new WorkflowMetadata(definition.Name, definition.Description, definition.CreatedAt),
|
||||
definition.Options,
|
||||
root,
|
||||
definition.Variables,
|
||||
definition.CustomProperties);
|
||||
}
|
||||
private Workflow ToWorkflow(WorkflowDefinition definition) => _workflowDefinitionMapper.Map(definition);
|
||||
}
|
||||
|
|
@ -1,21 +1,18 @@
|
|||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Core.Models;
|
||||
|
||||
namespace Elsa.Workflows.Management.Models
|
||||
namespace Elsa.Workflows.Management.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a request to save a workflow definition.
|
||||
/// </summary>
|
||||
public class SaveWorkflowDefinitionRequest
|
||||
{
|
||||
public string? DefinitionId { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public ICollection<VariableDefinition>? Variables { get; set; }
|
||||
public ICollection<InputDefinition>? Inputs { get; set; }
|
||||
public ICollection<OutputDefinition>? Outputs { get; set; }
|
||||
public ICollection<string>? Outcomes { get; set; }
|
||||
public IDictionary<string, object>? CustomProperties { get; set; }
|
||||
public IActivity? Root { get; set; }
|
||||
public WorkflowOptions? Options { get; set; }
|
||||
/// <summary>
|
||||
/// The workflow definition to save.
|
||||
/// </summary>
|
||||
public WorkflowDefinitionModel Model { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the workflow definition should be published.
|
||||
/// </summary>
|
||||
public bool Publish { get; set; }
|
||||
public bool? UsableAsActivity { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Core.Models;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Elsa.Workflows.Management.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a serializable workflow definition.
|
||||
/// </summary>
|
||||
/// <param name="Id"></param>
|
||||
/// <param name="DefinitionId"></param>
|
||||
/// <param name="Name"></param>
|
||||
/// <param name="Description"></param>
|
||||
/// <param name="CreatedAt"></param>
|
||||
/// <param name="Version"></param>
|
||||
/// <param name="Variables"></param>
|
||||
/// <param name="Inputs"></param>
|
||||
/// <param name="Outputs"></param>
|
||||
/// <param name="Outcomes"></param>
|
||||
/// <param name="CustomProperties"></param>
|
||||
/// <param name="UsableAsActivity"></param>
|
||||
/// <param name="IsLatest"></param>
|
||||
/// <param name="IsPublished"></param>
|
||||
/// <param name="Options">The type of <see cref="IWorkflowActivationStrategy"/> to apply when new instances are requested to be created.</param>
|
||||
/// <param name="Root"></param>
|
||||
[PublicAPI]
|
||||
public record WorkflowDefinitionModel(
|
||||
string Id,
|
||||
string DefinitionId,
|
||||
string? Name,
|
||||
string? Description,
|
||||
DateTimeOffset CreatedAt,
|
||||
int Version,
|
||||
ICollection<VariableDefinition>? Variables,
|
||||
ICollection<InputDefinition>? Inputs,
|
||||
ICollection<OutputDefinition>? Outputs,
|
||||
ICollection<string>? Outcomes,
|
||||
IDictionary<string, object>? CustomProperties,
|
||||
bool? UsableAsActivity,
|
||||
bool IsLatest,
|
||||
bool IsPublished,
|
||||
WorkflowOptions? Options,
|
||||
IActivity? Root
|
||||
)
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public WorkflowDefinitionModel() : this(
|
||||
default!,
|
||||
default!,
|
||||
default!,
|
||||
default!,
|
||||
default!,
|
||||
default!,
|
||||
default!,
|
||||
default!,
|
||||
default!,
|
||||
default!,
|
||||
default!,
|
||||
default!,
|
||||
default!,
|
||||
default!,
|
||||
default!,
|
||||
default!)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using Elsa.Common.Models;
|
||||
using Elsa.Workflows.Core.Activities;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
using Elsa.Workflows.Management.Mappers;
|
||||
|
|
@ -31,7 +32,8 @@ namespace Elsa.Workflows.Management.Services
|
|||
/// <inheritdoc />
|
||||
public async Task<WorkflowDefinition> ImportAsync(SaveWorkflowDefinitionRequest request, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var definitionId = request.DefinitionId;
|
||||
var model = request.Model;
|
||||
var definitionId = model.DefinitionId;
|
||||
|
||||
// Get a workflow draft version.
|
||||
var draft = !string.IsNullOrWhiteSpace(definitionId)
|
||||
|
|
@ -50,21 +52,21 @@ namespace Elsa.Workflows.Management.Services
|
|||
}
|
||||
|
||||
// Update the draft with the received model.
|
||||
var root = request.Root!;
|
||||
var root = model.Root ?? new Sequence();
|
||||
var stringData = _serializer.Serialize(root);
|
||||
var variables = _variableDefinitionMapper.Map(request.Variables).ToList();
|
||||
var variables = _variableDefinitionMapper.Map(model.Variables).ToList();
|
||||
|
||||
draft!.StringData = stringData;
|
||||
draft.MaterializerName = JsonWorkflowMaterializer.MaterializerName;
|
||||
draft.Name = request.Name?.Trim();
|
||||
draft.Description = request.Description?.Trim();
|
||||
draft.CustomProperties = request.CustomProperties ?? new Dictionary<string, object>();
|
||||
draft.Name = model.Name?.Trim();
|
||||
draft.Description = model.Description?.Trim();
|
||||
draft.CustomProperties = model.CustomProperties ?? new Dictionary<string, object>();
|
||||
draft.Variables = variables;
|
||||
draft.Inputs = request.Inputs ?? new List<InputDefinition>();
|
||||
draft.Outputs = request.Outputs ?? new List<OutputDefinition>();
|
||||
draft.Outcomes = request.Outcomes ?? new List<string>();
|
||||
draft.Options = request.Options;
|
||||
draft.UsableAsActivity = request.UsableAsActivity;
|
||||
draft.Inputs = model.Inputs ?? new List<InputDefinition>();
|
||||
draft.Outputs = model.Outputs ?? new List<OutputDefinition>();
|
||||
draft.Outcomes = model.Outcomes ?? new List<string>();
|
||||
draft.Options = model.Options;
|
||||
draft.UsableAsActivity = model.UsableAsActivity;
|
||||
draft = request.Publish ? await _workflowDefinitionPublisher.PublishAsync(draft, cancellationToken) : await _workflowDefinitionPublisher.SaveDraftAsync(draft, cancellationToken);
|
||||
|
||||
return draft;
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ using Elsa.Workflows.Core.Contracts;
|
|||
using Elsa.Workflows.Core.Models;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
|
||||
namespace Elsa.Workflows.Runtime.Services;
|
||||
namespace Elsa.Workflows.Management.Services;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class WorkflowDefinitionService : IWorkflowDefinitionService
|
||||
|
|
@ -14,7 +13,7 @@ public class WorkflowDefinitionService : IWorkflowDefinitionService
|
|||
private readonly IWorkflowDefinitionStore _workflowDefinitionStore;
|
||||
private readonly IActivityVisitor _activityVisitor;
|
||||
private readonly IIdentityGraphService _identityGraphService;
|
||||
private readonly IEnumerable<IWorkflowMaterializer> _materializers;
|
||||
private readonly Func<IEnumerable<IWorkflowMaterializer>> _materializers;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
|
|
@ -23,7 +22,7 @@ public class WorkflowDefinitionService : IWorkflowDefinitionService
|
|||
IWorkflowDefinitionStore workflowDefinitionStore,
|
||||
IActivityVisitor activityVisitor,
|
||||
IIdentityGraphService identityGraphService,
|
||||
IEnumerable<IWorkflowMaterializer> materializers)
|
||||
Func<IEnumerable<IWorkflowMaterializer>> materializers)
|
||||
{
|
||||
_workflowDefinitionStore = workflowDefinitionStore;
|
||||
_activityVisitor = activityVisitor;
|
||||
|
|
@ -34,7 +33,8 @@ public class WorkflowDefinitionService : IWorkflowDefinitionService
|
|||
/// <inheritdoc />
|
||||
public async Task<Workflow> MaterializeWorkflowAsync(WorkflowDefinition definition, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var provider = _materializers.FirstOrDefault(x => x.Name == definition.MaterializerName);
|
||||
var materializers = _materializers();
|
||||
var provider = materializers.FirstOrDefault(x => x.Name == definition.MaterializerName);
|
||||
|
||||
if (provider == null)
|
||||
throw new Exception("Provider not found");
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
using Elsa.Workflows.Core.Models;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
using Elsa.Workflows.Runtime.Models;
|
||||
|
||||
namespace Elsa.Workflows.Runtime.Contracts;
|
||||
|
||||
|
|
@ -8,9 +7,15 @@ namespace Elsa.Workflows.Runtime.Contracts;
|
|||
/// </summary>
|
||||
public interface IWorkflowDefinitionProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the provider.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the workflow definitions.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The workflow definitions.</returns>
|
||||
ValueTask<IEnumerable<WorkflowDefinitionResult>> GetWorkflowDefinitionsAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
|
||||
public record WorkflowDefinitionResult(WorkflowDefinition Definition, Workflow Workflow);
|
||||
}
|
||||
|
|
@ -1,9 +1,19 @@
|
|||
using Elsa.Workflows.Runtime.Contracts;
|
||||
using Elsa.Workflows.Runtime.Services;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="IServiceCollection"/>.
|
||||
/// </summary>
|
||||
public static class DependencyInjectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the <see cref="ClrWorkflowDefinitionProvider"/> to the service collection.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <typeparam name="T">The type of the workflow definition provider.</typeparam>
|
||||
/// <returns>The service collection.</returns>
|
||||
public static IServiceCollection AddWorkflowDefinitionProvider<T>(this IServiceCollection services) where T : class, IWorkflowDefinitionProvider => services.AddSingleton<IWorkflowDefinitionProvider, T>();
|
||||
}
|
||||
|
|
@ -6,7 +6,9 @@ using Elsa.Features.Services;
|
|||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Core.Notifications;
|
||||
using Elsa.Workflows.Core.State;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Management.Notifications;
|
||||
using Elsa.Workflows.Management.Services;
|
||||
using Elsa.Workflows.Runtime.ActivationValidators;
|
||||
using Elsa.Workflows.Runtime.Commands;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
|
|
@ -136,6 +138,10 @@ public class WorkflowRuntimeFeature : FeatureBase
|
|||
.AddSingleton<SynchronousTaskDispatcher>()
|
||||
.AddSingleton<AsynchronousTaskDispatcher>()
|
||||
.AddSingleton<IEventPublisher, EventPublisher>()
|
||||
|
||||
// Lazy services.
|
||||
.AddSingleton<Func<IEnumerable<IWorkflowDefinitionProvider>>>(sp => sp.GetServices<IWorkflowDefinitionProvider>)
|
||||
.AddSingleton<Func<IEnumerable<IWorkflowMaterializer>>>(sp => sp.GetServices<IWorkflowMaterializer>)
|
||||
|
||||
// Memory stores.
|
||||
.AddMemoryStore<WorkflowState, MemoryWorkflowStateStore>()
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Elsa.Workflows.Runtime.HostedServices;
|
|||
/// </summary>
|
||||
public class PopulateWorkflowDefinitionStore : IHostedService
|
||||
{
|
||||
private readonly IEnumerable<IWorkflowDefinitionProvider> _workflowDefinitionProviders;
|
||||
private readonly Func<IEnumerable<IWorkflowDefinitionProvider>> _workflowDefinitionProviders;
|
||||
private readonly ITriggerIndexer _triggerIndexer;
|
||||
private readonly IWorkflowDefinitionStore _workflowDefinitionStore;
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ public class PopulateWorkflowDefinitionStore : IHostedService
|
|||
/// Constructor.
|
||||
/// </summary>
|
||||
public PopulateWorkflowDefinitionStore(
|
||||
IEnumerable<IWorkflowDefinitionProvider> workflowDefinitionProviders,
|
||||
Func<IEnumerable<IWorkflowDefinitionProvider>> workflowDefinitionProviders,
|
||||
ITriggerIndexer triggerIndexer,
|
||||
IWorkflowDefinitionStore workflowDefinitionStore)
|
||||
{
|
||||
|
|
@ -33,7 +33,8 @@ public class PopulateWorkflowDefinitionStore : IHostedService
|
|||
/// <inheritdoc />
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var provider in _workflowDefinitionProviders)
|
||||
var providers = _workflowDefinitionProviders();
|
||||
foreach (var provider in providers)
|
||||
{
|
||||
var results = await provider.GetWorkflowDefinitionsAsync(cancellationToken).AsTask().ToList();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
using Elsa.Workflows.Core.Models;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
|
||||
namespace Elsa.Workflows.Runtime.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a workflow definition and its workflow.
|
||||
/// </summary>
|
||||
/// <param name="Definition">The workflow definition.</param>
|
||||
/// <param name="Workflow">The workflow materialized from its workflow definition.</param>
|
||||
public record WorkflowDefinitionResult(WorkflowDefinition Definition, Workflow Workflow);
|
||||
|
|
@ -4,6 +4,7 @@ using Elsa.Workflows.Management.Entities;
|
|||
using Elsa.Workflows.Management.Materializers;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
using Elsa.Workflows.Runtime.Features;
|
||||
using Elsa.Workflows.Runtime.Models;
|
||||
using Elsa.Workflows.Runtime.Options;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using Elsa.Common.Models;
|
|||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Core.Models;
|
||||
using Elsa.Workflows.Core.Services;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
using Elsa.Workflows.Runtime.Middleware.Activities;
|
||||
using Elsa.Workflows.Runtime.Models;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using Elsa.Common.Models;
|
|||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Core.Models;
|
||||
using Elsa.Workflows.Core.State;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
using Elsa.Workflows.Runtime.Entities;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Elsa.Extensions;
|
|||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Core.Helpers;
|
||||
using Elsa.Workflows.Core.Models;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
using Elsa.Workflows.Runtime.Comparers;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Elsa.Common.Models;
|
||||
using Elsa.Workflows.Core.State;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
using Elsa.Workflows.Sinks.Contracts;
|
||||
using Elsa.Workflows.Sinks.Models;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\bundles\Elsa\Elsa.csproj" />
|
||||
<ProjectReference Include="..\..\..\modules\Elsa.Identity\Elsa.Identity.csproj" />
|
||||
<ProjectReference Include="..\..\..\modules\Elsa.WorkflowProviders.FluentStorage\Elsa.WorkflowProviders.FluentStorage.csproj" />
|
||||
<ProjectReference Include="..\..\..\modules\Elsa.Workflows.Api\Elsa.Workflows.Api.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Workflows\HelloWorld.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
using Elsa.Extensions;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
var services = builder.Services;
|
||||
|
||||
// Add Elsa services.
|
||||
services.AddElsa(elsa => elsa
|
||||
// Configure the workflow runtime.
|
||||
.UseFluentStorageProvider()
|
||||
|
||||
// Expose API endpoints.
|
||||
.UseWorkflowsApi()
|
||||
|
||||
// Configure identity so that we can create a default admin user.
|
||||
.UseIdentity(identity =>
|
||||
{
|
||||
identity.UseAdminUserProvider();
|
||||
identity.TokenOptions = options =>
|
||||
{
|
||||
options.SigningKey = "secret-token-signing-key";
|
||||
options.AccessTokenLifetime = TimeSpan.FromDays(1);
|
||||
};
|
||||
})
|
||||
|
||||
// Use default authentication (JWT).
|
||||
.UseDefaultAuthentication(auth => auth.UseAdminApiKey())
|
||||
);
|
||||
|
||||
// Configure middleware pipeline.
|
||||
var app = builder.Build();
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
app.UseDeveloperExceptionPage();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseWorkflowsApi();
|
||||
app.Run();
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:55253",
|
||||
"sslPort": 44338
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5129",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7230;http://localhost:5129",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"id": "hello-world-v1",
|
||||
"definitionId": "hello-world",
|
||||
"name": "Hello World",
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"version": 1,
|
||||
"root": {
|
||||
"id": "Flowchart1",
|
||||
"type": "Elsa.Flowchart",
|
||||
"activities": [
|
||||
{
|
||||
"id": "WriteLine1",
|
||||
"type": "Elsa.WriteLine",
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Hello World!"
|
||||
},
|
||||
"memoryReference": {
|
||||
"id": "WriteLine1:input-1"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\bundles\Elsa\Elsa.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\Elsa.Testing.Shared\Elsa.Testing.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="HelloWorld.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"definitionId": "11e2b350dae8485498c16d1ce3843829",
|
||||
"name": "Hello World",
|
||||
"root": {
|
||||
"id": "Flowchart1",
|
||||
"type": "Elsa.Flowchart",
|
||||
"activities": [
|
||||
{
|
||||
"id": "WriteLine1",
|
||||
"type": "Elsa.WriteLine",
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Hello World!"
|
||||
},
|
||||
"memoryReference": {
|
||||
"id": "WriteLine1:input-1"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
33
src/samples/console/Elsa.Samples.JsonWorkflows/Program.cs
Normal file
33
src/samples/console/Elsa.Samples.JsonWorkflows/Program.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
using Elsa.Extensions;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.Core.Activities;
|
||||
using Elsa.Workflows.Core.Contracts;
|
||||
using Elsa.Workflows.Core.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
// Setup service container.
|
||||
var services = new ServiceCollection();
|
||||
|
||||
// Add Elsa services.
|
||||
services.AddElsa();
|
||||
|
||||
// Build service container.
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
// Populate registries. This is only necessary for applications that are not using hosted services.
|
||||
await serviceProvider.PopulateRegistriesAsync();
|
||||
|
||||
// Import a workflow from a JSON file.
|
||||
var workflowJson = await File.ReadAllTextAsync("HelloWorld.json");
|
||||
|
||||
// Get a serializer to deserialize the workflow.
|
||||
var serializer = serviceProvider.GetRequiredService<IActivitySerializer>();
|
||||
|
||||
// Deserialize the workflow.
|
||||
var workflow = serializer.Deserialize<Workflow>(workflowJson);
|
||||
|
||||
// Resolve a workflow runner to run the workflow.
|
||||
var workflowRunner = serviceProvider.GetRequiredService<IWorkflowRunner>();
|
||||
|
||||
// Run the workflow.
|
||||
await workflowRunner.RunAsync(workflow);
|
||||
5
src/samples/console/Elsa.Samples.JsonWorkflows/Readme.md
Normal file
5
src/samples/console/Elsa.Samples.JsonWorkflows/Readme.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# JSON workflows
|
||||
|
||||
This sample demonstrates how to load a JSON workflow from a file and then execute it.
|
||||
|
||||
There is another sample in the `aspnet` folder that demonstrates the use of the `JsonWorkflowDefinitionProvider` that provides workflows to the system loaded from JSON files.
|
||||
Loading…
Reference in a new issue