Restore workflow journal (#3582)
* Refactor identity options * Delete templates Will be created as part of a separate repo. * Little cleanup * Increase token lifetime for reference server app * Cleanup workflow definition editor plugin * Fix workflow instance viewer component * Update identity generator to use hyphen format * Improve workflow and activity execution pipeline registration API * Fix workflow journal * Update backend * Prevent unnecessary re-render * Restore autolayout function
This commit is contained in:
parent
0cadc9a712
commit
06e62b3bcc
9
Elsa.sln
9
Elsa.sln
|
|
@ -138,10 +138,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docker", "docker", "{986E54
|
|||
docker\Dockerfile = docker\Dockerfile
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "templates", "templates", "{26A89A29-FE67-4174-9809-7C63AAD6BD5D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElsaWorkflowsApp", "templates\elsa-workflow-app\ElsaWorkflowsApp\ElsaWorkflowsApp.csproj", "{15B2ABCB-7460-4E59-A991-10AD9190B1C9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.WorkflowSinks", "src\samples\aspnet\Elsa.Samples.WorkflowSinks\Elsa.Samples.WorkflowSinks.csproj", "{536EFB55-EEA1-4761-9A72-D4EA84A6DB70}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.EntityFrameworkCore.PostgreSql", "src\modules\Elsa.EntityFrameworkCore.PostgreSql\Elsa.EntityFrameworkCore.PostgreSql.csproj", "{AA84DBF7-F70F-4673-8DC2-6EFBE3E9BF83}"
|
||||
|
|
@ -352,10 +348,6 @@ Global
|
|||
{34FBB2D3-4E2B-4411-95F2-C6B56899826A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{34FBB2D3-4E2B-4411-95F2-C6B56899826A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{34FBB2D3-4E2B-4411-95F2-C6B56899826A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{15B2ABCB-7460-4E59-A991-10AD9190B1C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{15B2ABCB-7460-4E59-A991-10AD9190B1C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{15B2ABCB-7460-4E59-A991-10AD9190B1C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{15B2ABCB-7460-4E59-A991-10AD9190B1C9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{536EFB55-EEA1-4761-9A72-D4EA84A6DB70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{536EFB55-EEA1-4761-9A72-D4EA84A6DB70}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{536EFB55-EEA1-4761-9A72-D4EA84A6DB70}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
|
@ -423,7 +415,6 @@ Global
|
|||
{89157FB8-A25B-42EC-A91F-37DFEE274C8C} = {56C2FFB8-EA54-45B5-A095-4A78142EB4B5}
|
||||
{812B3B34-D2DD-4A3B-BA2D-3D3C52E0C062} = {56C2FFB8-EA54-45B5-A095-4A78142EB4B5}
|
||||
{34FBB2D3-4E2B-4411-95F2-C6B56899826A} = {56C2FFB8-EA54-45B5-A095-4A78142EB4B5}
|
||||
{15B2ABCB-7460-4E59-A991-10AD9190B1C9} = {26A89A29-FE67-4174-9809-7C63AAD6BD5D}
|
||||
{536EFB55-EEA1-4761-9A72-D4EA84A6DB70} = {56C2FFB8-EA54-45B5-A095-4A78142EB4B5}
|
||||
{AA84DBF7-F70F-4673-8DC2-6EFBE3E9BF83} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}
|
||||
EndGlobalSection
|
||||
|
|
|
|||
|
|
@ -13,8 +13,11 @@ var services = builder.Services;
|
|||
var configuration = builder.Configuration;
|
||||
var sqliteConnectionString = configuration.GetConnectionString("Sqlite")!;
|
||||
var identityOptions = new IdentityOptions();
|
||||
var identityTokenOptions = new IdentityTokenOptions();
|
||||
var identitySection = configuration.GetSection("Identity");
|
||||
var identityTokenSection = identitySection.GetSection("Tokens");
|
||||
identitySection.Bind(identityOptions);
|
||||
identityTokenSection.Bind(identityTokenOptions);
|
||||
|
||||
// Add Elsa services.
|
||||
services
|
||||
|
|
@ -23,8 +26,8 @@ services
|
|||
.UseWorkflowsApi()
|
||||
.UseIdentity(identity =>
|
||||
{
|
||||
identity.CreateDefaultUser = true;
|
||||
identity.IdentityOptions = identityOptions;
|
||||
identity.TokenOptions = identityTokenOptions;
|
||||
})
|
||||
.UseDefaultAuthentication()
|
||||
.UseWorkflowRuntime(runtime => { runtime.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString)); })
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@
|
|||
"BasePath": ""
|
||||
},
|
||||
"Identity": {
|
||||
"SigningKey": "secret-signing-key"
|
||||
"CreateDefaultAdmin": true,
|
||||
"Tokens": {
|
||||
"SigningKey": "secret-signing-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
using Elsa.EntityFrameworkCore.Extensions;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Http;
|
||||
using Elsa.Identity;
|
||||
using Elsa.Identity.Options;
|
||||
using Elsa.JavaScript.Activities;
|
||||
using Elsa.Jobs.Activities.Implementations;
|
||||
using Elsa.Jobs.Activities.Middleware.Activities;
|
||||
using Elsa.Jobs.Activities.Services;
|
||||
using Elsa.EntityFrameworkCore.Modules.ActivityDefinitions;
|
||||
using Elsa.EntityFrameworkCore.Modules.Labels;
|
||||
|
|
@ -13,11 +9,6 @@ using Elsa.EntityFrameworkCore.Modules.Management;
|
|||
using Elsa.EntityFrameworkCore.Modules.Runtime;
|
||||
using Elsa.MassTransit.Options;
|
||||
using Elsa.Requirements;
|
||||
using Elsa.Scheduling.Activities;
|
||||
using Elsa.Workflows.Core.Activities;
|
||||
using Elsa.Workflows.Core.Middleware.Activities;
|
||||
using Elsa.Workflows.Core.Middleware.Workflows;
|
||||
using Elsa.Workflows.Management.Services;
|
||||
using Elsa.WorkflowServer.Web.Jobs;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
|
|
@ -26,32 +17,30 @@ var services = builder.Services;
|
|||
var configuration = builder.Configuration;
|
||||
var sqliteConnectionString = configuration.GetConnectionString("Sqlite")!;
|
||||
var identityOptions = new IdentityOptions();
|
||||
var identityTokenOptions = new IdentityTokenOptions();
|
||||
var identitySection = configuration.GetSection("Identity");
|
||||
var identityTokenSection = identitySection.GetSection("Tokens");
|
||||
identitySection.Bind(identityOptions);
|
||||
identityTokenSection.Bind(identityTokenOptions);
|
||||
var rabbitMqOptions = new RabbitMqOptions();
|
||||
configuration.GetSection(RabbitMqOptions.RabbitMq).Bind(rabbitMqOptions);
|
||||
|
||||
// Add Elsa services.
|
||||
services
|
||||
.AddElsa(elsa => elsa
|
||||
.UseWorkflowManagement(management => management
|
||||
.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString))
|
||||
.AddActivitiesFrom<WriteLine>()
|
||||
.AddActivitiesFrom<HttpEndpoint>()
|
||||
.AddActivitiesFrom<Delay>()
|
||||
.AddActivitiesFrom<RunJavaScript>()
|
||||
.AddActivitiesFrom<Program>()
|
||||
)
|
||||
.AddActivitiesFrom<Program>()
|
||||
.UseIdentity(identity =>
|
||||
{
|
||||
identity.CreateDefaultUser = true;
|
||||
identity.IdentityOptions = identityOptions;
|
||||
identity.TokenOptions = identityTokenOptions;
|
||||
})
|
||||
.UseDefaultAuthentication()
|
||||
.UseWorkflowManagement(management => management.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString)))
|
||||
.UseWorkflowRuntime(runtime =>
|
||||
{
|
||||
//runtime.UseProtoActor(proto => proto.PersistenceProvider = _ => new SqliteProvider(new SqliteConnectionStringBuilder(sqliteConnectionString)));
|
||||
runtime.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString));
|
||||
runtime.UseAsyncWorkflowStateExporter();
|
||||
})
|
||||
.UseLabels(labels => labels.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString)))
|
||||
.UseActivityDefinitions(feature => feature.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString)))
|
||||
|
|
@ -78,24 +67,6 @@ var serviceProvider = app.Services;
|
|||
var jobRegistry = serviceProvider.GetRequiredService<IJobRegistry>();
|
||||
jobRegistry.Add(typeof(IndexBlockchainJob));
|
||||
|
||||
// Update activity providers.
|
||||
var activityRegistryPopulator = serviceProvider.GetRequiredService<IActivityRegistryPopulator>();
|
||||
activityRegistryPopulator.PopulateRegistryAsync(typeof(JobActivityProvider));
|
||||
|
||||
// Configure workflow engine execution pipeline.
|
||||
serviceProvider.ConfigureDefaultWorkflowExecutionPipeline(pipeline =>
|
||||
pipeline
|
||||
.UsePersistentVariables()
|
||||
.UseBookmarkPersistence()
|
||||
.UseWorkflowContexts()
|
||||
.UseDefaultActivityScheduler()
|
||||
);
|
||||
|
||||
// Configure activity execution pipeline to use the job-based activity invoker.
|
||||
serviceProvider.ConfigureDefaultActivityExecutionPipeline(pipeline => pipeline
|
||||
.UseExceptionHandling()
|
||||
.UseJobBasedActivityInvoker());
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
app.UseDeveloperExceptionPage();
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,11 @@
|
|||
"RabbitMq": "localhost"
|
||||
},
|
||||
"Identity": {
|
||||
"SigningKey": "secret-signing-key"
|
||||
"CreateDefaultAdmin": true,
|
||||
"Tokens": {
|
||||
"SigningKey": "secret-signing-key",
|
||||
"Lifetime": "8:00:00"
|
||||
}
|
||||
},
|
||||
"RabbitMq": {
|
||||
"Username": "guest",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\..\common.props"/>
|
||||
<Import Project="..\..\..\configureawait.props"/>
|
||||
<Import Project="..\..\..\common.props" />
|
||||
<Import Project="..\..\..\configureawait.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||
|
|
@ -13,17 +13,18 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\common\Elsa.Api.Common\Elsa.Api.Common.csproj"/>
|
||||
<ProjectReference Include="..\..\modules\Elsa.Mediator\Elsa.Mediator.csproj"/>
|
||||
<ProjectReference Include="..\..\modules\Elsa.Workflows.Core\Elsa.Workflows.Core.csproj"/>
|
||||
<ProjectReference Include="..\..\modules\Elsa.Workflows.Management\Elsa.Workflows.Management.csproj"/>
|
||||
<ProjectReference Include="..\..\modules\Elsa.Workflows.Runtime\Elsa.Workflows.Runtime.csproj"/>
|
||||
<ProjectReference Include="..\..\common\Elsa.Api.Common\Elsa.Api.Common.csproj" />
|
||||
<ProjectReference Include="..\..\modules\Elsa.Jobs.Activities\Elsa.Jobs.Activities.csproj" />
|
||||
<ProjectReference Include="..\..\modules\Elsa.Mediator\Elsa.Mediator.csproj" />
|
||||
<ProjectReference Include="..\..\modules\Elsa.Workflows.Core\Elsa.Workflows.Core.csproj" />
|
||||
<ProjectReference Include="..\..\modules\Elsa.Workflows.Management\Elsa.Workflows.Management.csproj" />
|
||||
<ProjectReference Include="..\..\modules\Elsa.Workflows.Runtime\Elsa.Workflows.Runtime.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -11,20 +11,11 @@ public static class ServiceProviderExtensions
|
|||
/// <summary>
|
||||
/// Configure the default activity execution pipeline.
|
||||
/// </summary>
|
||||
public static IServiceProvider ConfigureDefaultActivityExecutionPipeline(this IServiceProvider services, Action<IActivityExecutionBuilder> setup)
|
||||
public static IServiceProvider ConfigureDefaultActivityExecutionPipeline(this IServiceProvider services, Action<IActivityExecutionPipelineBuilder> setup)
|
||||
{
|
||||
var pipeline = services.GetRequiredService<IActivityExecutionPipeline>();
|
||||
pipeline.Setup(setup);
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure the default workflow execution pipeline.
|
||||
/// </summary>
|
||||
public static IServiceProvider ConfigureDefaultWorkflowExecutionPipeline(this IServiceProvider services, Action<IWorkflowExecutionBuilder> setup)
|
||||
{
|
||||
var pipeline = services.GetRequiredService<IWorkflowExecutionPipeline>();
|
||||
pipeline.Setup(setup);
|
||||
return services;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ public class ElsaFeature : FeatureBase
|
|||
/// Set this to true to opt out of automatically registering activities from Elsa.Workflows.Core.
|
||||
/// </summary>
|
||||
public bool DisableAutomaticActivityRegistration { get; set; }
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public ElsaFeature(IModule module) : base(module)
|
||||
{
|
||||
|
|
@ -33,6 +33,14 @@ public class ElsaFeature : FeatureBase
|
|||
/// <inheritdoc />
|
||||
public override void Configure()
|
||||
{
|
||||
Module.UseWorkflowManagement(management => management.AddActivitiesFrom<WriteLine>());
|
||||
Module
|
||||
.UseWorkflows(workflows => workflows
|
||||
.WithDefaultRuntimeWorkflowExecutionPipeline()
|
||||
.WithJobBasedActivityExecutionPipeline())
|
||||
.UseWorkflowManagement(management =>
|
||||
{
|
||||
if (!DisableAutomaticActivityRegistration)
|
||||
management.AddActivitiesFrom<WriteLine>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -17,8 +17,8 @@ import { ActivityInputContext } from "./services/node-input-driver";
|
|||
import { ContextMenuAnchorPoint, MenuItem, MenuItemGroup } from "./components/shared/context-menu/models";
|
||||
import { DropdownButtonItem, DropdownButtonOrigin } from "./components/shared/dropdown-button/models";
|
||||
import { Graph } from "@antv/x6";
|
||||
import { AddActivityArgs, FlowchartPathItem, LayoutDirection, RenameActivityArgs, UpdateActivityArgs } from "./modules/flowchart/models";
|
||||
import { OutNode } from "@antv/layout";
|
||||
import { AddActivityArgs, FlowchartPathItem, RenameActivityArgs, UpdateActivityArgs } from "./modules/flowchart/models";
|
||||
import { ActivityNodeShape } from "./modules/flowchart/shapes";
|
||||
import { PanelActionClickArgs, PanelActionDefinition } from "./components/shared/form-panel/models";
|
||||
import { ExpressionChangedArs } from "./components/designer/input-control-switch/input-control-switch";
|
||||
|
|
@ -99,6 +99,7 @@ export namespace Components {
|
|||
"items": Array<DropdownButtonItem>;
|
||||
"origin": DropdownButtonOrigin;
|
||||
"text": string;
|
||||
"theme": string;
|
||||
}
|
||||
interface ElsaDropdownInput {
|
||||
"inputContext": ActivityInputContext;
|
||||
|
|
@ -108,7 +109,7 @@ export namespace Components {
|
|||
}
|
||||
interface ElsaFlowchart {
|
||||
"addActivity": (args: AddActivityArgs) => Promise<Activity>;
|
||||
"autoLayout": (direction: "TB" | "BT" | "LR" | "RL") => Promise<void>;
|
||||
"autoLayout": (direction: LayoutDirection) => Promise<void>;
|
||||
"export": () => Promise<Activity>;
|
||||
"getGraph": () => Promise<Graph>;
|
||||
"interactiveMode": boolean;
|
||||
|
|
@ -259,7 +260,6 @@ export namespace Components {
|
|||
"workflowDefinition"?: WorkflowDefinition;
|
||||
}
|
||||
interface ElsaWorkflowDefinitionEditorToolbar {
|
||||
"autoLayout": (direction: "TB" | "BT" | "LR" | "RL") => Promise<void>;
|
||||
"zoomToFit": () => Promise<void>;
|
||||
}
|
||||
interface ElsaWorkflowDefinitionEditorToolbox {
|
||||
|
|
@ -409,6 +409,10 @@ export interface ElsaWorkflowDefinitionEditorCustomEvent<T> extends CustomEvent<
|
|||
detail: T;
|
||||
target: HTMLElsaWorkflowDefinitionEditorElement;
|
||||
}
|
||||
export interface ElsaWorkflowDefinitionEditorToolbarCustomEvent<T> extends CustomEvent<T> {
|
||||
detail: T;
|
||||
target: HTMLElsaWorkflowDefinitionEditorToolbarElement;
|
||||
}
|
||||
export interface ElsaWorkflowDefinitionPropertiesEditorCustomEvent<T> extends CustomEvent<T> {
|
||||
detail: T;
|
||||
target: HTMLElsaWorkflowDefinitionPropertiesEditorElement;
|
||||
|
|
@ -916,6 +920,7 @@ declare namespace LocalJSX {
|
|||
"onItemSelected"?: (event: ElsaDropdownButtonCustomEvent<DropdownButtonItem>) => void;
|
||||
"origin"?: DropdownButtonOrigin;
|
||||
"text"?: string;
|
||||
"theme"?: string;
|
||||
}
|
||||
interface ElsaDropdownInput {
|
||||
"inputContext"?: ActivityInputContext;
|
||||
|
|
@ -1078,7 +1083,7 @@ declare namespace LocalJSX {
|
|||
"workflowDefinition"?: WorkflowDefinition;
|
||||
}
|
||||
interface ElsaWorkflowDefinitionEditorToolbar {
|
||||
"autoLayout"?: (direction: "TB" | "BT" | "LR" | "RL") => Promise<void>;
|
||||
"onAutoLayout"?: (event: ElsaWorkflowDefinitionEditorToolbarCustomEvent<LayoutDirection>) => void;
|
||||
"zoomToFit"?: () => Promise<void>;
|
||||
}
|
||||
interface ElsaWorkflowDefinitionEditorToolbox {
|
||||
|
|
|
|||
|
|
@ -6,25 +6,29 @@ import {DropdownButtonItem, DropdownButtonOrigin} from "./models";
|
|||
tag: 'elsa-dropdown-button',
|
||||
shadow: false,
|
||||
})
|
||||
export class ElsaContextMenu {
|
||||
export class DropdownButton {
|
||||
@Prop() public text: string;
|
||||
@Prop() public icon?: any;
|
||||
@Prop() public origin: DropdownButtonOrigin = DropdownButtonOrigin.TopLeft;
|
||||
@Prop() public items: Array<DropdownButtonItem> = [];
|
||||
@Prop() public theme: string = 'Secondary';
|
||||
@Event() public itemSelected: EventEmitter<DropdownButtonItem>
|
||||
|
||||
private contextMenu: HTMLElement;
|
||||
private element: HTMLElement;
|
||||
|
||||
public render() {
|
||||
const buttonClass = this.theme == 'Secondary' ? 'btn-secondary' : 'btn-primary';
|
||||
const arrowClass = this.theme == 'Secondary' ? 'text-gray-400' : 'text-white';
|
||||
|
||||
return (
|
||||
<div class="relative" ref={el => this.element = el}>
|
||||
<button onClick={e => this.toggleMenu()} type="button"
|
||||
class="w-full bg-white border border-gray-300 rounded-md shadow-sm px-4 py-2 inline-flex justify-center text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
class={`btn ${buttonClass} w-full border`}
|
||||
aria-haspopup="true" aria-expanded="false">
|
||||
{this.renderIcon()}
|
||||
{this.text}
|
||||
<svg class="ml-2.5 -mr-1.5 h-5 w-5 text-gray-400" x-description="Heroicon name: chevron-down" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<svg class={`ml-2.5 -mr-1.5 h-5 w-5 ${arrowClass}`} x-description="Heroicon name: chevron-down" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
|
@ -83,6 +87,10 @@ export class ElsaContextMenu {
|
|||
|
||||
private async onItemClick(e: Event, menuItem: DropdownButtonItem) {
|
||||
e.preventDefault();
|
||||
|
||||
if(!!menuItem.handler)
|
||||
menuItem.handler();
|
||||
|
||||
this.itemSelected.emit(menuItem);
|
||||
this.closeContextMenu();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export class ModalDialog {
|
|||
|
||||
@Prop() modalDialogInstance: ModalDialogInstance;
|
||||
@Prop() actions: Array<ModalActionDefinition> = [];
|
||||
@Prop() size: string = 'sm:max-w-6xl';
|
||||
@Prop() size: string = 'max-w-6xl';
|
||||
@Prop() type: ModalType = ModalType.Default;
|
||||
@Prop() autoHide: boolean = true;
|
||||
@Prop() content: () => any = () => <div/>;
|
||||
|
|
@ -68,12 +68,6 @@ export class ModalDialog {
|
|||
}
|
||||
}
|
||||
|
||||
componentWillRender() {
|
||||
if (this.type == ModalType.Default) {
|
||||
this.size += " sm:w-full";
|
||||
}
|
||||
}
|
||||
|
||||
componentDidRender() {
|
||||
if (this.isVisible) {
|
||||
enter(this.overlay);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export interface Activity {
|
|||
metadata: any;
|
||||
canStartWorkflow?: boolean;
|
||||
runAsynchronously?: boolean;
|
||||
applicationProperties: any;
|
||||
customProperties: any;
|
||||
|
||||
[name: string]: any;
|
||||
}
|
||||
|
|
@ -30,6 +30,11 @@ export interface Container extends Activity {
|
|||
variables: Array<Variable>;
|
||||
}
|
||||
|
||||
export interface Workflow extends Activity {
|
||||
root: Activity;
|
||||
variables: Array<Variable>;
|
||||
}
|
||||
|
||||
export interface Variable {
|
||||
name: string;
|
||||
typeName: string;
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ export class ActivityDefinitionsPlugin implements Plugin {
|
|||
connections: [],
|
||||
id: newName,
|
||||
metadata: {},
|
||||
applicationProperties: {},
|
||||
customProperties: {},
|
||||
variables: []
|
||||
} as Flowchart;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import './ports';
|
|||
import {ActivityNodeShape} from './shapes';
|
||||
import {Activity, ActivityDeletedArgs, ActivityDescriptor, ActivitySelectedArgs, ContainerSelectedArgs, EditChildActivityArgs, GraphUpdatedArgs, Port, WorkflowUpdatedArgs} from '../../models';
|
||||
import {createGraph} from './graph-factory';
|
||||
import {AddActivityArgs, Connection, Flowchart, FlowchartModel, FlowchartNavigationItem, FlowchartPathItem, RenameActivityArgs, UpdateActivityArgs} from './models';
|
||||
import {AddActivityArgs, Connection, Flowchart, FlowchartModel, FlowchartNavigationItem, FlowchartPathItem, LayoutDirection, RenameActivityArgs, UpdateActivityArgs} from './models';
|
||||
import {NodeFactory} from "./node-factory";
|
||||
import {Container} from "typedi";
|
||||
import {ActivityNode, createActivityLookup, EventBus, flatten, PortProviderRegistry, walkActivities} from "../../services";
|
||||
|
|
@ -23,7 +23,6 @@ import {DagreLayout, OutNode} from '@antv/layout';
|
|||
import {adjustPortMarkupByNode, rebuildGraph} from '../../utils/graph';
|
||||
import {WorkflowDefinition} from "../workflow-definitions/models/entities";
|
||||
import FlowchartTunnel, {FlowchartState} from "./state";
|
||||
import {WorkflowDefinitionUpdatedArgs} from "../workflow-definitions/models/ui";
|
||||
|
||||
const FlowchartTypeName = 'Elsa.Flowchart';
|
||||
|
||||
|
|
@ -115,7 +114,8 @@ export class FlowchartComponent {
|
|||
}
|
||||
|
||||
@Method()
|
||||
async autoLayout(direction: "TB" | "BT" | "LR" | "RL") {
|
||||
async autoLayout(direction: LayoutDirection) {
|
||||
debugger;
|
||||
const dagreLayout = new DagreLayout({
|
||||
type: 'dagre',
|
||||
rankdir: direction,
|
||||
|
|
@ -150,7 +150,8 @@ export class FlowchartComponent {
|
|||
this.updateActivity({id: activity.id, originalId: activity.id, activity: activity});
|
||||
});
|
||||
|
||||
//this.importInternal()
|
||||
this.updateGraphInternal(flowchartModel.activities, flowchartModel.connections);
|
||||
this.graphUpdated.emit({});
|
||||
}
|
||||
|
||||
@Method()
|
||||
|
|
@ -166,7 +167,7 @@ export class FlowchartComponent {
|
|||
id: id,
|
||||
type: descriptor.typeName,
|
||||
version: descriptor.version,
|
||||
applicationProperties: {},
|
||||
customProperties: {},
|
||||
metadata: {
|
||||
designer: {
|
||||
position: {
|
||||
|
|
@ -276,8 +277,11 @@ export class FlowchartComponent {
|
|||
await this.setupGraph(childFlowchart);
|
||||
}
|
||||
|
||||
async componentDidLoad() {
|
||||
async componentWillLoad() {
|
||||
this.updateLookups();
|
||||
}
|
||||
|
||||
async componentDidLoad() {
|
||||
await this.createAndInitializeGraph();
|
||||
}
|
||||
|
||||
|
|
@ -296,7 +300,7 @@ export class FlowchartComponent {
|
|||
connections: [],
|
||||
metadata: {},
|
||||
variables: [],
|
||||
applicationProperties: {},
|
||||
customProperties: {},
|
||||
canStartWorkflow: false
|
||||
};
|
||||
}
|
||||
|
|
@ -392,6 +396,11 @@ export class FlowchartComponent {
|
|||
private setupGraph = async (flowchart: Flowchart) => {
|
||||
const activities = flowchart.activities;
|
||||
const connections = flowchart.connections;
|
||||
this.updateGraphInternal(activities, connections);
|
||||
await this.scrollToStart();
|
||||
};
|
||||
|
||||
private updateGraphInternal = (activities: Array<Activity>, connections: Array<Connection>) => {
|
||||
const edges: Array<Edge.Metadata> = [];
|
||||
|
||||
// Create an X6 node for each activity.
|
||||
|
|
@ -416,8 +425,7 @@ export class FlowchartComponent {
|
|||
this.graph.unfreeze();
|
||||
|
||||
rebuildGraph(this.graph);
|
||||
await this.scrollToStart();
|
||||
};
|
||||
}
|
||||
|
||||
private getFlowchartModel = (): FlowchartModel => {
|
||||
const graph = this.graph;
|
||||
|
|
|
|||
|
|
@ -47,3 +47,5 @@ export interface RenameActivityArgs {
|
|||
newId: string;
|
||||
activity: Activity;
|
||||
}
|
||||
|
||||
export type LayoutDirection = 'LR' | 'TB' | 'RL' | 'BT';
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {ActivityPropertyChangedEventArgs, WorkflowDefinitionPropsUpdatedArgs, Wo
|
|||
import {WorkflowDefinition} from "../models/entities";
|
||||
import {WorkflowDefinitionsApi} from "../services/api"
|
||||
import WorkflowDefinitionTunnel, {WorkflowDefinitionState} from "../../../state/workflow-definition-state";
|
||||
import {LayoutDirection} from "../../flowchart/models";
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-workflow-definition-editor',
|
||||
|
|
@ -226,7 +227,10 @@ export class WorkflowDefinitionEditor {
|
|||
|
||||
private onZoomToFit = async () => await this.flowchart.zoomToFit();
|
||||
|
||||
private onAutoLayout = async (direction: "TB" | "BT" | "LR" | "RL") => await this.flowchart.autoLayout(direction);
|
||||
private onAutoLayout = async (direction: LayoutDirection) => {
|
||||
debugger;
|
||||
await this.flowchart.autoLayout(direction);
|
||||
};
|
||||
|
||||
private onActivityUpdated = async (e: CustomEvent<ActivityUpdatedArgs>) => {
|
||||
await this.flowchart.updateActivity({
|
||||
|
|
@ -279,7 +283,7 @@ export class WorkflowDefinitionEditor {
|
|||
return (
|
||||
<WorkflowDefinitionTunnel.Provider state={state}>
|
||||
<div class="absolute inset-0" ref={el => this.container = el}>
|
||||
<elsa-workflow-definition-editor-toolbar zoomToFit={this.onZoomToFit} autoLayout={this.onAutoLayout}/>
|
||||
<elsa-workflow-definition-editor-toolbar zoomToFit={this.onZoomToFit} onAutoLayout={(e: CustomEvent<LayoutDirection>) => this.onAutoLayout(e.detail)}/>
|
||||
<elsa-panel
|
||||
class="elsa-activity-picker-container z-30"
|
||||
position={PanelPosition.Left}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { Component, h, Prop } from '@stencil/core';
|
||||
import {Component, h, Prop, Event, EventEmitter} from '@stencil/core';
|
||||
import {DropdownButtonItem} from "../../../components/shared/dropdown-button/models";
|
||||
import {LayoutDirection} from "../../flowchart/models";
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-workflow-definition-editor-toolbar',
|
||||
|
|
@ -7,18 +9,22 @@ export class Toolbar {
|
|||
@Prop()
|
||||
public zoomToFit: () => Promise<void>;
|
||||
|
||||
@Prop()
|
||||
public autoLayout: (direction: "TB" | "BT" | "LR" | "RL") => Promise<void>;
|
||||
@Event()
|
||||
public autoLayout: EventEmitter<LayoutDirection>;
|
||||
|
||||
render() {
|
||||
|
||||
const layoutButtons: Array<DropdownButtonItem> = [{
|
||||
text: 'Horizontally',
|
||||
handler: () => this.autoLayout.emit('LR')
|
||||
},{
|
||||
text: 'Vertically',
|
||||
handler: () => this.autoLayout.emit('TB')
|
||||
}];
|
||||
|
||||
return (
|
||||
<div class="elsa-panel-toolbar flex justify-center absolute border-b border-gray-200 top-0 px-1 pl-4 pb-2 text-sm bg-white z-10 space-x-2">
|
||||
<button class="btn btn-primary" onClick={() => this.autoLayout("LR")}>
|
||||
Auto-Layout (LR)
|
||||
</button>
|
||||
<button class="btn btn-primary" onClick={() => this.autoLayout("TB")}>
|
||||
Auto-Layout (TB)
|
||||
</button>
|
||||
<elsa-dropdown-button text="Auto-layout" theme="Primary" items={layoutButtons}/>
|
||||
<button onClick={this.zoomToFit}class="btn btn-primary">
|
||||
Zoom to fit
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export class WorkflowDefinitionsPlugin implements Plugin {
|
|||
|
||||
const newWorkflowDefinitionItem: MenuItem = {
|
||||
text: 'Workflow Definition',
|
||||
clickHandler: this.onNewWorkflowDefinitionClick
|
||||
clickHandler: this.onNewWorkflowDefinitionSelected
|
||||
}
|
||||
|
||||
const importWorkflowDefinitionItem: MenuItem = {
|
||||
|
|
@ -80,7 +80,7 @@ export class WorkflowDefinitionsPlugin implements Plugin {
|
|||
connections: [],
|
||||
id: newName,
|
||||
metadata: {},
|
||||
applicationProperties: {},
|
||||
customProperties: {},
|
||||
variables: []
|
||||
} as Flowchart;
|
||||
|
||||
|
|
@ -146,7 +146,7 @@ export class WorkflowDefinitionsPlugin implements Plugin {
|
|||
});
|
||||
};
|
||||
|
||||
private onNewWorkflowDefinitionClick = async () => {
|
||||
private onNewWorkflowDefinitionSelected = async () => {
|
||||
await this.newWorkflow();
|
||||
this.modalDialogService.hide(this.workflowDefinitionBrowserInstance);
|
||||
};
|
||||
|
|
@ -158,32 +158,16 @@ export class WorkflowDefinitionsPlugin implements Plugin {
|
|||
|
||||
private onWorkflowUpdated = async (e: CustomEvent<WorkflowDefinitionUpdatedArgs>) => {
|
||||
const updatedWorkflowDefinition = e.detail.workflowDefinition;
|
||||
|
||||
await this.saveWorkflowDefinition(updatedWorkflowDefinition, false);
|
||||
|
||||
// if (e.detail.latestVersionNumber == undefined) {
|
||||
// await this.saveWorkflowDefinition(updatedWorkflowDefinition, false);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if (updatedWorkflowDefinition.version == e.detail.latestVersionNumber || updatedWorkflowDefinition.isPublished) {
|
||||
// const currentWorkflowDefinition = await this.api.get({definitionId: updatedWorkflowDefinition.definitionId, versionOptions: {version: updatedWorkflowDefinition.version}});
|
||||
// if (!isEqual(currentWorkflowDefinition.root.activities, updatedWorkflowDefinition.root.activities) || !isEqual(currentWorkflowDefinition.variables, updatedWorkflowDefinition.variables)) {
|
||||
// if (updatedWorkflowDefinition.isPublished)
|
||||
// updatedWorkflowDefinition.version = e.detail.latestVersionNumber;
|
||||
//
|
||||
// await this.saveWorkflowDefinition(updatedWorkflowDefinition, false);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private onBrowseWorkflowDefinitions = async () => {
|
||||
const closeAction = DefaultModalActions.Close();
|
||||
const newAction = DefaultModalActions.New(this.onNewWorkflowDefinitionClick);
|
||||
const newAction = DefaultModalActions.New(this.onNewWorkflowDefinitionSelected);
|
||||
const actions = [closeAction, newAction];
|
||||
|
||||
this.workflowDefinitionBrowserInstance = this.modalDialogService.show(() =>
|
||||
<elsa-workflow-definition-browser onWorkflowDefinitionSelected={this.onWorkflowDefinitionSelected} onNewWorkflowDefinitionSelected={this.onNewWorkflowDefinitionClick}/>,
|
||||
<elsa-workflow-definition-browser onWorkflowDefinitionSelected={this.onWorkflowDefinitionSelected} onNewWorkflowDefinitionSelected={this.onNewWorkflowDefinitionSelected}/>,
|
||||
{actions})
|
||||
}
|
||||
|
||||
|
|
@ -198,16 +182,10 @@ export class WorkflowDefinitionsPlugin implements Plugin {
|
|||
e.detail.begin();
|
||||
const notification = NotificationService.createNotification({title: 'Publishing', id: uuid(), text: 'Workflow is being publishing. Please wait'})
|
||||
const workflowDefinition = await this.workflowDefinitionEditorElement.getWorkflowDefinition();
|
||||
//await this.eventBus.emit(NotificationEventTypes.Add, this, {id: workflowDefinition.definitionId, message: `Starting publishing ${workflowDefinition.name}`});
|
||||
await this.saveWorkflowDefinition(workflowDefinition, true);
|
||||
|
||||
NotificationService.updateNotification(notification, {title: 'Workflow published', text: 'Published !'})
|
||||
e.detail.complete();
|
||||
// setTimeout(async () => {
|
||||
// await this.eventBus.emit(NotificationEventTypes.Update, this, {id: workflowDefinition.definitionId, message: `${workflowDefinition.name} publish finished`});
|
||||
// e.detail.complete();
|
||||
// }, 2000)
|
||||
|
||||
}
|
||||
|
||||
private onExportClicked = async (e: CustomEvent) => {
|
||||
|
|
|
|||
|
|
@ -108,8 +108,6 @@ export class WorkflowInstanceBrowser {
|
|||
<th>Status</th>
|
||||
<th class="optional">Created</th>
|
||||
<th class="optional">Finished</th>
|
||||
<th class="optional">Executed</th>
|
||||
<th class="optional">Faulted</th>
|
||||
<th/>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -143,8 +141,6 @@ export class WorkflowInstanceBrowser {
|
|||
</td>
|
||||
<td class="optional">{formatTimestamp(workflowInstance.createdAt, '-')}</td>
|
||||
<td class="optional">{formatTimestamp(workflowInstance.finishedAt, '-')}</td>
|
||||
<td class="optional">{formatTimestamp(workflowInstance.lastExecutedAt, '-')}</td>
|
||||
<td class="optional">{formatTimestamp(workflowInstance.faultedAt, '-')}</td>
|
||||
<td class="pr-6">
|
||||
<elsa-context-menu menuItems={[
|
||||
{text: 'Edit', clickHandler: e => this.onWorkflowInstanceClick(e, workflowInstance), icon: <EditIcon/>},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import {Component, h, Prop, State, Watch} from "@stencil/core";
|
||||
import {ActivityDescriptor, WorkflowExecutionLogRecord, WorkflowInstance} from "../../../models";
|
||||
import {ActivityDescriptor, Workflow, WorkflowExecutionLogRecord, WorkflowInstance} from "../../../models";
|
||||
import {Container} from "typedi";
|
||||
import {ActivityIconRegistry, ActivityNode, createActivityNodeMap, flatten, walkActivities} from "../../../services";
|
||||
import {durationToString, formatTime, getDuration, Hash, isNullOrWhitespace} from "../../../utils";
|
||||
|
|
@ -115,7 +115,7 @@ export class Journal {
|
|||
const activityDisplayText = isNullOrWhitespace(activityMetadata.displayText) ? activity.id : activityMetadata.displayText;
|
||||
const duration = durationToString(block.duration);
|
||||
const status = block.completed ? 'Completed' : 'Started';
|
||||
const icon = iconRegistry.get(activity.type)({size: ActivityIconSize.Small});
|
||||
const icon = iconRegistry.getOrDefault(activity.type)({size: ActivityIconSize.Small});
|
||||
const expanded = !!expandedBlocks.find(x => x == block);
|
||||
|
||||
const toggleIcon = expanded
|
||||
|
|
@ -166,7 +166,19 @@ export class Journal {
|
|||
if (!this.workflowInstance || !this.workflowDefinition)
|
||||
return;
|
||||
|
||||
this.nodes = flatten(walkActivities(this.workflowDefinition.root));
|
||||
const workflow: Workflow = {
|
||||
type: 'Elsa.Workflow',
|
||||
id: 'Workflow1', // Always 'Workflow1'.
|
||||
version: this.workflowDefinition.version,
|
||||
customProperties: this.workflowDefinition.customProperties,
|
||||
canStartWorkflow: false,
|
||||
runAsynchronously: false,
|
||||
metadata: {},
|
||||
root: this.workflowDefinition.root,
|
||||
variables: this.workflowDefinition.variables
|
||||
};
|
||||
|
||||
this.nodes = flatten(walkActivities(workflow));
|
||||
this.nodeMap = createActivityNodeMap(this.nodes);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {WorkflowEditorEventTypes} from "../../workflow-definitions/models/ui";
|
|||
|
||||
@Component({
|
||||
tag: 'elsa-workflow-instance-viewer',
|
||||
styleUrl: 'workflow-instance-viewer.scss',
|
||||
styleUrl: 'viewer.scss',
|
||||
})
|
||||
export class WorkflowInstanceViewer {
|
||||
private readonly pluginRegistry: PluginRegistry;
|
||||
|
|
@ -99,7 +99,12 @@ export class WorkflowInstanceViewer {
|
|||
public async importWorkflow(workflowDefinition: WorkflowDefinition, workflowInstance: WorkflowInstance): Promise<void> {
|
||||
this.workflowInstanceState = workflowInstance;
|
||||
await this.updateWorkflowDefinition(workflowDefinition);
|
||||
//await this.flowchartElement.import(workflowDefinition.root);
|
||||
// Update the flowchart after state is updated.
|
||||
window.requestAnimationFrame(async () => {
|
||||
await this.flowchartElement.updateGraph();
|
||||
});
|
||||
|
||||
await this.eventBus.emit(WorkflowEditorEventTypes.WorkflowDefinition.Imported, this, {workflowDefinition});
|
||||
}
|
||||
|
||||
// Updates the workflow definition without importing it into the designer.
|
||||
|
|
@ -165,6 +170,7 @@ export class WorkflowInstanceViewer {
|
|||
</elsa-panel>
|
||||
<elsa-flowchart
|
||||
ref={el => this.flowchartElement = el}
|
||||
workflowDefinition={workflowDefinition}
|
||||
interactiveMode={false}/>
|
||||
<elsa-panel
|
||||
class="elsa-workflow-editor-container"
|
||||
|
|
@ -53,7 +53,7 @@ export class WorkflowInstancesPlugin implements Plugin {
|
|||
|
||||
this.workflowInstanceBrowserInstance = this.modalDialogService.show(() =>
|
||||
<elsa-workflow-instance-browser onWorkflowInstanceSelected={this.onWorkflowInstanceSelected}/>,
|
||||
{actions: actions, size: 'max-w-10xl'})
|
||||
{actions: actions, size: 'max-w-screen-2xl'})
|
||||
}
|
||||
|
||||
private onWorkflowInstanceSelected = async (e: CustomEvent<WorkflowInstanceSummary>) => {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,30 @@
|
|||
using Elsa.Workflows.Runtime.Features;
|
||||
using Elsa.Workflows.Runtime.Implementations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.EntityFrameworkCore.Modules.Runtime
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extensions to the <see cref="WorkflowRuntimeFeature"/> feature.
|
||||
/// </summary>
|
||||
public static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures the <see cref="WorkflowRuntimeFeature"/> to use the <see cref="EFCoreRuntimePersistenceFeature"/>.
|
||||
/// </summary>
|
||||
public static WorkflowRuntimeFeature UseEntityFrameworkCore(this WorkflowRuntimeFeature feature, Action<EFCoreRuntimePersistenceFeature>? configure = default)
|
||||
{
|
||||
feature.Module.Configure(configure);
|
||||
return feature;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the workflow runtime to send workflow state to the <see cref="AsyncWorkflowStateExporter"/>.
|
||||
/// </summary>
|
||||
public static WorkflowRuntimeFeature UseAsyncWorkflowStateExporter(this WorkflowRuntimeFeature feature)
|
||||
{
|
||||
feature.WorkflowStateExporter = sp => ActivatorUtilities.CreateInstance<AsyncWorkflowStateExporter>(sp);
|
||||
return feature;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ public static class ModuleExtensions
|
|||
/// </summary>
|
||||
public static IModule UseIdentity(this IModule module, string signingKey, string issuer = "http://elsa.api", string audience = "http://elsa.api", TimeSpan? tokenLifetime = default)
|
||||
{
|
||||
module.UseIdentity(identity => identity.IdentityOptions = new IdentityOptions
|
||||
module.UseIdentity(identity => identity.TokenOptions = new IdentityTokenOptions
|
||||
{
|
||||
Audience = audience,
|
||||
Issuer = issuer,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ public class DefaultAuthenticationFeature : FeatureBase
|
|||
public override void Apply()
|
||||
{
|
||||
var identityFeature = Module.Configure<IdentityFeature>();
|
||||
var identityOptions = identityFeature.IdentityOptions;
|
||||
var identityOptions = identityFeature.TokenOptions;
|
||||
|
||||
Services
|
||||
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
|
|
|
|||
|
|
@ -23,16 +23,16 @@ public class IdentityFeature : FeatureBase
|
|||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A flag indicating whether a default user should be created.
|
||||
/// </summary>
|
||||
public bool CreateDefaultUser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to configure <see cref="Options.IdentityOptions"/>.
|
||||
/// </summary>
|
||||
public IdentityOptions IdentityOptions { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to configure <see cref="Options.IdentityTokenOptions"/>.
|
||||
/// </summary>
|
||||
public IdentityTokenOptions TokenOptions { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// A delegate that creates an instance of an implementation of <see cref="IUserStore"/>.
|
||||
/// </summary>
|
||||
|
|
@ -52,14 +52,14 @@ public class IdentityFeature : FeatureBase
|
|||
/// <inheritdoc />
|
||||
public override void ConfigureHostedServices()
|
||||
{
|
||||
if(CreateDefaultUser)
|
||||
if(IdentityOptions.CreateDefaultAdmin)
|
||||
Module.ConfigureHostedService<SetupDefaultUserHostedService>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Apply()
|
||||
{
|
||||
Services.Configure<IdentityOptions>(options => options.CopyFrom(IdentityOptions));
|
||||
Services.Configure<IdentityTokenOptions>(options => options.CopyFrom(TokenOptions));
|
||||
|
||||
Services
|
||||
.AddMemoryStore<User, MemoryUserStore>()
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ namespace Elsa.Identity.Implementations;
|
|||
public class DefaultAccessTokenIssuer : IAccessTokenIssuer
|
||||
{
|
||||
private readonly ISystemClock _systemClock;
|
||||
private readonly IdentityOptions _identityOptions;
|
||||
private readonly IdentityTokenOptions _identityOptions;
|
||||
|
||||
public DefaultAccessTokenIssuer(ISystemClock systemClock, IOptions<IdentityOptions> identityOptions)
|
||||
public DefaultAccessTokenIssuer(ISystemClock systemClock, IOptions<IdentityTokenOptions> identityOptions)
|
||||
{
|
||||
_systemClock = systemClock;
|
||||
_identityOptions = identityOptions.Value;
|
||||
|
|
|
|||
|
|
@ -1,42 +1,6 @@
|
|||
using System.Text;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace Elsa.Identity.Options;
|
||||
|
||||
public class IdentityOptions
|
||||
{
|
||||
public string SigningKey { get; set; }
|
||||
public string Issuer { get; set; } = "http://elsa.api";
|
||||
public string Audience { get; set; } = "http://elsa.api";
|
||||
public TimeSpan? Lifetime { get; set; } = TimeSpan.FromHours(1);
|
||||
|
||||
public SecurityKey CreateSecurityKey() => new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SigningKey));
|
||||
|
||||
public void ConfigureJwtBearerOptions(JwtBearerOptions options) =>
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
IssuerSigningKey = CreateSecurityKey(),
|
||||
ValidAudience = Audience,
|
||||
ValidIssuer = Issuer
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Deconstructor.
|
||||
/// </summary>
|
||||
internal void Deconstruct(out string signingKey, out string issuer, out string audience, out TimeSpan? lifetime)
|
||||
{
|
||||
signingKey = SigningKey;
|
||||
issuer = Issuer;
|
||||
audience = Audience;
|
||||
lifetime = Lifetime;
|
||||
}
|
||||
|
||||
internal void CopyFrom(IdentityOptions identityOptions)
|
||||
{
|
||||
SigningKey = identityOptions.SigningKey;
|
||||
Audience = identityOptions.Audience;
|
||||
Issuer = identityOptions.Issuer;
|
||||
Lifetime = identityOptions.Lifetime;
|
||||
}
|
||||
public bool CreateDefaultAdmin { get; set; }
|
||||
}
|
||||
42
src/modules/Elsa.Identity/Options/IdentityTokenOptions.cs
Normal file
42
src/modules/Elsa.Identity/Options/IdentityTokenOptions.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
using System.Text;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace Elsa.Identity.Options;
|
||||
|
||||
public class IdentityTokenOptions
|
||||
{
|
||||
public string SigningKey { get; set; }
|
||||
public string Issuer { get; set; } = "http://elsa.api";
|
||||
public string Audience { get; set; } = "http://elsa.api";
|
||||
public TimeSpan? Lifetime { get; set; } = TimeSpan.FromHours(1);
|
||||
|
||||
public SecurityKey CreateSecurityKey() => new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SigningKey));
|
||||
|
||||
public void ConfigureJwtBearerOptions(JwtBearerOptions options) =>
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
IssuerSigningKey = CreateSecurityKey(),
|
||||
ValidAudience = Audience,
|
||||
ValidIssuer = Issuer
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Deconstructor.
|
||||
/// </summary>
|
||||
internal void Deconstruct(out string signingKey, out string issuer, out string audience, out TimeSpan? lifetime)
|
||||
{
|
||||
signingKey = SigningKey;
|
||||
issuer = Issuer;
|
||||
audience = Audience;
|
||||
lifetime = Lifetime;
|
||||
}
|
||||
|
||||
internal void CopyFrom(IdentityTokenOptions identityOptions)
|
||||
{
|
||||
SigningKey = identityOptions.SigningKey;
|
||||
Audience = identityOptions.Audience;
|
||||
Issuer = identityOptions.Issuer;
|
||||
Lifetime = identityOptions.Lifetime;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using Elsa.Jobs.Activities.Middleware.Activities;
|
||||
using Elsa.Workflows.Core.Pipelines.ActivityExecution;
|
||||
using Elsa.Workflows.Core.Services;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Adds extensions to <see cref="IActivityExecutionPipelineBuilder"/>.
|
||||
/// </summary>
|
||||
public static class JobBasedActivityInvokerMiddlewareExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Installs the <see cref="JobBasedActivityInvokerMiddleware"/>.
|
||||
/// </summary>
|
||||
public static IActivityExecutionPipelineBuilder UseJobBasedActivityInvoker(this IActivityExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.UseMiddleware<JobBasedActivityInvokerMiddleware>();
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using Elsa.Workflows.Core.Features;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Adds an extension method to the <see cref="WorkflowsFeature"/> that installs a default workflow runtime execution pipeline.
|
||||
/// </summary>
|
||||
public static class WorkflowsFeatureExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Installs a default workflow runtime execution pipeline.
|
||||
/// </summary>
|
||||
public static WorkflowsFeature WithJobBasedActivityExecutionPipeline(this WorkflowsFeature workflowsFeature) => workflowsFeature.WithActivityExecutionPipeline(pipeline => pipeline.UseJobBasedActivityInvoker());
|
||||
}
|
||||
|
|
@ -5,16 +5,10 @@ using Elsa.Jobs.Services;
|
|||
using Elsa.Workflows.Core.Middleware.Activities;
|
||||
using Elsa.Workflows.Core.Models;
|
||||
using Elsa.Workflows.Core.Pipelines.ActivityExecution;
|
||||
using Elsa.Workflows.Core.Services;
|
||||
using Elsa.Workflows.Management.Services;
|
||||
|
||||
namespace Elsa.Jobs.Activities.Middleware.Activities;
|
||||
|
||||
public static class JobBasedActivityInvokerMiddlewareExtensions
|
||||
{
|
||||
public static IActivityExecutionBuilder UseJobBasedActivityInvoker(this IActivityExecutionBuilder builder) => builder.UseMiddleware<JobBasedActivityInvokerMiddleware>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the current activity from a background job if the activity is of kind <see cref="ActivityKind.Job"/> or <see cref="ActivityKind.Task"/>
|
||||
/// </summary>
|
||||
|
|
@ -26,6 +20,7 @@ public class JobBasedActivityInvokerMiddleware : DefaultActivityInvokerMiddlewar
|
|||
private readonly IJobFactory _jobFactory;
|
||||
private readonly IJobQueue _jobQueue;
|
||||
|
||||
/// <inheritdoc />
|
||||
public JobBasedActivityInvokerMiddleware(
|
||||
ActivityMiddlewareDelegate next,
|
||||
IActivityRegistry activityRegistry,
|
||||
|
|
@ -39,6 +34,7 @@ public class JobBasedActivityInvokerMiddleware : DefaultActivityInvokerMiddlewar
|
|||
_jobQueue = jobQueue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ExecuteActivityAsync(ActivityExecutionContext context)
|
||||
{
|
||||
var activity = context.Activity;
|
||||
|
|
|
|||
|
|
@ -5,22 +5,30 @@ using Elsa.Features.Services;
|
|||
using Elsa.Scheduling.Handlers;
|
||||
using Elsa.Scheduling.Implementations;
|
||||
using Elsa.Scheduling.Services;
|
||||
using Elsa.Workflows.Management.Features;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Scheduling.Features;
|
||||
|
||||
/// <summary>
|
||||
/// Provides scheduling features to the system.
|
||||
/// </summary>
|
||||
[DependsOn(typeof(SystemClockFeature))]
|
||||
public class SchedulingFeature : FeatureBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public SchedulingFeature(IModule module) : base(module)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Apply()
|
||||
{
|
||||
Services
|
||||
.AddSingleton<IWorkflowTriggerScheduler, WorkflowTriggerScheduler>()
|
||||
.AddSingleton<IWorkflowBookmarkScheduler, WorkflowBookmarkScheduler>()
|
||||
.AddNotificationHandlersFrom<ScheduleWorkflows>();
|
||||
|
||||
Module.Configure<WorkflowManagementFeature>(management => management.AddActivitiesFrom<SchedulingFeature>());
|
||||
}
|
||||
}
|
||||
|
|
@ -11,10 +11,10 @@ public static class WorkflowExecutionBuilderExtensions
|
|||
/// <summary>
|
||||
/// Installs middleware that enables the use of workflow context.
|
||||
/// </summary>
|
||||
public static IWorkflowExecutionBuilder UseWorkflowContexts(this IWorkflowExecutionBuilder builder) => builder.UseMiddleware<WorkflowContextWorkflowExecutionMiddleware>();
|
||||
public static IWorkflowExecutionPipelineBuilder UseWorkflowContexts(this IWorkflowExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.UseMiddleware<WorkflowContextWorkflowExecutionMiddleware>();
|
||||
|
||||
/// <summary>
|
||||
/// Installs middleware that enables the use of workflow context.
|
||||
/// </summary>
|
||||
public static IActivityExecutionBuilder UseWorkflowContexts(this IActivityExecutionBuilder builder) => builder.UseMiddleware<WorkflowContextActivityExecutionMiddleware>();
|
||||
public static IActivityExecutionPipelineBuilder UseWorkflowContexts(this IActivityExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.UseMiddleware<WorkflowContextActivityExecutionMiddleware>();
|
||||
}
|
||||
|
|
@ -3,10 +3,15 @@ using FastEndpoints;
|
|||
|
||||
namespace Elsa.Workflows.Api.Endpoints.WorkflowInstances.Get;
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="WorkflowInstance"/> to <see cref="Response"/>.
|
||||
/// </summary>
|
||||
public class WorkflowInstanceMapper : ResponseMapper<Response, WorkflowInstance>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override Response FromEntity(WorkflowInstance e) => new()
|
||||
{
|
||||
Id = e.Id,
|
||||
DefinitionId = e.DefinitionId,
|
||||
DefinitionVersionId = e.DefinitionVersionId,
|
||||
Version = e.Version,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ public class Request
|
|||
|
||||
public class Response
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string DefinitionId { get; init; } = default!;
|
||||
public string DefinitionVersionId { get; init; } = default!;
|
||||
public int Version { get; init; }
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ public abstract class Composite : ActivityBase, IVariableContainer
|
|||
/// The activity to schedule when this activity executes.
|
||||
/// </summary>
|
||||
[Port]
|
||||
[Browsable(false)]
|
||||
[JsonIgnore] // Composite activities' Root is intended to be constructed from code only.
|
||||
public IActivity Root { get; set; } = new Sequence();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.ComponentModel;
|
||||
using Elsa.Workflows.Core.Attributes;
|
||||
using Elsa.Workflows.Core.Models;
|
||||
|
||||
namespace Elsa.Workflows.Core.Activities;
|
||||
|
|
@ -7,6 +8,7 @@ namespace Elsa.Workflows.Core.Activities;
|
|||
/// This activity is instantiated in case a workflow references an activity type that could not be found.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
[Activity("Elsa", "Workflows", "A placeholder activity that will be used in case a workflow definition references an activity type that cannot be found.")]
|
||||
public class NotFoundActivity : Activity
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -10,9 +10,12 @@ using Elsa.Workflows.Core.ActivityNodeResolvers;
|
|||
using Elsa.Workflows.Core.Builders;
|
||||
using Elsa.Workflows.Core.Expressions;
|
||||
using Elsa.Workflows.Core.Implementations;
|
||||
using Elsa.Workflows.Core.Middleware.Activities;
|
||||
using Elsa.Workflows.Core.Middleware.Workflows;
|
||||
using Elsa.Workflows.Core.Pipelines.ActivityExecution;
|
||||
using Elsa.Workflows.Core.Pipelines.WorkflowExecution;
|
||||
using Elsa.Workflows.Core.Serialization;
|
||||
using Elsa.Workflows.Core.Serialization.Converters;
|
||||
using Elsa.Workflows.Core.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
|
|
@ -40,6 +43,16 @@ public class WorkflowsFeature : FeatureBase
|
|||
/// </summary>
|
||||
public Func<IServiceProvider, IStandardOutStreamProvider> StandardOutStreamProvider { get; set; } = _ => new StandardOutStreamProvider(Console.Out);
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to configure the <see cref="IWorkflowExecutionPipeline"/>.
|
||||
/// </summary>
|
||||
public Action<IWorkflowExecutionPipelineBuilder> WorkflowExecutionPipeline { get; set; } = builder => builder.UseDefaultActivityScheduler();
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to configure the <see cref="IActivityExecutionPipeline"/>.
|
||||
/// </summary>
|
||||
public Action<IActivityExecutionPipelineBuilder> ActivityExecutionPipeline { get; set; } = builder => builder.UseDefaultActivityInvoker();
|
||||
|
||||
/// <summary>
|
||||
/// Fluent method to set <see cref="StandardInStreamProvider"/>.
|
||||
/// </summary>
|
||||
|
|
@ -58,6 +71,24 @@ public class WorkflowsFeature : FeatureBase
|
|||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fluent method to configure the <see cref="IWorkflowExecutionPipeline"/>.
|
||||
/// </summary>
|
||||
public WorkflowsFeature WithWorkflowExecutionPipeline(Action<IWorkflowExecutionPipelineBuilder> setup)
|
||||
{
|
||||
WorkflowExecutionPipeline = setup;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fluent method to configure the <see cref="IActivityExecutionPipeline"/>.
|
||||
/// </summary>
|
||||
public WorkflowsFeature WithActivityExecutionPipeline(Action<IActivityExecutionPipelineBuilder> setup)
|
||||
{
|
||||
ActivityExecutionPipeline = setup;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Apply()
|
||||
{
|
||||
|
|
@ -78,7 +109,7 @@ public class WorkflowsFeature : FeatureBase
|
|||
.AddSingleton<IActivitySchedulerFactory, ActivitySchedulerFactory>()
|
||||
.AddSingleton<IHasher, Hasher>()
|
||||
.AddSingleton<IBookmarkHasher, BookmarkHasher>()
|
||||
.AddSingleton<IIdentityGenerator, RandomIdentityGenerator>()
|
||||
.AddSingleton<IIdentityGenerator, GuidIdentityGenerator>()
|
||||
.AddSingleton<IWorkflowExecutionContextFactory, DefaultWorkflowExecutionContextFactory>()
|
||||
.AddSingleton<IBookmarkPayloadSerializer, BookmarkPayloadSerializer>()
|
||||
.AddTransient<WorkflowBuilder>()
|
||||
|
|
@ -89,7 +120,7 @@ public class WorkflowsFeature : FeatureBase
|
|||
|
||||
// Pipelines.
|
||||
.AddSingleton<IActivityExecutionPipeline, ActivityExecutionPipeline>()
|
||||
.AddSingleton<IWorkflowExecutionPipeline, WorkflowExecutionPipeline>()
|
||||
.AddSingleton<IWorkflowExecutionPipeline>(sp => new WorkflowExecutionPipeline(sp, WorkflowExecutionPipeline))
|
||||
|
||||
// Built-in activity services.
|
||||
.AddSingleton<IActivityPortResolver, OutboundActivityPortResolver>()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
using Elsa.Workflows.Core.Services;
|
||||
|
||||
namespace Elsa.Workflows.Core.Implementations;
|
||||
|
||||
/// <summary>
|
||||
/// Generates a unique identifier using <see cref="Guid"/>.
|
||||
/// </summary>
|
||||
public class GuidIdentityGenerator : IIdentityGenerator
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string GenerateId() => Guid.NewGuid().ToString("N");
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
using Elsa.Workflows.Core.Services;
|
||||
|
||||
namespace Elsa.Workflows.Core.Implementations;
|
||||
|
||||
public class RandomIdentityGenerator : IIdentityGenerator
|
||||
{
|
||||
public string GenerateId() => Guid.NewGuid().ToString("N");
|
||||
}
|
||||
|
|
@ -7,14 +7,14 @@ using Elsa.Workflows.Core.Services;
|
|||
namespace Elsa.Workflows.Core.Middleware.Activities;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods to <see cref="IActivityExecutionBuilder"/>.
|
||||
/// Provides extension methods to <see cref="IActivityExecutionPipelineBuilder"/>.
|
||||
/// </summary>
|
||||
public static class ActivityInvokerMiddlewareExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the <see cref="DefaultActivityInvokerMiddleware"/> component to the pipeline.
|
||||
/// </summary>
|
||||
public static IActivityExecutionBuilder UseDefaultActivityInvoker(this IActivityExecutionBuilder builder) => builder.UseMiddleware<DefaultActivityInvokerMiddleware>();
|
||||
public static IActivityExecutionPipelineBuilder UseDefaultActivityInvoker(this IActivityExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.UseMiddleware<DefaultActivityInvokerMiddleware>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ public static class ExceptionHandlingMiddlewareExtensions
|
|||
/// <summary>
|
||||
/// Installs the <see cref="ExceptionHandlingMiddleware"/> component in the activity execution pipeline.
|
||||
/// </summary>
|
||||
public static IActivityExecutionBuilder UseExceptionHandling(this IActivityExecutionBuilder builder) => builder.UseMiddleware<ExceptionHandlingMiddleware>();
|
||||
public static IActivityExecutionPipelineBuilder UseExceptionHandling(this IActivityExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.UseMiddleware<ExceptionHandlingMiddleware>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -32,5 +32,5 @@ public class LoggingMiddleware : IActivityExecutionMiddleware
|
|||
|
||||
public static class LoggingMiddlewareExtensions
|
||||
{
|
||||
public static IActivityExecutionBuilder UseLogging(this IActivityExecutionBuilder builder) => builder.UseMiddleware<LoggingMiddleware>();
|
||||
public static IActivityExecutionPipelineBuilder UseLogging(this IActivityExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.UseMiddleware<LoggingMiddleware>();
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ public static class UseActivitySchedulerMiddlewareExtensions
|
|||
/// <summary>
|
||||
/// Installs middleware that executes scheduled work items (activities).
|
||||
/// </summary>
|
||||
public static IWorkflowExecutionBuilder UseDefaultActivityScheduler(this IWorkflowExecutionBuilder builder) => builder.UseMiddleware<DefaultActivitySchedulerMiddleware>();
|
||||
public static IWorkflowExecutionPipelineBuilder UseDefaultActivityScheduler(this IWorkflowExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.UseMiddleware<DefaultActivitySchedulerMiddleware>();
|
||||
}
|
||||
|
||||
public class DefaultActivitySchedulerMiddleware : WorkflowExecutionMiddleware
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System.ComponentModel;
|
|||
using System.Text.Json.Serialization;
|
||||
using Elsa.Expressions.Models;
|
||||
using Elsa.Workflows.Core.Activities;
|
||||
using Elsa.Workflows.Core.Attributes;
|
||||
using Elsa.Workflows.Core.Services;
|
||||
|
||||
namespace Elsa.Workflows.Core.Models;
|
||||
|
|
@ -10,6 +11,7 @@ namespace Elsa.Workflows.Core.Models;
|
|||
/// Represents an executable process.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
[Activity("Elsa", "Workflows", "A workflow is an activity that executes its Root activity.")]
|
||||
public class Workflow : Composite, ICloneable
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@ namespace Elsa.Workflows.Core.Pipelines.ActivityExecution;
|
|||
|
||||
public static class ActivityExecutionMiddlewareExtensions
|
||||
{
|
||||
public static IActivityExecutionBuilder UseMiddleware<TMiddleware>(this IActivityExecutionBuilder builder, params object[] args) where TMiddleware : IActivityExecutionMiddleware
|
||||
public static IActivityExecutionPipelineBuilder UseMiddleware<TMiddleware>(this IActivityExecutionPipelineBuilder pipelineBuilder, params object[] args) where TMiddleware : IActivityExecutionMiddleware
|
||||
{
|
||||
var middleware = typeof(TMiddleware);
|
||||
|
||||
return builder.Use(next =>
|
||||
return pipelineBuilder.Use(next =>
|
||||
{
|
||||
var invokeMethod = MiddlewareHelpers.GetInvokeMethod(middleware);
|
||||
var ctorArgs = new[] { next }.Concat(args).Select(x => x!).ToArray();
|
||||
var instance = ActivatorUtilities.CreateInstance(builder.ServiceProvider, middleware, ctorArgs);
|
||||
var instance = ActivatorUtilities.CreateInstance(pipelineBuilder.ServiceProvider, middleware, ctorArgs);
|
||||
return (ActivityMiddlewareDelegate)invokeMethod.CreateDelegate(typeof(ActivityMiddlewareDelegate), instance);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ public class ActivityExecutionPipeline : IActivityExecutionPipeline
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ActivityMiddlewareDelegate Setup(Action<IActivityExecutionBuilder> setup)
|
||||
public ActivityMiddlewareDelegate Setup(Action<IActivityExecutionPipelineBuilder> setup)
|
||||
{
|
||||
var builder = new ActivityExecutionPipelineBuilder(_serviceProvider);
|
||||
var builder = new ActivityExecutionPipelinePipelineBuilder(_serviceProvider);
|
||||
setup(builder);
|
||||
_pipeline = builder.Build();
|
||||
return _pipeline;
|
||||
|
|
|
|||
|
|
@ -2,18 +2,18 @@ using Elsa.Workflows.Core.Services;
|
|||
|
||||
namespace Elsa.Workflows.Core.Pipelines.ActivityExecution;
|
||||
|
||||
public class ActivityExecutionPipelineBuilder : IActivityExecutionBuilder
|
||||
public class ActivityExecutionPipelinePipelineBuilder : IActivityExecutionPipelineBuilder
|
||||
{
|
||||
private readonly IList<Func<ActivityMiddlewareDelegate, ActivityMiddlewareDelegate>> _components = new List<Func<ActivityMiddlewareDelegate, ActivityMiddlewareDelegate>>();
|
||||
|
||||
public ActivityExecutionPipelineBuilder(IServiceProvider serviceProvider)
|
||||
public ActivityExecutionPipelinePipelineBuilder(IServiceProvider serviceProvider)
|
||||
{
|
||||
ServiceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
|
||||
public IActivityExecutionBuilder Use(Func<ActivityMiddlewareDelegate, ActivityMiddlewareDelegate> middleware)
|
||||
public IActivityExecutionPipelineBuilder Use(Func<ActivityMiddlewareDelegate, ActivityMiddlewareDelegate> middleware)
|
||||
{
|
||||
_components.Add(middleware);
|
||||
return this;
|
||||
|
|
@ -3,17 +3,23 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
|
||||
namespace Elsa.Workflows.Core.Pipelines.WorkflowExecution;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extensions to <see cref="IWorkflowExecutionPipelineBuilder"/> that adds support for installing <see cref="IWorkflowExecutionMiddleware"/> components.
|
||||
/// </summary>
|
||||
public static class WorkflowExecutionMiddlewareExtensions
|
||||
{
|
||||
public static IWorkflowExecutionBuilder UseMiddleware<TMiddleware>(this IWorkflowExecutionBuilder builder, params object[] args) where TMiddleware: IWorkflowExecutionMiddleware
|
||||
/// <summary>
|
||||
/// Installs the specified middleware component into the pipeline being built.
|
||||
/// </summary>
|
||||
public static IWorkflowExecutionPipelineBuilder UseMiddleware<TMiddleware>(this IWorkflowExecutionPipelineBuilder pipelineBuilder, params object[] args) where TMiddleware: IWorkflowExecutionMiddleware
|
||||
{
|
||||
var middleware = typeof(TMiddleware);
|
||||
|
||||
return builder.Use(next =>
|
||||
return pipelineBuilder.Use(next =>
|
||||
{
|
||||
var invokeMethod = MiddlewareHelpers.GetInvokeMethod(middleware);
|
||||
var ctorParams = new[] { next }.Concat(args).Select(x => x!).ToArray();
|
||||
var instance = ActivatorUtilities.CreateInstance(builder.ApplicationServices, middleware, ctorParams);
|
||||
var ctorParams = new[] { next }.Concat(args).Select(x => x).ToArray();
|
||||
var instance = ActivatorUtilities.CreateInstance(pipelineBuilder.ServiceProvider, middleware, ctorParams);
|
||||
return (WorkflowMiddlewareDelegate)invokeMethod.CreateDelegate(typeof(WorkflowMiddlewareDelegate), instance);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,15 +4,26 @@ using Elsa.Workflows.Core.Services;
|
|||
|
||||
namespace Elsa.Workflows.Core.Pipelines.WorkflowExecution;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class WorkflowExecutionPipeline : IWorkflowExecutionPipeline
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private WorkflowMiddlewareDelegate? _pipeline;
|
||||
|
||||
public WorkflowExecutionPipeline(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider;
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public WorkflowExecutionPipeline(IServiceProvider serviceProvider, Action<IWorkflowExecutionPipelineBuilder> pipelineBuilder)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
Setup(pipelineBuilder);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public WorkflowMiddlewareDelegate Pipeline => _pipeline ??= CreateDefaultPipeline();
|
||||
|
||||
public WorkflowMiddlewareDelegate Setup(Action<IWorkflowExecutionBuilder> setup)
|
||||
/// <inheritdoc />
|
||||
public WorkflowMiddlewareDelegate Setup(Action<IWorkflowExecutionPipelineBuilder> setup)
|
||||
{
|
||||
var builder = new WorkflowExecutionPipelineBuilder(_serviceProvider);
|
||||
setup(builder);
|
||||
|
|
@ -20,6 +31,7 @@ public class WorkflowExecutionPipeline : IWorkflowExecutionPipeline
|
|||
return _pipeline;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ExecuteAsync(WorkflowExecutionContext context) => await Pipeline(context);
|
||||
|
||||
private WorkflowMiddlewareDelegate CreateDefaultPipeline() => Setup(x => x.UseDefaultActivityScheduler());
|
||||
|
|
|
|||
|
|
@ -2,30 +2,38 @@ using Elsa.Workflows.Core.Services;
|
|||
|
||||
namespace Elsa.Workflows.Core.Pipelines.WorkflowExecution;
|
||||
|
||||
public class WorkflowExecutionPipelineBuilder : IWorkflowExecutionBuilder
|
||||
/// <inheritdoc />
|
||||
public class WorkflowExecutionPipelineBuilder : IWorkflowExecutionPipelineBuilder
|
||||
{
|
||||
private const string ServicesKey = "workflow-execution.Services";
|
||||
private readonly IList<Func<WorkflowMiddlewareDelegate, WorkflowMiddlewareDelegate>> _components = new List<Func<WorkflowMiddlewareDelegate, WorkflowMiddlewareDelegate>>();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public WorkflowExecutionPipelineBuilder(IServiceProvider serviceProvider)
|
||||
{
|
||||
ApplicationServices = serviceProvider;
|
||||
ServiceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public IDictionary<string, object?> Properties { get; } = new Dictionary<string, object?>();
|
||||
/// <inheritdoc />
|
||||
public IDictionary<object, object?> Properties { get; } = new Dictionary<object, object?>();
|
||||
|
||||
public IServiceProvider ApplicationServices
|
||||
/// <inheritdoc />
|
||||
public IServiceProvider ServiceProvider
|
||||
{
|
||||
get => GetProperty<IServiceProvider>(ServicesKey)!;
|
||||
set => SetProperty(ServicesKey, value);
|
||||
}
|
||||
|
||||
public IWorkflowExecutionBuilder Use(Func<WorkflowMiddlewareDelegate, WorkflowMiddlewareDelegate> middleware)
|
||||
/// <inheritdoc />
|
||||
public IWorkflowExecutionPipelineBuilder Use(Func<WorkflowMiddlewareDelegate, WorkflowMiddlewareDelegate> middleware)
|
||||
{
|
||||
_components.Add(middleware);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public WorkflowMiddlewareDelegate Build()
|
||||
{
|
||||
WorkflowMiddlewareDelegate pipeline = _ => new ValueTask();
|
||||
|
|
@ -36,6 +44,13 @@ public class WorkflowExecutionPipelineBuilder : IWorkflowExecutionBuilder
|
|||
return pipeline;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IWorkflowExecutionPipelineBuilder Reset()
|
||||
{
|
||||
_components.Clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
private T? GetProperty<T>(string key) => Properties.TryGetValue(key, out var value) ? (T?)value : default(T);
|
||||
private void SetProperty<T>(string key, T value) => Properties[key] = value;
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ namespace Elsa.Workflows.Core.Services;
|
|||
|
||||
public interface IActivityExecutionPipeline
|
||||
{
|
||||
ActivityMiddlewareDelegate Setup(Action<IActivityExecutionBuilder> setup);
|
||||
ActivityMiddlewareDelegate Setup(Action<IActivityExecutionPipelineBuilder> setup);
|
||||
ActivityMiddlewareDelegate Pipeline { get; }
|
||||
Task ExecuteAsync(ActivityExecutionContext context);
|
||||
}
|
||||
|
|
@ -2,9 +2,9 @@ using Elsa.Workflows.Core.Pipelines.ActivityExecution;
|
|||
|
||||
namespace Elsa.Workflows.Core.Services;
|
||||
|
||||
public interface IActivityExecutionBuilder
|
||||
public interface IActivityExecutionPipelineBuilder
|
||||
{
|
||||
IServiceProvider ServiceProvider { get; }
|
||||
IActivityExecutionBuilder Use(Func<ActivityMiddlewareDelegate, ActivityMiddlewareDelegate> middleware);
|
||||
IActivityExecutionPipelineBuilder Use(Func<ActivityMiddlewareDelegate, ActivityMiddlewareDelegate> middleware);
|
||||
public ActivityMiddlewareDelegate Build();
|
||||
}
|
||||
|
|
@ -1,6 +1,12 @@
|
|||
namespace Elsa.Workflows.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a unique identity generator.
|
||||
/// </summary>
|
||||
public interface IIdentityGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates a unique identifier.
|
||||
/// </summary>
|
||||
string GenerateId();
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ using Elsa.Workflows.Core.Models;
|
|||
namespace Elsa.Workflows.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A workflow builder collects information about a workflow to be built programmatically.
|
||||
/// A workflow pipelineBuilder collects information about a workflow to be built programmatically.
|
||||
/// </summary>
|
||||
public interface IWorkflowBuilder
|
||||
{
|
||||
|
|
@ -98,7 +98,7 @@ public interface IWorkflowBuilder
|
|||
IWorkflowBuilder WithActivationStrategyType<T>() where T : IWorkflowActivationStrategy;
|
||||
|
||||
/// <summary>
|
||||
/// Build a new <see cref="Workflow"/> instance using the information collected in this builder.
|
||||
/// Build a new <see cref="Workflow"/> instance using the information collected in this pipelineBuilder.
|
||||
/// </summary>
|
||||
Workflow BuildWorkflow();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
using Elsa.Workflows.Core.Pipelines.WorkflowExecution;
|
||||
|
||||
namespace Elsa.Workflows.Core.Services;
|
||||
|
||||
public interface IWorkflowExecutionBuilder
|
||||
{
|
||||
public IDictionary<string, object?> Properties { get; }
|
||||
IServiceProvider ApplicationServices { get; }
|
||||
IWorkflowExecutionBuilder Use(Func<WorkflowMiddlewareDelegate, WorkflowMiddlewareDelegate> middleware);
|
||||
public WorkflowMiddlewareDelegate Build();
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ namespace Elsa.Workflows.Core.Services;
|
|||
|
||||
public interface IWorkflowExecutionPipeline
|
||||
{
|
||||
WorkflowMiddlewareDelegate Setup(Action<IWorkflowExecutionBuilder> setup);
|
||||
WorkflowMiddlewareDelegate Setup(Action<IWorkflowExecutionPipelineBuilder> setup);
|
||||
WorkflowMiddlewareDelegate Pipeline { get; }
|
||||
Task ExecuteAsync(WorkflowExecutionContext context);
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
using Elsa.Workflows.Core.Pipelines.WorkflowExecution;
|
||||
|
||||
namespace Elsa.Workflows.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Used to build a workflow execution pipeline.
|
||||
/// </summary>
|
||||
public interface IWorkflowExecutionPipelineBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// A general-purpose dictionary of values that can be used by middleware components.
|
||||
/// </summary>
|
||||
public IDictionary<object, object?> Properties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The current service provider to resolve services from.
|
||||
/// </summary>
|
||||
IServiceProvider ServiceProvider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Installs the specified delegate as a middleware component.
|
||||
/// </summary>
|
||||
IWorkflowExecutionPipelineBuilder Use(Func<WorkflowMiddlewareDelegate, WorkflowMiddlewareDelegate> middleware);
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the final <see cref="WorkflowMiddlewareDelegate"/> delegate that invokes each installed middleware component.
|
||||
/// </summary>
|
||||
public WorkflowMiddlewareDelegate Build();
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current pipeline.
|
||||
/// </summary>
|
||||
IWorkflowExecutionPipelineBuilder Reset();
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ using Elsa.Workflows.Core.Models;
|
|||
namespace Elsa.Workflows.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A base class for implementing workflow definitions using the builder API.
|
||||
/// A base class for implementing workflow definitions using the pipelineBuilder API.
|
||||
/// </summary>
|
||||
public abstract class WorkflowBase : IWorkflow
|
||||
{
|
||||
|
|
@ -43,7 +43,7 @@ public abstract class WorkflowBase : IWorkflow
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// A base class for implementing workflow definitions that can return a result using the builder API.
|
||||
/// A base class for implementing workflow definitions that can return a result using the pipelineBuilder API.
|
||||
/// </summary>
|
||||
public abstract class WorkflowBase<TResult> : WorkflowBase
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Elsa.Features.Services;
|
||||
using Elsa.Workflows.Core.Activities;
|
||||
using Elsa.Workflows.Core.Services;
|
||||
using Elsa.Workflows.Management.Features;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
|
|
@ -22,4 +23,14 @@ public static class ModuleExtensions
|
|||
});
|
||||
return module;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds all types implementing <see cref="IActivity"/> to the system.
|
||||
/// </summary>
|
||||
public static IModule AddActivitiesFrom<TMarkerType>(this IModule module) => module.UseWorkflowManagement(management => management.AddActivitiesFrom<TMarkerType>());
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified activity type to the system.
|
||||
/// </summary>
|
||||
public static IModule AddActivity<T>(this IModule module) where T:IActivity => module.UseWorkflowManagement(management => management.AddActivity<T>());
|
||||
}
|
||||
|
|
@ -89,12 +89,9 @@ public class WorkflowManagementFeature : FeatureBase
|
|||
/// </summary>
|
||||
public WorkflowManagementFeature AddActivitiesFrom<TMarker>()
|
||||
{
|
||||
var activityTypes = typeof(TMarker).Assembly.GetExportedTypes().Where(x =>
|
||||
{
|
||||
var browsableAttr = x.GetCustomAttribute<BrowsableAttribute>();
|
||||
var isBrowsable = browsableAttr == null || browsableAttr.Browsable;
|
||||
return typeof(IActivity).IsAssignableFrom(x) && !x.IsAbstract && !x.IsInterface && !x.IsGenericType && isBrowsable;
|
||||
}).ToList();
|
||||
var activityTypes = typeof(TMarker).Assembly.GetExportedTypes()
|
||||
.Where(x => typeof(IActivity).IsAssignableFrom(x) && !x.IsAbstract && !x.IsInterface && !x.IsGenericType)
|
||||
.ToList();
|
||||
return AddActivities(activityTypes);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,12 +10,16 @@ public class ActivityRegistryPopulator : IActivityRegistryPopulator
|
|||
private readonly IEnumerable<IActivityProvider> _providers;
|
||||
private readonly IActivityRegistry _registry;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public ActivityRegistryPopulator(IEnumerable<IActivityProvider> providers, IActivityRegistry registry)
|
||||
{
|
||||
_providers = providers;
|
||||
_registry = registry;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task PopulateRegistryAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_registry.Clear();
|
||||
|
|
@ -24,6 +28,7 @@ public class ActivityRegistryPopulator : IActivityRegistryPopulator
|
|||
await PopulateRegistryAsync(provider, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task PopulateRegistryAsync(Type providerType, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_registry.ClearProvider(providerType);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,14 @@ using Elsa.Workflows.Runtime.Features;
|
|||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Adds extensions to setup the <see cref="WorkflowRuntimeFeature"/> feature.
|
||||
/// </summary>
|
||||
public static class ModuleExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables the <see cref="WorkflowRuntimeFeature"/> feature.
|
||||
/// </summary>
|
||||
public static IModule UseWorkflowRuntime(this IModule module, Action<WorkflowRuntimeFeature>? configure = default)
|
||||
{
|
||||
module.Configure(configure);
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
using Elsa.Workflows.Core.Pipelines.WorkflowExecution;
|
||||
using Elsa.Workflows.Core.Services;
|
||||
using Elsa.Workflows.Runtime.Middleware;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Extensions;
|
||||
|
||||
public static class WorkflowExecutionPipelineBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Installs middleware that persists the workflow instance before and after workflow execution.
|
||||
/// </summary>
|
||||
public static IWorkflowExecutionBuilder UsePersistentVariables(this IWorkflowExecutionBuilder builder) => builder.UseMiddleware<PersistentVariablesMiddleware>();
|
||||
|
||||
/// <summary>
|
||||
/// Installs middleware that persists bookmarks after workflow execution.
|
||||
/// </summary>
|
||||
public static IWorkflowExecutionBuilder UseBookmarkPersistence(this IWorkflowExecutionBuilder builder) => builder.UseMiddleware<PersistBookmarkMiddleware>();
|
||||
|
||||
/// <summary>
|
||||
/// Installs middleware that persist the workflow execution journal.
|
||||
/// </summary>
|
||||
public static IWorkflowExecutionBuilder UseWorkflowExecutionLogPersistence(this IWorkflowExecutionBuilder builder) => builder.UseMiddleware<PersistWorkflowExecutionLogMiddleware>();
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
using Elsa.Features.Services;
|
||||
using Elsa.Workflows.Core.Middleware.Workflows;
|
||||
using Elsa.Workflows.Core.Pipelines.WorkflowExecution;
|
||||
using Elsa.Workflows.Core.Services;
|
||||
using Elsa.Workflows.Runtime.Middleware;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extensions to <see cref="IWorkflowExecutionPipelineBuilder"/> that add various middleware components.
|
||||
/// </summary>
|
||||
public static class WorkflowExecutionPipelineBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures the workflow execution pipeline with commonly used components.
|
||||
/// </summary>
|
||||
public static IWorkflowExecutionPipelineBuilder UseDefaultRuntimePipeline(this IWorkflowExecutionPipelineBuilder pipelineBuilder) =>
|
||||
pipelineBuilder
|
||||
.Reset()
|
||||
.UsePersistentVariables()
|
||||
.UseBookmarkPersistence()
|
||||
.UseWorkflowExecutionLogPersistence()
|
||||
.UseDefaultActivityScheduler();
|
||||
|
||||
/// <summary>
|
||||
/// Installs middleware that persists the workflow instance before and after workflow execution.
|
||||
/// </summary>
|
||||
public static IWorkflowExecutionPipelineBuilder UsePersistentVariables(this IWorkflowExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.UseMiddleware<PersistentVariablesMiddleware>();
|
||||
|
||||
/// <summary>
|
||||
/// Installs middleware that persists bookmarks after workflow execution.
|
||||
/// </summary>
|
||||
public static IWorkflowExecutionPipelineBuilder UseBookmarkPersistence(this IWorkflowExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.UseMiddleware<PersistBookmarkMiddleware>();
|
||||
|
||||
/// <summary>
|
||||
/// Installs middleware that persist the workflow execution journal.
|
||||
/// </summary>
|
||||
public static IWorkflowExecutionPipelineBuilder UseWorkflowExecutionLogPersistence(this IWorkflowExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.UseMiddleware<PersistWorkflowExecutionLogMiddleware>();
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using Elsa.Workflows.Core.Features;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Adds an extension method to the <see cref="WorkflowsFeature"/> that installs a default workflow runtime execution pipeline.
|
||||
/// </summary>
|
||||
public static class WorkflowsFeatureExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Installs a default workflow runtime execution pipeline.
|
||||
/// </summary>
|
||||
public static WorkflowsFeature WithDefaultRuntimeWorkflowExecutionPipeline(this WorkflowsFeature workflowsFeature) => workflowsFeature.WithWorkflowExecutionPipeline(pipeline => pipeline.UseDefaultRuntimePipeline());
|
||||
}
|
||||
|
|
@ -55,9 +55,24 @@ public class WorkflowRuntimeFeature : FeatureBase
|
|||
/// </summary>
|
||||
public Func<IServiceProvider, IBookmarkStore> BookmarkStore { get; set; } = sp => sp.GetRequiredService<MemoryBookmarkStore>();
|
||||
|
||||
/// <summary>
|
||||
/// A factory that instantiates an <see cref="ITriggerStore"/>.
|
||||
/// </summary>
|
||||
public Func<IServiceProvider, ITriggerStore> WorkflowTriggerStore { get; set; } = sp => sp.GetRequiredService<MemoryTriggerStore>();
|
||||
|
||||
/// <summary>
|
||||
/// A factory that instantiates an <see cref="IWorkflowExecutionLogStore"/>.
|
||||
/// </summary>
|
||||
public Func<IServiceProvider, IWorkflowExecutionLogStore> WorkflowExecutionLogStore { get; set; } = sp => sp.GetRequiredService<MemoryWorkflowExecutionLogStore>();
|
||||
|
||||
/// <summary>
|
||||
/// A factory that instantiates an <see cref="IDistributedLockProvider"/>.
|
||||
/// </summary>
|
||||
public Func<IServiceProvider, IDistributedLockProvider> DistributedLockProvider { get; set; } = _ => new FileDistributedSynchronizationProvider(new DirectoryInfo( Path.Combine(Environment.CurrentDirectory, "App_Data/locks")));
|
||||
|
||||
/// <summary>
|
||||
/// A factory that instantiates an <see cref="IWorkflowStateExporter"/>.
|
||||
/// </summary>
|
||||
public Func<IServiceProvider, IWorkflowStateExporter> WorkflowStateExporter { get; set; } = sp => sp.GetRequiredService<NoopWorkflowStateExporter>();
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -36,10 +36,12 @@ public class AsyncWorkflowStateExporter : IWorkflowStateExporter, ICommandHandle
|
|||
_workflowInstanceStore = workflowInstanceStore;
|
||||
_systemClock = systemClock;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ExportAsync(Workflow workflow, WorkflowState workflowState, CancellationToken cancellationToken) =>
|
||||
await _backgroundCommandSender.SendAsync(new ExportWorkflowStateToDb(workflowState), cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Unit> HandleAsync(ExportWorkflowStateToDb command, CancellationToken cancellationToken)
|
||||
{
|
||||
var workflowState = command.WorkflowState;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ using Elsa.Workflows.Runtime.Services;
|
|||
namespace Elsa.Workflows.Runtime.Middleware;
|
||||
|
||||
/// <summary>
|
||||
/// Takes care of loading & persisting workflow variables.
|
||||
/// Takes care of loading and persisting bookmarks.
|
||||
/// </summary>
|
||||
public class PersistBookmarkMiddleware : WorkflowExecutionMiddleware
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,12 +14,14 @@ public class PersistWorkflowExecutionLogMiddleware : WorkflowExecutionMiddleware
|
|||
private readonly IWorkflowExecutionLogStore _workflowExecutionLogStore;
|
||||
private readonly IIdentityGenerator _identityGenerator;
|
||||
|
||||
/// <inheritdoc />
|
||||
public PersistWorkflowExecutionLogMiddleware(WorkflowMiddlewareDelegate next, IWorkflowExecutionLogStore workflowExecutionLogStore, IIdentityGenerator identityGenerator) : base(next)
|
||||
{
|
||||
_workflowExecutionLogStore = workflowExecutionLogStore;
|
||||
_identityGenerator = identityGenerator;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async ValueTask InvokeAsync(WorkflowExecutionContext context)
|
||||
{
|
||||
// Invoke next middleware.
|
||||
|
|
|
|||
|
|
@ -22,8 +22,11 @@ var services = builder.Services;
|
|||
var configuration = builder.Configuration;
|
||||
var sqliteConnectionString = configuration.GetConnectionString("Sqlite")!;
|
||||
var identityOptions = new IdentityOptions();
|
||||
var identityTokenOptions = new IdentityTokenOptions();
|
||||
var identitySection = configuration.GetSection("Identity");
|
||||
var identityTokenSection = identitySection.GetSection("Tokens");
|
||||
identitySection.Bind(identityOptions);
|
||||
identityTokenSection.Bind(identityTokenOptions);
|
||||
|
||||
// Add Elsa services.
|
||||
services
|
||||
|
|
@ -47,8 +50,8 @@ services
|
|||
)
|
||||
.UseIdentity(identity =>
|
||||
{
|
||||
identity.CreateDefaultUser = true;
|
||||
identity.IdentityOptions = identityOptions;
|
||||
identity.TokenOptions = identityTokenOptions;
|
||||
})
|
||||
.UseWorkflowRuntime(runtime =>
|
||||
{
|
||||
|
|
@ -76,7 +79,7 @@ services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.AllowAnyHeader()
|
|||
// Authentication & Authorization.
|
||||
services
|
||||
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, identityOptions.ConfigureJwtBearerOptions);
|
||||
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, identityTokenOptions.ConfigureJwtBearerOptions);
|
||||
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddSingleton<IAuthorizationHandler, LocalHostRequirementHandler>();
|
||||
|
|
@ -88,15 +91,6 @@ services.AddAuthorization(options => options.AddPolicy(IdentityPolicyNames.Secur
|
|||
var app = builder.Build();
|
||||
var serviceProvider = app.Services;
|
||||
|
||||
// Configure workflow engine execution pipeline.
|
||||
serviceProvider.ConfigureDefaultWorkflowExecutionPipeline(pipeline =>
|
||||
pipeline
|
||||
.UsePersistentVariables()
|
||||
.UseBookmarkPersistence()
|
||||
.UseWorkflowContexts()
|
||||
.UseDefaultActivityScheduler()
|
||||
);
|
||||
|
||||
// Configure activity execution pipeline to use the job-based activity invoker.
|
||||
serviceProvider.ConfigureDefaultActivityExecutionPipeline(pipeline => pipeline.UseJobBasedActivityInvoker());
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@
|
|||
"Sqlite": "Data Source=elsa.sqlite.db;Cache=Shared;"
|
||||
},
|
||||
"Identity": {
|
||||
"SigningKey": "secret-signing-key"
|
||||
"CreateDefaultAdmin": true,
|
||||
"Tokens": {
|
||||
"SigningKey": "secret-signing-key"
|
||||
}
|
||||
},
|
||||
"Telnyx": {
|
||||
"ApiKey": "",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ builder.Services.AddElsa(elsa =>
|
|||
// Configure identity so that we can create a default admin user.
|
||||
elsa.UseIdentity(identity =>
|
||||
{
|
||||
identity.CreateDefaultUser = builder.Environment.IsDevelopment();
|
||||
identity.IdentityOptions.SigningKey = "secret-token-signing-key";
|
||||
identity.IdentityOptions.CreateDefaultAdmin = builder.Environment.IsDevelopment();
|
||||
identity.TokenOptions.SigningKey = "secret-token-signing-key";
|
||||
});
|
||||
|
||||
// Use default authentication (JWT).
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ builder.Services.AddElsa(elsa =>
|
|||
// Configure identity so that we can create a default admin user.
|
||||
elsa.UseIdentity(identity =>
|
||||
{
|
||||
identity.CreateDefaultUser = builder.Environment.IsDevelopment();
|
||||
identity.IdentityOptions.SigningKey = "secret-token-signing-key";
|
||||
identity.IdentityOptions.CreateDefaultAdmin = builder.Environment.IsDevelopment();
|
||||
identity.TokenOptions.SigningKey = "secret-token-signing-key";
|
||||
});
|
||||
|
||||
// Use default authentication (JWT).
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\bundles\Elsa\Elsa.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.EntityFrameworkCore.Sqlite\Elsa.EntityFrameworkCore.Sqlite.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.Http\Elsa.Http.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.Identity\Elsa.Identity.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.JavaScript\Elsa.JavaScript.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.Liquid\Elsa.Liquid.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.Workflows.Api\Elsa.Workflows.Api.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
using Elsa.EntityFrameworkCore.Extensions;
|
||||
using Elsa.EntityFrameworkCore.Modules.ActivityDefinitions;
|
||||
using Elsa.EntityFrameworkCore.Modules.Labels;
|
||||
using Elsa.EntityFrameworkCore.Modules.Runtime;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Identity;
|
||||
using Elsa.Identity.Options;
|
||||
using Elsa.Requirements;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
var services = builder.Services;
|
||||
var configuration = builder.Configuration;
|
||||
var sqliteConnectionString = configuration.GetConnectionString("Sqlite")!;
|
||||
var identityOptions = new IdentityOptions();
|
||||
var identitySection = configuration.GetSection("Identity");
|
||||
identitySection.Bind(identityOptions);
|
||||
|
||||
// Add Elsa services.
|
||||
services
|
||||
.AddElsa(elsa => elsa
|
||||
.UseWorkflows()
|
||||
.UseWorkflowsApi()
|
||||
.UseIdentity(identity =>
|
||||
{
|
||||
identity.CreateDefaultUser = true;
|
||||
identity.IdentityOptions = identityOptions;
|
||||
})
|
||||
.UseDefaultAuthentication()
|
||||
.UseWorkflowRuntime(runtime => { runtime.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString)); })
|
||||
.UseLabels(labels => labels.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString)))
|
||||
.UseActivityDefinitions(feature => feature.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString)))
|
||||
.UseJavaScript()
|
||||
.UseLiquid()
|
||||
.UseHttp()
|
||||
);
|
||||
|
||||
services.AddHealthChecks();
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddSingleton<IAuthorizationHandler, LocalHostRequirementHandler>();
|
||||
services.AddAuthorization(options => options.AddPolicy(IdentityPolicyNames.SecurityRoot, policy => policy.AddRequirements(new LocalHostRequirement())));
|
||||
|
||||
// Razor Pages.
|
||||
services.AddRazorPages();
|
||||
|
||||
// Configure middleware pipeline.
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Error");
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.UseStaticFiles();
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseWorkflowsApi();
|
||||
app.UseWorkflows();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapFallbackToPage("/Index");
|
||||
});
|
||||
|
||||
app.MapRazorPages();
|
||||
app.Run();
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:20241",
|
||||
"sslPort": 44391
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5118",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7113;http://localhost:5118",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
Loading…
Reference in a new issue