From 22e19545fe5ef0b4ddd2ca746ca23d26e0d2f9e9 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 22 Apr 2021 22:20:38 +0200 Subject: [PATCH] Add workflow registry screen component and link from definition to instances --- Elsa.sln | 8 ++ .../elsa-workflows-studio/src/components.d.ts | 21 ++++ .../elsa-studio-root/elsa-studio-root.tsx | 2 +- .../elsa-studio-workflow-registry.tsx | 48 +------- .../elsa-workflow-definitions-list-screen.tsx | 12 +- .../elsa-workflow-registry-list-screen.tsx | 111 ++++++++++++++++++ .../Startup.cs | 7 ++ .../Workflows/HeartbeatWorkflow.cs | 21 ++++ 8 files changed, 180 insertions(+), 50 deletions(-) create mode 100644 src/designer/elsa-workflows-studio/src/components/screens/workflow-registry-list/elsa-workflow-registry-list-screen/elsa-workflow-registry-list-screen.tsx create mode 100644 src/samples/dashboard/aspnetcore/ElsaDashboard.Samples.AspNetCore.Monolith/Workflows/HeartbeatWorkflow.cs diff --git a/Elsa.sln b/Elsa.sln index 646bcd726..ee3757e95 100644 --- a/Elsa.sln +++ b/Elsa.sln @@ -261,6 +261,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.Persistence.Ye EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Designer.Components.Web", "src\designer\bindings\aspnet\Elsa.Designer.Components.Web\Elsa.Designer.Components.Web.csproj", "{3DF05CFC-AC77-489F-9A42-C4FDB599AA69}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docker", "docker", "{D7F75477-D477-4899-BA3B-67D6B56515B0}" +ProjectSection(SolutionItems) = preProject + docker\docker-compose.yaml = docker\docker-compose.yaml + docker\Dockerfile = docker\Dockerfile + docker\Dockerfile-with-npm = docker\Dockerfile-with-npm + docker\README.md = docker\README.md +EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/src/designer/elsa-workflows-studio/src/components.d.ts b/src/designer/elsa-workflows-studio/src/components.d.ts index d16d85e97..49d8d45a6 100644 --- a/src/designer/elsa-workflows-studio/src/components.d.ts +++ b/src/designer/elsa-workflows-studio/src/components.d.ts @@ -150,6 +150,8 @@ export namespace Components { "serverUrl": string; } interface ElsaStudioWorkflowRegistry { + "history": RouterHistory; + "serverUrl": string; } interface ElsaSwitchCasesProperty { "propertyDescriptor": ActivityPropertyDescriptor; @@ -197,6 +199,10 @@ export namespace Components { "publishing": boolean; "workflowDefinition": WorkflowDefinition; } + interface ElsaWorkflowRegistryListScreen { + "history"?: RouterHistory; + "serverUrl": string; + } interface ElsaWorkflowSettingsModal { "workflowDefinition": WorkflowDefinition; } @@ -418,6 +424,12 @@ declare global { prototype: HTMLElsaWorkflowPublishButtonElement; new (): HTMLElsaWorkflowPublishButtonElement; }; + interface HTMLElsaWorkflowRegistryListScreenElement extends Components.ElsaWorkflowRegistryListScreen, HTMLStencilElement { + } + var HTMLElsaWorkflowRegistryListScreenElement: { + prototype: HTMLElsaWorkflowRegistryListScreenElement; + new (): HTMLElsaWorkflowRegistryListScreenElement; + }; interface HTMLElsaWorkflowSettingsModalElement extends Components.ElsaWorkflowSettingsModal, HTMLStencilElement { } var HTMLElsaWorkflowSettingsModalElement: { @@ -461,6 +473,7 @@ declare global { "elsa-workflow-instance-viewer-screen": HTMLElsaWorkflowInstanceViewerScreenElement; "elsa-workflow-instances-list-screen": HTMLElsaWorkflowInstancesListScreenElement; "elsa-workflow-publish-button": HTMLElsaWorkflowPublishButtonElement; + "elsa-workflow-registry-list-screen": HTMLElsaWorkflowRegistryListScreenElement; "elsa-workflow-settings-modal": HTMLElsaWorkflowSettingsModalElement; } } @@ -608,6 +621,8 @@ declare namespace LocalJSX { "serverUrl"?: string; } interface ElsaStudioWorkflowRegistry { + "history"?: RouterHistory; + "serverUrl"?: string; } interface ElsaSwitchCasesProperty { "propertyDescriptor"?: ActivityPropertyDescriptor; @@ -651,6 +666,10 @@ declare namespace LocalJSX { "publishing"?: boolean; "workflowDefinition"?: WorkflowDefinition; } + interface ElsaWorkflowRegistryListScreen { + "history"?: RouterHistory; + "serverUrl"?: string; + } interface ElsaWorkflowSettingsModal { "workflowDefinition"?: WorkflowDefinition; } @@ -691,6 +710,7 @@ declare namespace LocalJSX { "elsa-workflow-instance-viewer-screen": ElsaWorkflowInstanceViewerScreen; "elsa-workflow-instances-list-screen": ElsaWorkflowInstancesListScreen; "elsa-workflow-publish-button": ElsaWorkflowPublishButton; + "elsa-workflow-registry-list-screen": ElsaWorkflowRegistryListScreen; "elsa-workflow-settings-modal": ElsaWorkflowSettingsModal; } } @@ -734,6 +754,7 @@ declare module "@stencil/core" { "elsa-workflow-instance-viewer-screen": LocalJSX.ElsaWorkflowInstanceViewerScreen & JSXBase.HTMLAttributes; "elsa-workflow-instances-list-screen": LocalJSX.ElsaWorkflowInstancesListScreen & JSXBase.HTMLAttributes; "elsa-workflow-publish-button": LocalJSX.ElsaWorkflowPublishButton & JSXBase.HTMLAttributes; + "elsa-workflow-registry-list-screen": LocalJSX.ElsaWorkflowRegistryListScreen & JSXBase.HTMLAttributes; "elsa-workflow-settings-modal": LocalJSX.ElsaWorkflowSettingsModal & JSXBase.HTMLAttributes; } } diff --git a/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-root/elsa-studio-root.tsx b/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-root/elsa-studio-root.tsx index 61ae2ad8b..810a6b458 100644 --- a/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-root/elsa-studio-root.tsx +++ b/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-root/elsa-studio-root.tsx @@ -47,7 +47,7 @@ export class ElsaStudioRoot { } exact={true}/> - + diff --git a/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-workflow-registry/elsa-studio-workflow-registry.tsx b/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-workflow-registry/elsa-studio-workflow-registry.tsx index 261b80b83..978db762f 100644 --- a/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-workflow-registry/elsa-studio-workflow-registry.tsx +++ b/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-workflow-registry/elsa-studio-workflow-registry.tsx @@ -1,10 +1,14 @@ -import {Component, h} from '@stencil/core'; +import {Component, h, Prop} from '@stencil/core'; +import {RouterHistory} from "@stencil/router"; @Component({ tag: 'elsa-studio-workflow-registry', shadow: false, }) export class ElsaStudioWorkflowRegistry { + @Prop() history: RouterHistory; + @Prop() serverUrl: string; + render() { return (
@@ -21,47 +25,7 @@ export class ElsaStudioWorkflowRegistry {
-
- - - - - - - - - - - - - - - - - - - -
Name - Instances - -
- - -
-
-
4 - 999 - 75
-
-
-
- -
-
+ ); } diff --git a/src/designer/elsa-workflows-studio/src/components/screens/workflow-definition-list/elsa-workflow-definitions-screen/elsa-workflow-definitions-list-screen.tsx b/src/designer/elsa-workflows-studio/src/components/screens/workflow-definition-list/elsa-workflow-definitions-screen/elsa-workflow-definitions-list-screen.tsx index c0e8ff8c5..ba1d04ff8 100644 --- a/src/designer/elsa-workflows-studio/src/components/screens/workflow-definition-list/elsa-workflow-definitions-screen/elsa-workflow-definitions-list-screen.tsx +++ b/src/designer/elsa-workflows-studio/src/components/screens/workflow-definition-list/elsa-workflow-definitions-screen/elsa-workflow-definitions-list-screen.tsx @@ -70,7 +70,7 @@ export class ElsaWorkflowDefinitionsListScreen { {collection.map(groupings, group => { const versions = collection.orderBy(group, 'version', 'desc'); - const workflowDefinition = versions[0] + const workflowDefinition: WorkflowDefinitionSummary = versions[0]; const latestVersionNumber = workflowDefinition.version; const publishedVersion: WorkflowDefinitionSummary = versions.find(x => x.isPublished); const publishedVersionNumber = !!publishedVersion ? publishedVersion.version : '-'; @@ -83,6 +83,7 @@ export class ElsaWorkflowDefinitionsListScreen { workflowDisplayName = 'Untitled'; const editUrl = `/workflow-definitions/${workflowDefinition.definitionId}`; + const instancesUrl = `/workflow-instances?workflow=${workflowDefinition.definitionId}`; const editIcon = ( @@ -111,14 +112,11 @@ export class ElsaWorkflowDefinitionsListScreen { -
-
-
4 - 999 - 75
-
+
+ Instances
+ {latestVersionNumber} {publishedVersionNumber} diff --git a/src/designer/elsa-workflows-studio/src/components/screens/workflow-registry-list/elsa-workflow-registry-list-screen/elsa-workflow-registry-list-screen.tsx b/src/designer/elsa-workflows-studio/src/components/screens/workflow-registry-list/elsa-workflow-registry-list-screen/elsa-workflow-registry-list-screen.tsx new file mode 100644 index 000000000..b6af33ff0 --- /dev/null +++ b/src/designer/elsa-workflows-studio/src/components/screens/workflow-registry-list/elsa-workflow-registry-list-screen/elsa-workflow-registry-list-screen.tsx @@ -0,0 +1,111 @@ +import {Component, h, Prop, State} from '@stencil/core'; +import * as collection from 'lodash/collection'; +import {createElsaClient} from "../../../../services/elsa-client"; +import {PagedList, VersionOptions, WorkflowBlueprintSummary} from "../../../../models"; +import {RouterHistory} from "@stencil/router"; + +@Component({ + tag: 'elsa-workflow-registry-list-screen', + shadow: false, +}) +export class ElsaWorkflowRegistryListScreen { + @Prop() history?: RouterHistory; + @Prop() serverUrl: string; + @State() workflowBlueprints: PagedList = {items: [], page: 1, pageSize: 50, totalCount: 0}; + + async componentWillLoad() { + await this.loadWorkflowBlueprints(); + } + + async loadWorkflowBlueprints() { + const elsaClient = this.createClient(); + const page = 0; + const pageSize = 50; + const versionOptions: VersionOptions = {allVersions: true}; + this.workflowBlueprints = await elsaClient.workflowRegistryApi.list(page, pageSize, versionOptions); + } + + createClient() { + return createElsaClient(this.serverUrl); + } + + render() { + const workflowBlueprints = this.workflowBlueprints.items; + const groupings = collection.groupBy(workflowBlueprints, 'id'); + + return ( +
+
+ + + + + + + + + + + {collection.map(groupings, group => { + const versions = collection.orderBy(group, 'version', 'desc'); + const workflowBlueprint: WorkflowBlueprintSummary = versions[0]; + const latestVersionNumber = workflowBlueprint.version; + const publishedVersion: WorkflowBlueprintSummary = versions.find(x => x.isPublished); + const publishedVersionNumber = !!publishedVersion ? publishedVersion.version : '-'; + let workflowDisplayName = workflowBlueprint.displayName; + + if (!workflowDisplayName || workflowDisplayName.trim().length == 0) + workflowDisplayName = workflowBlueprint.name; + + if (!workflowDisplayName || workflowDisplayName.trim().length == 0) + workflowDisplayName = 'Untitled'; + + const editUrl = `/workflow-definitions/${workflowBlueprint.id}`; + const instancesUrl = `/workflow-instances?workflow=${workflowBlueprint.id}`; + + const editIcon = ( + + + + + ); + + return ( + + + + + + + + + + ); + })} + +
Name + Instances + +
+
+ {workflowDisplayName} +
+
+
+ Instances +
+
+ +
+
+ +
+ ); + } +} diff --git a/src/samples/dashboard/aspnetcore/ElsaDashboard.Samples.AspNetCore.Monolith/Startup.cs b/src/samples/dashboard/aspnetcore/ElsaDashboard.Samples.AspNetCore.Monolith/Startup.cs index a754594de..5a55402ad 100644 --- a/src/samples/dashboard/aspnetcore/ElsaDashboard.Samples.AspNetCore.Monolith/Startup.cs +++ b/src/samples/dashboard/aspnetcore/ElsaDashboard.Samples.AspNetCore.Monolith/Startup.cs @@ -1,6 +1,7 @@ using Elsa; using Elsa.Persistence.EntityFramework.Core.Extensions; using Elsa.Persistence.EntityFramework.Sqlite; +using ElsaDashboard.Samples.AspNetCore.Monolith.Workflows; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; @@ -34,10 +35,15 @@ namespace ElsaDashboard.Samples.AspNetCore.Monolith .AddEmailActivities(elsaSection.GetSection("Smtp").Bind) .AddQuartzTemporalActivities() .AddJavaScriptActivities() + .AddWorkflow() ); services .AddElsaApiEndpoints(); + + // Allow arbitrary client browser apps to access the API for demo purposes only. + // In a production environment, make sure to allow only origins you trust. + services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().WithExposedHeaders("Content-Disposition"))); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -56,6 +62,7 @@ namespace ElsaDashboard.Samples.AspNetCore.Monolith app.UseStaticFiles(); app.UseHttpActivities(); + app.UseCors(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => diff --git a/src/samples/dashboard/aspnetcore/ElsaDashboard.Samples.AspNetCore.Monolith/Workflows/HeartbeatWorkflow.cs b/src/samples/dashboard/aspnetcore/ElsaDashboard.Samples.AspNetCore.Monolith/Workflows/HeartbeatWorkflow.cs new file mode 100644 index 000000000..5d6d008fe --- /dev/null +++ b/src/samples/dashboard/aspnetcore/ElsaDashboard.Samples.AspNetCore.Monolith/Workflows/HeartbeatWorkflow.cs @@ -0,0 +1,21 @@ +using Elsa.Activities.Console; +using Elsa.Activities.Temporal; +using Elsa.Builders; +using NodaTime; + +namespace ElsaDashboard.Samples.AspNetCore.Monolith.Workflows +{ + public class HeartbeatWorkflow : IWorkflow + { + private readonly IClock _clock; + public HeartbeatWorkflow(IClock clock) => _clock = clock; + + public void Build(IWorkflowBuilder builder) + { + builder + .WithDisplayName("Timer") + .Timer(Duration.FromSeconds(10)) + .WriteLine(() => $"Heartbeat at {_clock.GetCurrentInstant()}"); + } + } +} \ No newline at end of file