diff --git a/src/designer/elsa-workflows-designer/src/components/designer/variables-editor/variable-editor-dialog.tsx b/src/designer/elsa-workflows-designer/src/components/designer/variables-editor/variable-editor-dialog.tsx index 607979f3b..a874eb2ce 100644 --- a/src/designer/elsa-workflows-designer/src/components/designer/variables-editor/variable-editor-dialog.tsx +++ b/src/designer/elsa-workflows-designer/src/components/designer/variables-editor/variable-editor-dialog.tsx @@ -1,9 +1,8 @@ import {Component, h, Prop, Event, EventEmitter, Method} from "@stencil/core"; -import {DeleteIcon, EditIcon, PublishIcon, UnPublishIcon} from "../../icons/tooling"; -import {DataDrive, DefaultActions, InputDescriptor, Variable, WorkflowDefinitionSummary} from "../../../models"; -import {Filter} from "../../modals/workflow-definition-browser/filter"; +import {DefaultActions, StorageDriverDescriptor, Variable} from "../../../models"; import {FormEntry} from "../../shared/forms/form-entry"; import {isNullOrWhitespace} from "../../../utils"; +import descriptorsStore from '../../../data/descriptors-store'; @Component({ tag: 'elsa-variable-editor-dialog', @@ -32,7 +31,7 @@ export class VariableEditorDialog { const saveAction = DefaultActions.Save(); const actions = [cancelAction, saveAction]; const availableTypes: Array = ['Object', 'String', 'Int32', 'Int64', 'Single', 'Double']; // TODO: Fetch these from backend. - const availableDrives: Array = [null, {id: 'Workflow'}, {id: 'Blob Storage'}, {id: 'FTP'}]; // TODO: Fetch these from backend. + const storageDrivers: Array = descriptorsStore.storageDrivers; return (
@@ -57,12 +56,12 @@ export class VariableEditorDialog { - - + {storageDrivers.map(driver => { + const value = driver.id; + const text = driver.displayName; + const selected = value == variable.storageDriverId; return ; })} @@ -82,13 +81,13 @@ export class VariableEditorDialog { const name = formData.get('variableName') as string; const value = formData.get('variableValue') as string; const type = formData.get('variableType') as string; - const driveId = formData.get('variableStorage') as string; + const driverId = formData.get('variableStorageDriverId') as string; const variable = this.variable; variable.name = name; variable.type = type; variable.value = value; - variable.driveId = isNullOrWhitespace(driveId) ? null : driveId; + variable.storageDriverId = isNullOrWhitespace(driverId) ? null : driverId; this.variableChanged.emit(variable); await this.hide(); diff --git a/src/designer/elsa-workflows-designer/src/components/designer/variables-editor/variables-editor.tsx b/src/designer/elsa-workflows-designer/src/components/designer/variables-editor/variables-editor.tsx index 8b110b923..18eb37fae 100644 --- a/src/designer/elsa-workflows-designer/src/components/designer/variables-editor/variables-editor.tsx +++ b/src/designer/elsa-workflows-designer/src/components/designer/variables-editor/variables-editor.tsx @@ -1,7 +1,8 @@ import {Component, h, Prop, State, Event, EventEmitter, Watch} from "@stencil/core"; import {DeleteIcon, EditIcon} from "../../icons/tooling"; -import {Variable} from "../../../models"; +import {StorageDriverDescriptor, Variable} from "../../../models"; import {isNullOrWhitespace} from "../../../utils"; +import descriptorsStore from "../../../data/descriptors-store"; @Component({ tag: 'elsa-variables-editor', @@ -24,6 +25,7 @@ export class VariablesEditor { render() { const variables = this.variables; + const storageDrivers: Array = descriptorsStore.storageDrivers; return (
@@ -43,7 +45,8 @@ export class VariablesEditor { {variables.map(variable => { - const storageName = isNullOrWhitespace(variable.driveId) ? 'Transient' : variable.driveId; + const storage = storageDrivers.find(x => x.id == variable.storageDriverId); + const storageName = storage?.displayName ?? 'None'; return ( diff --git a/src/designer/elsa-workflows-designer/src/components/designer/workflow-definition-editor/workflow-definition-editor.tsx b/src/designer/elsa-workflows-designer/src/components/designer/workflow-definition-editor/workflow-definition-editor.tsx index 44ee3dc5e..96dd5b979 100644 --- a/src/designer/elsa-workflows-designer/src/components/designer/workflow-definition-editor/workflow-definition-editor.tsx +++ b/src/designer/elsa-workflows-designer/src/components/designer/workflow-definition-editor/workflow-definition-editor.tsx @@ -8,7 +8,7 @@ import { ActivityDescriptor, ActivitySelectedArgs, ContainerSelectedArgs, - GraphUpdatedArgs, + GraphUpdatedArgs, StorageDriverDescriptor, WorkflowDefinition } from '../../../models'; import WorkflowEditorTunnel, {WorkflowDesignerState} from '../state'; @@ -50,8 +50,8 @@ export class WorkflowDefinitionEditor { @Prop() workflowDefinition?: WorkflowDefinition; @Prop({attribute: 'monaco-lib-path'}) public monacoLibPath: string; - @Prop() public activityDescriptors: Array = []; - @Event() public workflowUpdated: EventEmitter + @Prop() activityDescriptors: Array = []; + @Event() workflowUpdated: EventEmitter @State() private workflowDefinitionState: WorkflowDefinition; @State() private selectedActivity?: Activity; diff --git a/src/designer/elsa-workflows-designer/src/components/designer/workflow-definition-editor/workflow-definition-properties-editor.tsx b/src/designer/elsa-workflows-designer/src/components/designer/workflow-definition-editor/workflow-definition-properties-editor.tsx index 03255714b..0c9b152ca 100644 --- a/src/designer/elsa-workflows-designer/src/components/designer/workflow-definition-editor/workflow-definition-properties-editor.tsx +++ b/src/designer/elsa-workflows-designer/src/components/designer/workflow-definition-editor/workflow-definition-properties-editor.tsx @@ -128,6 +128,14 @@ export class WorkflowDefinitionPropertiesEditor { } } + // const dataDrivesTabModel: TabModel = { + // name: 'dataDrives', + // tab: { + // displayText: 'Data Drives', + // content: () => this.renderDataDrivesTab() + // } + // } + model.tabModels = [propertiesTabModel, variablesTabModel]; const args: WorkflowPropertiesEditorDisplayingArgs = {model}; @@ -149,10 +157,18 @@ export class WorkflowDefinitionPropertiesEditor { const variables: Array = this.workflowDefinition?.variables ?? []; return
- this.onVariablesUpdated(e)} /> + this.onVariablesUpdated(e)}/>
}; + // private renderDataDrivesTab = () => { + // const dataDrives: Array = this.workflowDefinition?.dataDrives ?? []; + // + // return
+ // this.onDataDrivesUpdated(e)}/> + //
+ // }; + private onSelectedTabIndexChanged = (e: CustomEvent) => this.selectedTabIndex = e.detail.selectedTabIndex; private onPropertyEditorChanged = (apply: (w: WorkflowDefinition) => void) => { @@ -164,7 +180,7 @@ export class WorkflowDefinitionPropertiesEditor { private onVariablesUpdated = async (e: CustomEvent>) => { const workflowDefinition = this.workflowDefinition; - if(!workflowDefinition) + if (!workflowDefinition) return; const variables = e.detail; @@ -172,6 +188,18 @@ export class WorkflowDefinitionPropertiesEditor { this.workflowPropsUpdated.emit({workflowDefinition}); await this.createModel(); } + + // private onDataDrivesUpdated = async (e: CustomEvent>) => { + // const workflowDefinition = this.workflowDefinition; + // + // if (!workflowDefinition) + // return; + // + // const dataDrives = e.detail; + // workflowDefinition.dataDrives = dataDrives; + // this.workflowPropsUpdated.emit({workflowDefinition}); + // await this.createModel(); + // } } WorkflowEditorTunnel.injectProps(WorkflowDefinitionPropertiesEditor, ['activityDescriptors']); diff --git a/src/designer/elsa-workflows-designer/src/components/shell/studio/studio.tsx b/src/designer/elsa-workflows-designer/src/components/shell/studio/studio.tsx index e2f94c552..04e22c28a 100644 --- a/src/designer/elsa-workflows-designer/src/components/shell/studio/studio.tsx +++ b/src/designer/elsa-workflows-designer/src/components/shell/studio/studio.tsx @@ -15,6 +15,7 @@ import {ExportWorkflowRequest, ImportWorkflowRequest} from "../../../services/ap import {WorkflowDefinitionManager} from "../../../services/workflow-definition-manager"; import {WorkflowDefinitionUpdatedArgs} from "../../designer/workflow-definition-editor/models"; import {Flowchart} from "../../activities/flowchart/models"; +import descriptorsStore from '../../../data/descriptors-store'; @Component({ tag: 'elsa-studio' @@ -193,6 +194,10 @@ export class Studio { this.elsaClient = await elsaClientProvider.getElsaClient(); this.activityDescriptors = await this.elsaClient.descriptors.activities.list(); + const storageDrivers = await this.elsaClient.descriptors.storageDrivers.list(); + + descriptorsStore.activityDescriptors = this.activityDescriptors; + descriptorsStore.storageDrivers = storageDrivers; this.workflowManagerElement = this.el.getElementsByTagName('elsa-workflow-manager')[0] as HTMLElsaWorkflowManagerElement; diff --git a/src/designer/elsa-workflows-designer/src/data/descriptors-store.ts b/src/designer/elsa-workflows-designer/src/data/descriptors-store.ts new file mode 100644 index 000000000..25c138eaa --- /dev/null +++ b/src/designer/elsa-workflows-designer/src/data/descriptors-store.ts @@ -0,0 +1,8 @@ +import { createStore } from '@stencil/store'; + +const { state, onChange } = createStore({ + activityDescriptors: [], + storageDrivers: [] +}); + +export default state; diff --git a/src/designer/elsa-workflows-designer/src/models/api.ts b/src/designer/elsa-workflows-designer/src/models/api.ts index 3adbf80a6..6fd2a8c21 100644 --- a/src/designer/elsa-workflows-designer/src/models/api.ts +++ b/src/designer/elsa-workflows-designer/src/models/api.ts @@ -125,3 +125,8 @@ export interface IntellisenseContext { activityTypeName: string; propertyName: string; } + +export interface StorageDriverDescriptor { + id: string; + displayName: string; +} diff --git a/src/designer/elsa-workflows-designer/src/models/core.ts b/src/designer/elsa-workflows-designer/src/models/core.ts index 03ca7ee5e..4dff5988f 100644 --- a/src/designer/elsa-workflows-designer/src/models/core.ts +++ b/src/designer/elsa-workflows-designer/src/models/core.ts @@ -25,7 +25,7 @@ export interface Variable { name: string; type: string; value?: any; - driveId?: string; + storageDriverId?: string; } export interface ActivityInput { @@ -98,10 +98,6 @@ export interface WorkflowExecutionLogRecord { eventName: string; } -export interface DataDrive { - id: string; -} - export enum SyntaxNames { Literal = 'Literal', JavaScript = 'JavaScript', diff --git a/src/designer/elsa-workflows-designer/src/services/api-client/descriptors-api.ts b/src/designer/elsa-workflows-designer/src/services/api-client/descriptors-api.ts index b7a32d031..e61bc50e3 100644 --- a/src/designer/elsa-workflows-designer/src/services/api-client/descriptors-api.ts +++ b/src/designer/elsa-workflows-designer/src/services/api-client/descriptors-api.ts @@ -1,17 +1,21 @@ import {ActivityDescriptorsApi, ActivityDescriptorsApiImpl} from "./activity-descriptors-api"; import {AxiosInstance} from "axios"; +import {StorageDriversApi, StorageDriversApiImpl} from "./storage-drivers-api"; export interface DescriptorsApi { activities: ActivityDescriptorsApi; + storageDrivers: StorageDriversApi; } export class DescriptorsApiImpl implements DescriptorsApi { httpClient: AxiosInstance; activities: ActivityDescriptorsApi; + storageDrivers: StorageDriversApi; constructor(httpClient: AxiosInstance) { this.httpClient = httpClient; this.activities = new ActivityDescriptorsApiImpl(httpClient); + this.storageDrivers = new StorageDriversApiImpl(httpClient); } } diff --git a/src/designer/elsa-workflows-designer/src/services/api-client/storage-drivers-api.ts b/src/designer/elsa-workflows-designer/src/services/api-client/storage-drivers-api.ts new file mode 100644 index 000000000..f0711d02f --- /dev/null +++ b/src/designer/elsa-workflows-designer/src/services/api-client/storage-drivers-api.ts @@ -0,0 +1,23 @@ +import {ActivityDescriptor, StorageDriverDescriptor} from "../../models"; +import {AxiosInstance} from "axios"; + +export interface StorageDriversApi { + list(): Promise>; +} + +export interface StorageDriversResponse { + items: Array; +} + +export class StorageDriversApiImpl implements StorageDriversApi { + private httpClient: AxiosInstance; + + constructor(httpClient: AxiosInstance) { + this.httpClient = httpClient; + } + + async list(): Promise> { + const response = await this.httpClient.get('descriptors/storage-drivers'); + return response.data.items; + } +} diff --git a/src/modules/Elsa.Workflows.Api/ControllerNames.cs b/src/modules/Elsa.Workflows.Api/ControllerNames.cs index 9f2be349d..ddf38f960 100644 --- a/src/modules/Elsa.Workflows.Api/ControllerNames.cs +++ b/src/modules/Elsa.Workflows.Api/ControllerNames.cs @@ -9,4 +9,5 @@ public class ControllerNames public const string Labels = "Labels"; public const string WorkflowDefinitionLabels = "WorkflowDefinitionLabels"; public const string WorkflowJournal = "WorkflowJournal"; + public const string StorageDrivers = "StorageDrivers"; } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/StorageDrivers/List.cs b/src/modules/Elsa.Workflows.Api/Endpoints/StorageDrivers/List.cs new file mode 100644 index 000000000..574b4b24f --- /dev/null +++ b/src/modules/Elsa.Workflows.Api/Endpoints/StorageDrivers/List.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Linq; +using Elsa.AspNetCore.Attributes; +using Elsa.AspNetCore.Models; +using Elsa.Workflows.Core.Serialization; +using Elsa.Workflows.Core.Services; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +// ReSharper disable NotAccessedPositionalProperty.Global + +namespace Elsa.Workflows.Api.Endpoints.StorageDrivers; + +[Area(AreaNames.Elsa)] +[ApiEndpoint(ControllerNames.StorageDrivers, "List")] +[ProducesResponseType(typeof(StorageDrivers), StatusCodes.Status200OK, "application/json")] +public class List : Controller +{ + private readonly IStorageDriverManager _registry; + private readonly WorkflowSerializerOptionsProvider _serializerOptionsProvider; + + public List(IStorageDriverManager registry, WorkflowSerializerOptionsProvider serializerOptionsProvider) + { + _registry = registry; + _serializerOptionsProvider = serializerOptionsProvider; + } + + [HttpGet] + public IActionResult Handle() + { + var drivers = _registry.List(); + var descriptors = drivers.Select(x => new StorageDriverDescriptor(x.Id, x.DisplayName)).ToList(); + var model = new StorageDrivers(descriptors); + var serializerOptions = _serializerOptionsProvider.CreateApiOptions(); + + return Json(model, serializerOptions); + } +} + +public record StorageDrivers(ICollection Items) : ListModel(Items); + +public record StorageDriverDescriptor(string Id, string DisplayName); \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Api/Extensions/EndpointRouteBuilderExtensions.cs b/src/modules/Elsa.Workflows.Api/Extensions/EndpointRouteBuilderExtensions.cs index 861c71120..9aea4077e 100644 --- a/src/modules/Elsa.Workflows.Api/Extensions/EndpointRouteBuilderExtensions.cs +++ b/src/modules/Elsa.Workflows.Api/Extensions/EndpointRouteBuilderExtensions.cs @@ -43,6 +43,9 @@ public static class EndpointRouteBuilderExtensions // Workflow Journal. Map("WorkflowJournal.Get", "workflow-instances/{id}/journal", new { Controller = ControllerNames.WorkflowJournal, Action = "Get" }); + + // Storage Drivers. + Map("StorageDrivers.List", "descriptors/storage-drivers", new { Controller = ControllerNames.StorageDrivers, Action = "List" }); return endpoints; } diff --git a/src/modules/Elsa.Workflows.Api/Mappers/VariableDefinitionMapper.cs b/src/modules/Elsa.Workflows.Api/Mappers/VariableDefinitionMapper.cs index 01be5a5ad..2e4e4b0a4 100644 --- a/src/modules/Elsa.Workflows.Api/Mappers/VariableDefinitionMapper.cs +++ b/src/modules/Elsa.Workflows.Api/Mappers/VariableDefinitionMapper.cs @@ -28,7 +28,7 @@ public class VariableDefinitionMapper variable.Name = source.Name; variable.Value = source.Value.ConvertTo(type); - variable.DriveId = source.DriveId; + variable.StorageDriverId = source.StorageDriverId; return variable; } @@ -46,11 +46,11 @@ public class VariableDefinitionMapper var value = source.Value; var valueType = source.Value?.GetType() ?? (variableType.IsConstructedGenericType ? variableType.GetGenericArguments().FirstOrDefault() ?? typeof(object) : typeof(object)); var valueTypeAlias = _wellKnownTypeRegistry.GetAliasOrDefault(valueType); - var driveId = source.DriveId; + var driverId = source.StorageDriverId; var serializedValue = value.Format(); - return new VariableDefinition(source.Name, valueTypeAlias, serializedValue, driveId); + return new VariableDefinition(source.Name, valueTypeAlias, serializedValue, driverId); } public IEnumerable Map(IEnumerable? source) => source?.Select(Map) ?? Enumerable.Empty(); diff --git a/src/modules/Elsa.Workflows.Api/Models/VariableDefinition.cs b/src/modules/Elsa.Workflows.Api/Models/VariableDefinition.cs index 99e8ab529..33403bc84 100644 --- a/src/modules/Elsa.Workflows.Api/Models/VariableDefinition.cs +++ b/src/modules/Elsa.Workflows.Api/Models/VariableDefinition.cs @@ -1,3 +1,3 @@ namespace Elsa.Workflows.Api.Models; -public record VariableDefinition(string Name, string Type, string? Value, string? DriveId); \ No newline at end of file +public record VariableDefinition(string Name, string Type, string? Value, string? StorageDriverId); \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Builders/WorkflowDefinitionBuilder.cs b/src/modules/Elsa.Workflows.Core/Builders/WorkflowDefinitionBuilder.cs index fb052fe0f..0a6505333 100644 --- a/src/modules/Elsa.Workflows.Core/Builders/WorkflowDefinitionBuilder.cs +++ b/src/modules/Elsa.Workflows.Core/Builders/WorkflowDefinitionBuilder.cs @@ -39,28 +39,28 @@ public class WorkflowDefinitionBuilder : IWorkflowDefinitionBuilder return this; } - public Variable WithVariable(string? driveId = default) + public Variable WithVariable(string? storageDriverId = default) { var variable = new Variable() { - DriveId = driveId + StorageDriverId = storageDriverId }; Variables.Add(variable); return variable; } - public Variable WithVariable(string name, T value, string? driveId = default) + public Variable WithVariable(string name, T value, string? storageDriverId = default) { var variable = value != null ? new Variable(name, value) : new Variable(name); - variable.DriveId = driveId; + variable.StorageDriverId = storageDriverId; Variables.Add(variable); return variable; } - public Variable WithVariable(T value, string? driveId = default) + public Variable WithVariable(T value, string? storageDriverId = default) { var variable = value != null ? new Variable(value) : new Variable(); - variable.DriveId = driveId; + variable.StorageDriverId = storageDriverId; Variables.Add(variable); return variable; } diff --git a/src/modules/Elsa.Workflows.Core/Extensions/DependencyInjectionExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/DependencyInjectionExtensions.cs index 972583176..8e6ce07d5 100644 --- a/src/modules/Elsa.Workflows.Core/Extensions/DependencyInjectionExtensions.cs +++ b/src/modules/Elsa.Workflows.Core/Extensions/DependencyInjectionExtensions.cs @@ -13,8 +13,8 @@ public static class DependencyInjectionExtensions return configuration; } - public static IServiceCollection AddDataDrive(this IServiceCollection services) where T : class, IDataDrive + public static IServiceCollection AddDataDrive(this IServiceCollection services) where T : class, IStorageDriver { - return services.AddSingleton(); + return services.AddSingleton(); } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Extensions/VariableExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/VariableExtensions.cs index e82eff7b2..50cba886a 100644 --- a/src/modules/Elsa.Workflows.Core/Extensions/VariableExtensions.cs +++ b/src/modules/Elsa.Workflows.Core/Extensions/VariableExtensions.cs @@ -4,12 +4,12 @@ namespace Elsa.Workflows.Core; public static class VariableExtensions { - public static Variable WithWorkflowDrive(this Variable variable) => variable.WithDrive(DataDriveNames.Workflow); - public static Variable WithMemoryDrive(this Variable variable) => variable.WithDrive(DataDriveNames.Memory); + public static Variable WithWorkflowDrive(this Variable variable) => variable.WithStorage(StorageDriverNames.Workflow); + public static Variable WithMemoryDrive(this Variable variable) => variable.WithStorage(StorageDriverNames.Memory); - public static Variable WithDrive(this Variable variable, string driveId) + public static Variable WithStorage(this Variable variable, string storageDriverId) { - variable.DriveId = driveId; + variable.StorageDriverId = storageDriverId; return variable; } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Features/WorkflowsFeature.cs b/src/modules/Elsa.Workflows.Core/Features/WorkflowsFeature.cs index a76520108..9cf2212da 100644 --- a/src/modules/Elsa.Workflows.Core/Features/WorkflowsFeature.cs +++ b/src/modules/Elsa.Workflows.Core/Features/WorkflowsFeature.cs @@ -81,9 +81,9 @@ public class WorkflowsFeature : FeatureBase .AddSingleton(StandardOutStreamProvider) // Data drives. - .AddSingleton() - .AddDataDrive() - .AddDataDrive() + .AddSingleton() + .AddDataDrive() + .AddDataDrive() // Logging .AddLogging(); diff --git a/src/modules/Elsa.Workflows.Core/Implementations/DataDriveManager.cs b/src/modules/Elsa.Workflows.Core/Implementations/DataDriveManager.cs deleted file mode 100644 index 3794773c0..000000000 --- a/src/modules/Elsa.Workflows.Core/Implementations/DataDriveManager.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Elsa.Workflows.Core.Services; - -namespace Elsa.Workflows.Core.Implementations; - -public class DataDriveManager : IDataDriveManager -{ - private readonly IEnumerable _drives; - public DataDriveManager(IEnumerable drives) => _drives = drives; - public IDataDrive? GetDriveById(string id) => _drives.FirstOrDefault(x => x.Id == id); -} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Implementations/MemoryDataDrive.cs b/src/modules/Elsa.Workflows.Core/Implementations/MemoryStorageDriver.cs similarity index 81% rename from src/modules/Elsa.Workflows.Core/Implementations/MemoryDataDrive.cs rename to src/modules/Elsa.Workflows.Core/Implementations/MemoryStorageDriver.cs index d6108036e..4bcd347da 100644 --- a/src/modules/Elsa.Workflows.Core/Implementations/MemoryDataDrive.cs +++ b/src/modules/Elsa.Workflows.Core/Implementations/MemoryStorageDriver.cs @@ -2,10 +2,11 @@ using Elsa.Workflows.Core.Services; namespace Elsa.Workflows.Core.Implementations; -public class MemoryDataDrive : IDataDrive +public class MemoryStorageDriver : IStorageDriver { private readonly IDictionary _dictionary = new Dictionary(); - public string Id => DataDriveNames.Memory; + public string Id => StorageDriverNames.Memory; + public string DisplayName => StorageDriverNames.Memory; public ValueTask WriteAsync(string id, object value, DataDriveContext context) { diff --git a/src/modules/Elsa.Workflows.Core/Implementations/StorageDriverManager.cs b/src/modules/Elsa.Workflows.Core/Implementations/StorageDriverManager.cs new file mode 100644 index 000000000..7017e16a8 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/Implementations/StorageDriverManager.cs @@ -0,0 +1,11 @@ +using Elsa.Workflows.Core.Services; + +namespace Elsa.Workflows.Core.Implementations; + +public class StorageDriverManager : IStorageDriverManager +{ + private readonly IEnumerable _drivers; + public StorageDriverManager(IEnumerable drivers) => _drivers = drivers; + public IStorageDriver? GetDriveById(string id) => _drivers.FirstOrDefault(x => x.Id == id); + public IEnumerable List() => _drivers; +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Implementations/WorkflowStateSerializer.cs b/src/modules/Elsa.Workflows.Core/Implementations/WorkflowStateSerializer.cs index 1a13fb4da..07c6803b7 100644 --- a/src/modules/Elsa.Workflows.Core/Implementations/WorkflowStateSerializer.cs +++ b/src/modules/Elsa.Workflows.Core/Implementations/WorkflowStateSerializer.cs @@ -168,8 +168,8 @@ public class WorkflowStateSerializer : IWorkflowStateSerializer var workflow = workflowExecutionContext.Workflow; state.PersistentVariables = workflow.Variables - .Where(x => x.DriveId != null) - .Select(x => new PersistentVariableState(x.Name, x.DriveId!)) + .Where(x => x.StorageDriverId != null) + .Select(x => new PersistentVariableState(x.Name, x.StorageDriverId!)) .ToList(); } diff --git a/src/modules/Elsa.Workflows.Core/Implementations/WorkflowStateDataDrive.cs b/src/modules/Elsa.Workflows.Core/Implementations/WorkflowStateStorageDriver.cs similarity index 87% rename from src/modules/Elsa.Workflows.Core/Implementations/WorkflowStateDataDrive.cs rename to src/modules/Elsa.Workflows.Core/Implementations/WorkflowStateStorageDriver.cs index 28c69b58b..0713aaddd 100644 --- a/src/modules/Elsa.Workflows.Core/Implementations/WorkflowStateDataDrive.cs +++ b/src/modules/Elsa.Workflows.Core/Implementations/WorkflowStateStorageDriver.cs @@ -5,10 +5,11 @@ namespace Elsa.Workflows.Core.Implementations; /// /// A data drive that stores objects in the workflow state itself. /// -public class WorkflowStateDataDrive : IDataDrive +public class WorkflowStateStorageDriver : IStorageDriver { public const string VariablesDictionaryStateKey = "PersistentVariablesDictionary"; - public string Id => DataDriveNames.Workflow; + public string Id => StorageDriverNames.Workflow; + public string DisplayName => StorageDriverNames.Workflow; public ValueTask WriteAsync(string id, object value, DataDriveContext context) { diff --git a/src/modules/Elsa.Workflows.Core/Models/DataDriveDefinition.cs b/src/modules/Elsa.Workflows.Core/Models/DataDriveDefinition.cs deleted file mode 100644 index 42b656c72..000000000 --- a/src/modules/Elsa.Workflows.Core/Models/DataDriveDefinition.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Elsa.Workflows.Core.Models; - -/// -/// Represents a workflow-specific memory drive definition that is configured with a specific storage driver. -/// -public class DataDriveDefinition -{ - [JsonConstructor] - public DataDriveDefinition() - { - } - - public DataDriveDefinition(string id, string driverId, string displayName) - { - Id = id; - DriverId = driverId; - DisplayName = displayName; - } - - public string Id { get; set; } = default!; - public string DriverId { get; set; } = default!; - public string DisplayName { get; set; } = default!; -} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Models/Variable.cs b/src/modules/Elsa.Workflows.Core/Models/Variable.cs index 0ee288153..e68dbe033 100644 --- a/src/modules/Elsa.Workflows.Core/Models/Variable.cs +++ b/src/modules/Elsa.Workflows.Core/Models/Variable.cs @@ -29,9 +29,9 @@ public class Variable : MemoryReference public object? Value { get; set; } /// - /// When specified, the variable's value will be stored on the specified data drive. + /// When specified, the variable's value will be stored on the specified . /// - public string? DriveId { get; set; } + public string? StorageDriverId { get; set; } public override MemoryBlock Declare() => new(Value); } diff --git a/src/modules/Elsa.Workflows.Core/Models/Workflow.cs b/src/modules/Elsa.Workflows.Core/Models/Workflow.cs index 4950a7db4..3e57dcfda 100644 --- a/src/modules/Elsa.Workflows.Core/Models/Workflow.cs +++ b/src/modules/Elsa.Workflows.Core/Models/Workflow.cs @@ -47,15 +47,6 @@ public class Workflow : Composite, ICloneable public WorkflowPublication Publication { get; set; } public WorkflowMetadata WorkflowMetadata { get; set; } public ICollection Variables { get; init; } - - /// - /// A list of storage drive definitions that can be used by variables to persist their values. - /// - /// - /// Depending on the drive's driver capabilities, only serializable variables can be persisted. - /// - public ICollection Drives { get; set; } = new List(); - public Workflow Clone() => (Workflow)((ICloneable)this).Clone(); object ICloneable.Clone() => MemberwiseClone(); } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Serialization/Converters/VariableConverter.cs b/src/modules/Elsa.Workflows.Core/Serialization/Converters/VariableConverter.cs index a86031f34..d4e25eaa3 100644 --- a/src/modules/Elsa.Workflows.Core/Serialization/Converters/VariableConverter.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Converters/VariableConverter.cs @@ -42,7 +42,7 @@ public class VariableConverter : JsonConverter variable.Name = source.Name; variable.Value = source.Value.ConvertTo(type); - variable.DriveId = source.DriveId; + variable.StorageDriverId = source.StorageDriverId; return variable; } @@ -53,10 +53,10 @@ public class VariableConverter : JsonConverter var value = source.Value; var valueType = source.Value?.GetType() ?? (variableType.IsConstructedGenericType ? variableType.GetGenericArguments().FirstOrDefault() ?? typeof(object) : typeof(object)); var valueTypeAlias = _wellKnownTypeRegistry.GetAliasOrDefault(valueType); - var driveId = source.DriveId; + var driverId = source.StorageDriverId; var serializedValue = value.Format(); - return new VariableModel(source.Name, valueTypeAlias, serializedValue, driveId); + return new VariableModel(source.Name, valueTypeAlias, serializedValue, driverId); } public class VariableModel @@ -66,17 +66,17 @@ public class VariableConverter : JsonConverter { } - public VariableModel(string name, string typeName, string? value, string? driveId) + public VariableModel(string name, string typeName, string? value, string? storageDriverId) { Name = name; TypeName = typeName; Value = value; - DriveId = driveId; + StorageDriverId = storageDriverId; } public string Name { get; set; } = default!; public string TypeName { get; set; } = default!; public string? Value { get; set; } - public string? DriveId { get; set; } + public string? StorageDriverId { get; set; } } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Services/IDataDriveManager.cs b/src/modules/Elsa.Workflows.Core/Services/IDataDriveManager.cs deleted file mode 100644 index b1dab05fd..000000000 --- a/src/modules/Elsa.Workflows.Core/Services/IDataDriveManager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Elsa.Workflows.Core.Services; - -public interface IDataDriveManager -{ - IDataDrive? GetDriveById(string id); -} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Services/IDataDrive.cs b/src/modules/Elsa.Workflows.Core/Services/IStorageDriver.cs similarity index 88% rename from src/modules/Elsa.Workflows.Core/Services/IDataDrive.cs rename to src/modules/Elsa.Workflows.Core/Services/IStorageDriver.cs index abd81a31a..be25bd094 100644 --- a/src/modules/Elsa.Workflows.Core/Services/IDataDrive.cs +++ b/src/modules/Elsa.Workflows.Core/Services/IStorageDriver.cs @@ -5,9 +5,10 @@ namespace Elsa.Workflows.Core.Services; /// /// Represents a storage driver for workflows to write state to. /// -public interface IDataDrive +public interface IStorageDriver { string Id { get; } + string DisplayName { get; } ValueTask WriteAsync(string id, object value, DataDriveContext context); ValueTask ReadAsync(string id, DataDriveContext context); ValueTask DeleteAsync(string id, DataDriveContext context); diff --git a/src/modules/Elsa.Workflows.Core/Services/IStorageDriverManager.cs b/src/modules/Elsa.Workflows.Core/Services/IStorageDriverManager.cs new file mode 100644 index 000000000..60a2488aa --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/Services/IStorageDriverManager.cs @@ -0,0 +1,7 @@ +namespace Elsa.Workflows.Core.Services; + +public interface IStorageDriverManager +{ + IStorageDriver? GetDriveById(string id); + IEnumerable List(); +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Services/IWorkflowDefinitionBuilder.cs b/src/modules/Elsa.Workflows.Core/Services/IWorkflowDefinitionBuilder.cs index 49958874b..fe4f8a2e5 100644 --- a/src/modules/Elsa.Workflows.Core/Services/IWorkflowDefinitionBuilder.cs +++ b/src/modules/Elsa.Workflows.Core/Services/IWorkflowDefinitionBuilder.cs @@ -12,9 +12,9 @@ public interface IWorkflowDefinitionBuilder IWorkflowDefinitionBuilder WithDefinitionId(string definitionId); IWorkflowDefinitionBuilder WithVersion(int version); IWorkflowDefinitionBuilder WithRoot(IActivity root); - Variable WithVariable(string? driveId = default); - Variable WithVariable(string name, T value, string? driveId = default); - Variable WithVariable(T value, string? driveId = default); + Variable WithVariable(string? storageDriverId = default); + Variable WithVariable(string name, T value, string? storageDriverId = default); + Variable WithVariable(T value, string? storageDriverId = default); IWorkflowDefinitionBuilder WithVariable(Variable variable); IWorkflowDefinitionBuilder WithVariables(params Variable[] variables); IWorkflowDefinitionBuilder WithApplicationProperty(string name, object value); diff --git a/src/modules/Elsa.Workflows.Core/State/PersistentVariableState.cs b/src/modules/Elsa.Workflows.Core/State/PersistentVariableState.cs index ce6bb8502..4d4d7f733 100644 --- a/src/modules/Elsa.Workflows.Core/State/PersistentVariableState.cs +++ b/src/modules/Elsa.Workflows.Core/State/PersistentVariableState.cs @@ -9,12 +9,12 @@ public class PersistentVariableState { } - public PersistentVariableState(string name, string driveId) + public PersistentVariableState(string name, string storageDriverId) { Name = name; - DriveId = driveId; + StorageDriverId = storageDriverId; } public string Name { get; set; } = default!; - public string DriveId { get; set; } = default!; + public string StorageDriverId { get; set; } = default!; } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/DataDriveNames.cs b/src/modules/Elsa.Workflows.Core/StorageDriverNames.cs similarity index 76% rename from src/modules/Elsa.Workflows.Core/DataDriveNames.cs rename to src/modules/Elsa.Workflows.Core/StorageDriverNames.cs index 8f8034a41..46176eb0a 100644 --- a/src/modules/Elsa.Workflows.Core/DataDriveNames.cs +++ b/src/modules/Elsa.Workflows.Core/StorageDriverNames.cs @@ -1,6 +1,6 @@ namespace Elsa.Workflows.Core; -public static class DataDriveNames +public static class StorageDriverNames { public const string Workflow = "Workflow"; public const string Memory = "Memory"; diff --git a/src/modules/Elsa.Workflows.Persistence.EntityFrameworkCore/Handlers/WorkflowDefinitionSerializer.cs b/src/modules/Elsa.Workflows.Persistence.EntityFrameworkCore/Handlers/WorkflowDefinitionSerializer.cs index dc6371b63..6b8fd3045 100644 --- a/src/modules/Elsa.Workflows.Persistence.EntityFrameworkCore/Handlers/WorkflowDefinitionSerializer.cs +++ b/src/modules/Elsa.Workflows.Persistence.EntityFrameworkCore/Handlers/WorkflowDefinitionSerializer.cs @@ -14,7 +14,7 @@ public class WorkflowDefinitionSerializer : IEntitySerializer(json, options)!; } - + entity.Variables = data.Variables; entity.Tags = data.Tags; entity.Metadata = data.Metadata; entity.ApplicationProperties = data.ApplicationProperties; } - + // Can't use records when using System.Text.Json serialization and reference handling. Hence, using a class with default constructor. private class WorkflowDefinitionState { @@ -55,14 +55,18 @@ public class WorkflowDefinitionSerializer : IEntitySerializer variables, ICollection tags, IDictionary metadata, IDictionary applicationProperties) + public WorkflowDefinitionState( + ICollection variables, + ICollection tags, + IDictionary metadata, + IDictionary applicationProperties) { Variables = variables; Tags = tags; Metadata = metadata; ApplicationProperties = applicationProperties; } - + public ICollection Variables { get; set; } = new List(); public ICollection Tags { get; set; } = new List(); public IDictionary Metadata { get; set; } = new Dictionary(); diff --git a/src/modules/Elsa.Workflows.Runtime/Middleware/PersistWorkflowInstanceMiddleware.cs b/src/modules/Elsa.Workflows.Runtime/Middleware/PersistWorkflowInstanceMiddleware.cs index 58122248c..7f5de318e 100644 --- a/src/modules/Elsa.Workflows.Runtime/Middleware/PersistWorkflowInstanceMiddleware.cs +++ b/src/modules/Elsa.Workflows.Runtime/Middleware/PersistWorkflowInstanceMiddleware.cs @@ -25,7 +25,7 @@ public class PersistWorkflowInstanceMiddleware : WorkflowExecutionMiddleware private readonly IRequestSender _requestSender; private readonly IEventPublisher _eventPublisher; private readonly IWorkflowStateSerializer _workflowStateSerializer; - private readonly IDataDriveManager _dataDriveManager; + private readonly IStorageDriverManager _storageDriverManager; private readonly IBookmarkManager _bookmarkManager; private readonly IIdentityGenerator _identityGenerator; private readonly ISystemClock _clock; @@ -38,7 +38,7 @@ public class PersistWorkflowInstanceMiddleware : WorkflowExecutionMiddleware IEventPublisher eventPublisher, IBookmarkManager bookmarkManager, IWorkflowStateSerializer workflowStateSerializer, - IDataDriveManager dataDriveManager, + IStorageDriverManager storageDriverManager, IIdentityGenerator identityGenerator, ISystemClock clock) : base(next) { @@ -48,7 +48,7 @@ public class PersistWorkflowInstanceMiddleware : WorkflowExecutionMiddleware _eventPublisher = eventPublisher; _bookmarkManager = bookmarkManager; _workflowStateSerializer = workflowStateSerializer; - _dataDriveManager = dataDriveManager; + _storageDriverManager = storageDriverManager; _identityGenerator = identityGenerator; _clock = clock; } @@ -104,7 +104,7 @@ public class PersistWorkflowInstanceMiddleware : WorkflowExecutionMiddleware foreach (var variableState in workflowInstance.WorkflowState.PersistentVariables) { - var drive = _dataDriveManager.GetDriveById(variableState.DriveId); + var drive = _storageDriverManager.GetDriveById(variableState.StorageDriverId); if (drive == null) continue; var id = $"{context.Id}:{variableState.Name}"; var value = await drive.ReadAsync(id, dataDriveContext); @@ -150,7 +150,7 @@ public class PersistWorkflowInstanceMiddleware : WorkflowExecutionMiddleware foreach (var variableState in workflowState.PersistentVariables) { - var drive = _dataDriveManager.GetDriveById(variableState.DriveId); + var drive = _storageDriverManager.GetDriveById(variableState.StorageDriverId); if (drive == null) continue; if (!context.MemoryRegister.TryGetBlock(variableState.Name, out var block)) continue; if (block.Value == null) continue; diff --git a/test/Elsa.IntegrationTests/Scenarios/PersistentVariables/Tests.cs b/test/Elsa.IntegrationTests/Scenarios/PersistentVariables/Tests.cs index 7ad8196c2..e84c0a51f 100644 --- a/test/Elsa.IntegrationTests/Scenarios/PersistentVariables/Tests.cs +++ b/test/Elsa.IntegrationTests/Scenarios/PersistentVariables/Tests.cs @@ -76,7 +76,7 @@ public class WorkflowInstancePersistenceTests workflowState = result.WorkflowState; // Assert that the expected variable is persisted. - var persistentVariablesDictionary = (IDictionary)workflowState.Properties[WorkflowStateDataDrive.VariablesDictionaryStateKey]; + var persistentVariablesDictionary = (IDictionary)workflowState.Properties[WorkflowStateStorageDriver.VariablesDictionaryStateKey]; var stateId = $"{workflowState.Id}:{variable.Name}"; var persistedValue = persistentVariablesDictionary[stateId]; var expectedValue = languages[currentIndex];