Use as Json Schema implementation
This commit is contained in:
parent
08235ce8d4
commit
b11ccbbc2d
29
Elsa.sln
29
Elsa.sln
|
|
@ -340,10 +340,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.WorkflowSettings.Persi
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.WorkflowSettings.Persistence.EntityFramework.PostgreSql", "src\modules\workflowsettings\Elsa.WorkflowSettings.Persistence.EntityFramework.PostgreSql\Elsa.WorkflowSettings.Persistence.EntityFramework.PostgreSql.csproj", "{F006F3CC-22E9-4FDD-A8D4-2E8CBDFDA54A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElsaDashboard.Samples.AspNetCore.SubPath", "src\samples\dashboard\aspnetcore\ElsaDashboard.Samples.AspNetCore.SubPath\ElsaDashboard.Samples.AspNetCore.SubPath.csproj", "{BB715C0C-BE88-4F7F-A43F-1F8DFC9A1388}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.MassTransitRabbitMq", "src\samples\aspnet\Elsa.Samples.MassTransitRabbitMq\Elsa.Samples.MassTransitRabbitMq.csproj", "{6BDC89F4-2E0A-4534-9BE4-10E771D06C93}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Webhooks.Api", "src\activities\webhooks\Elsa.Webhooks.Api\Elsa.Webhooks.Api.csproj", "{9D4B7BD1-AC36-4C94-9770-C42E4030E699}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.WorkflowSettings.Api", "src\modules\workflowsettings\Elsa.WorkflowSettings.Api\Elsa.WorkflowSettings.Api.csproj", "{7595784C-97DA-4C75-BCBC-D99C30E63973}"
|
||||
|
|
@ -362,8 +358,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElsaDashboard.Samples.Blazo
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.HttpEndpointSecurity", "src\samples\aspnet\Elsa.Samples.HttpEndpointSecurity\Elsa.Samples.HttpEndpointSecurity.csproj", "{82B115DA-E3D0-49D7-AD08-DE9387656756}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.WatchDirectoryWorker", "src\samples\worker\Elsa.Samples.WatchDirectoryWorker\Elsa.Samples.WatchDirectoryWorker.csproj", "{8EC3CF51-EBC1-4B45-881E-4B1B8190D579}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.InfiniteLoopDetection", "src\samples\aspnet\Elsa.Samples.InfiniteLoopDetection\Elsa.Samples.InfiniteLoopDetection.csproj", "{A2572DBF-AF11-4A02-AF99-B343871F2D85}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.WatchDirectoryWorker", "src\samples\worker\Elsa.Samples.WatchDirectoryWorker\Elsa.Samples.WatchDirectoryWorker.csproj", "{8EC3CF51-EBC1-4B45-881E-4B1B8190D579}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.InfiniteLoopDetection", "src\samples\aspnet\Elsa.Samples.InfiniteLoopDetection\Elsa.Samples.InfiniteLoopDetection.csproj", "{A2572DBF-AF11-4A02-AF99-B343871F2D85}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
|
@ -775,6 +772,14 @@ Global
|
|||
{6E191628-927B-42FB-8805-D2977D8FC4E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6E191628-927B-42FB-8805-D2977D8FC4E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6E191628-927B-42FB-8805-D2977D8FC4E3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BB715C0C-BE88-4F7F-A43F-1F8DFC9A1388}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BB715C0C-BE88-4F7F-A43F-1F8DFC9A1388}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BB715C0C-BE88-4F7F-A43F-1F8DFC9A1388}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BB715C0C-BE88-4F7F-A43F-1F8DFC9A1388}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6BDC89F4-2E0A-4534-9BE4-10E771D06C93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6BDC89F4-2E0A-4534-9BE4-10E771D06C93}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6BDC89F4-2E0A-4534-9BE4-10E771D06C93}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6BDC89F4-2E0A-4534-9BE4-10E771D06C93}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AB926BCA-B73E-463D-8549-84D3D124F089}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AB926BCA-B73E-463D-8549-84D3D124F089}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AB926BCA-B73E-463D-8549-84D3D124F089}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
|
@ -807,14 +812,6 @@ Global
|
|||
{F006F3CC-22E9-4FDD-A8D4-2E8CBDFDA54A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F006F3CC-22E9-4FDD-A8D4-2E8CBDFDA54A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F006F3CC-22E9-4FDD-A8D4-2E8CBDFDA54A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BB715C0C-BE88-4F7F-A43F-1F8DFC9A1388}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BB715C0C-BE88-4F7F-A43F-1F8DFC9A1388}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BB715C0C-BE88-4F7F-A43F-1F8DFC9A1388}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BB715C0C-BE88-4F7F-A43F-1F8DFC9A1388}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6BDC89F4-2E0A-4534-9BE4-10E771D06C93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6BDC89F4-2E0A-4534-9BE4-10E771D06C93}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6BDC89F4-2E0A-4534-9BE4-10E771D06C93}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6BDC89F4-2E0A-4534-9BE4-10E771D06C93}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9D4B7BD1-AC36-4C94-9770-C42E4030E699}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9D4B7BD1-AC36-4C94-9770-C42E4030E699}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9D4B7BD1-AC36-4C94-9770-C42E4030E699}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
|
@ -997,6 +994,8 @@ Global
|
|||
{4D4022DD-244B-47EE-95FA-7071EC687B24} = {B43B546E-23F3-46E8-ACB7-D04F05CDA180}
|
||||
{39DE1EE2-DF4F-44EB-9F5A-551BEADD6673} = {4673732F-2853-47BD-91B8-C95C229D2C89}
|
||||
{6E191628-927B-42FB-8805-D2977D8FC4E3} = {22E75696-6FE9-436A-9097-EE21C603F818}
|
||||
{BB715C0C-BE88-4F7F-A43F-1F8DFC9A1388} = {39DE1EE2-DF4F-44EB-9F5A-551BEADD6673}
|
||||
{6BDC89F4-2E0A-4534-9BE4-10E771D06C93} = {22E75696-6FE9-436A-9097-EE21C603F818}
|
||||
{69BB424A-AAA3-415C-9651-9F4349CA3AC5} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925}
|
||||
{513B0FAD-F92F-4C7B-B756-D735F8C3C5E1} = {69BB424A-AAA3-415C-9651-9F4349CA3AC5}
|
||||
{AB926BCA-B73E-463D-8549-84D3D124F089} = {513B0FAD-F92F-4C7B-B756-D735F8C3C5E1}
|
||||
|
|
@ -1007,8 +1006,6 @@ Global
|
|||
{770456A5-547D-43AE-AB00-CF17BA799AF7} = {513B0FAD-F92F-4C7B-B756-D735F8C3C5E1}
|
||||
{F51C54C3-7C68-41E9-BC05-798589740F39} = {513B0FAD-F92F-4C7B-B756-D735F8C3C5E1}
|
||||
{F006F3CC-22E9-4FDD-A8D4-2E8CBDFDA54A} = {513B0FAD-F92F-4C7B-B756-D735F8C3C5E1}
|
||||
{BB715C0C-BE88-4F7F-A43F-1F8DFC9A1388} = {39DE1EE2-DF4F-44EB-9F5A-551BEADD6673}
|
||||
{6BDC89F4-2E0A-4534-9BE4-10E771D06C93} = {22E75696-6FE9-436A-9097-EE21C603F818}
|
||||
{9D4B7BD1-AC36-4C94-9770-C42E4030E699} = {90EC2D29-DE36-40AF-BDA7-70FFD538B8CA}
|
||||
{7595784C-97DA-4C75-BCBC-D99C30E63973} = {513B0FAD-F92F-4C7B-B756-D735F8C3C5E1}
|
||||
{6242E81F-688E-4098-948A-F0C59593328A} = {B43B546E-23F3-46E8-ACB7-D04F05CDA180}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
using Elsa.Models;
|
||||
|
||||
namespace Elsa.Events
|
||||
{
|
||||
public class WorkflowDefinitionUpdated : WorkflowDefinitionNotification
|
||||
{
|
||||
public WorkflowDefinitionUpdated(WorkflowDefinition workflowDefinition) : base(workflowDefinition)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ namespace Elsa.Services
|
|||
Task<WorkflowDefinition?> RetractAsync(string workflowDefinitionId, CancellationToken cancellationToken = default);
|
||||
Task<WorkflowDefinition> RetractAsync(WorkflowDefinition workflowDefinition, CancellationToken cancellationToken = default);
|
||||
Task<WorkflowDefinition?> GetDraftAsync(string workflowDefinitionId, CancellationToken cancellationToken= default);
|
||||
Task<WorkflowDefinition?> GetAsync(string workflowDefinitionId, int version, CancellationToken cancellationToken = default);
|
||||
Task<WorkflowDefinition> SaveDraftAsync(WorkflowDefinition workflowDefinition, CancellationToken cancellationToken = default);
|
||||
Task DeleteAsync(string workflowDefinitionId, CancellationToken cancellationToken = default);
|
||||
Task DeleteAsync(WorkflowDefinition workflowDefinition, CancellationToken cancellationToken = default);
|
||||
|
|
|
|||
|
|
@ -150,7 +150,8 @@ namespace Elsa.Services.Workflows
|
|||
// Acquire a lock on correlation ID to prevent duplicate workflow instances from being created.
|
||||
await using var correlationLockHandle = await AcquireLockAsync(correlationId, cancellationToken);
|
||||
|
||||
return await CollectStartableWorkflowInternalAsync(workflowBlueprint, activityId, correlationId, contextId, tenantId, cancellationToken);
|
||||
var startableWorkflowDefinition = await CollectStartableWorkflowInternalAsync(workflowBlueprint, activityId, correlationId, contextId, tenantId, cancellationToken);
|
||||
return startableWorkflowDefinition != null ? await InstantiateStartableWorkflow(startableWorkflowDefinition, cancellationToken) : default;
|
||||
}
|
||||
|
||||
public async Task FindAndExecuteStartableWorkflowAsync(
|
||||
|
|
|
|||
|
|
@ -135,6 +135,16 @@ namespace Elsa.Services.Workflows
|
|||
return draft;
|
||||
}
|
||||
|
||||
public async Task<WorkflowDefinition?> GetAsync(string workflowDefinitionId, int version, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var definition = await _workflowDefinitionStore.FindByDefinitionIdAsync(
|
||||
workflowDefinitionId,
|
||||
VersionOptions.SpecificVersion(version),
|
||||
cancellationToken);
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
public async Task<WorkflowDefinition> SaveDraftAsync(WorkflowDefinition workflowDefinition, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var draft = workflowDefinition;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import {Component, Event, EventEmitter, h, Host, Method, Prop, State, Watch} from '@stencil/core';
|
||||
import {Component, Prop, Event, EventEmitter, h, Method, State, Watch, Host} from '@stencil/core';
|
||||
import {HubConnection, HubConnectionBuilder} from '@microsoft/signalr';
|
||||
import {enter, leave} from "el-transition"
|
||||
import * as collection from 'lodash/collection';
|
||||
import {WorkflowDefinition, WorkflowDefinitionSummary, WorkflowTestActivityMessage} from "../../../../models";
|
||||
import {i18n} from "i18next";
|
||||
import {loadTranslations} from "../../../i18n/i18n-loader";
|
||||
import {resources} from "./localizations";
|
||||
import {createElsaClient} from "../../../../services";
|
||||
import {createElsaClient, WorkflowTestExecuteRequest, WorkflowTestSaveRequest} from "../../../../services";
|
||||
import Tunnel from "../../../../data/dashboard";
|
||||
import { isJSDocThisTag } from 'typescript';
|
||||
|
||||
interface Tab {
|
||||
id: string;
|
||||
|
|
@ -49,8 +49,6 @@ export class ElsaWorkflowPropertiesPanel {
|
|||
tabs: Array<Tab> = [];
|
||||
testActivity: WorkflowTestActivityMessage;
|
||||
|
||||
t = (key: string) => this.i18next.t(key);
|
||||
|
||||
@Method()
|
||||
async selectTestActivity(message?: WorkflowTestActivityMessage) {
|
||||
const messageInternal = !!message ? message : null;
|
||||
|
|
@ -81,7 +79,7 @@ export class ElsaWorkflowPropertiesPanel {
|
|||
});
|
||||
|
||||
this.hubConnection.on('DispatchMessage', (message) => {
|
||||
debugger
|
||||
message.data = JSON.parse(message.data);
|
||||
this.testActivityMessageReceived.emit(message);
|
||||
});
|
||||
|
||||
|
|
@ -92,23 +90,40 @@ export class ElsaWorkflowPropertiesPanel {
|
|||
|
||||
onTabClick(e: Event, tab: Tab) {
|
||||
e.preventDefault();
|
||||
|
||||
this.selectedTabId = tab.id;
|
||||
}
|
||||
|
||||
async onExecuteWorkflowClick() {
|
||||
this.testActivityMessageReceived.emit(null);
|
||||
const elsaClient = this.createClient();
|
||||
await elsaClient.workflowsApi.test(this.workflowDefinition.definitionId, this.workflowDefinition.version, this.signalRConnectionId);
|
||||
|
||||
const request: WorkflowTestExecuteRequest = {
|
||||
workflowDefinitionId: this.workflowDefinition.definitionId,
|
||||
version: this.workflowDefinition.version,
|
||||
signalRConnectionId: this.signalRConnectionId
|
||||
};
|
||||
|
||||
await elsaClient.workflowTestApi.execute(request);
|
||||
}
|
||||
|
||||
async onUseAsSchemaClick() {
|
||||
|
||||
const elsaClient = this.createClient();
|
||||
const value = this.testActivity.data["Inbound Request"];
|
||||
|
||||
const request: WorkflowTestSaveRequest = {
|
||||
workflowDefinitionId: this.workflowDefinition.definitionId,
|
||||
version: this.workflowDefinition.version,
|
||||
activityId: this.testActivity.activityId,
|
||||
json: value
|
||||
};
|
||||
|
||||
await elsaClient.workflowTestApi.save(request);
|
||||
}
|
||||
|
||||
selectTestActivityMessageInternal(message?: WorkflowTestActivityMessage) {
|
||||
this.testActivity = message;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
|
|
@ -121,89 +136,72 @@ export class ElsaWorkflowPropertiesPanel {
|
|||
|
||||
renderPropertiesButton() {
|
||||
|
||||
const {workflowDefinition, expanded, expandButtonPosition} = this;
|
||||
const name = workflowDefinition.name || this.i18next.t("Untitled");
|
||||
const {expanded, expandButtonPosition} = this;
|
||||
const expandPositionClass = expandButtonPosition == 1 ? "elsa-right-12" : "elsa-right-28";
|
||||
|
||||
return (
|
||||
<button type="button"
|
||||
onClick={this.toggle}
|
||||
class={`${expanded ? "hidden" : expandPositionClass} workflow-settings-button elsa-fixed elsa-top-20 elsa-inline-flex elsa-items-center elsa-p-2 elsa-rounded-full elsa-border elsa-border-transparent elsa-bg-white shadow elsa-text-gray-400 hover:elsa-text-blue-500 focus:elsa-text-blue-500 hover:elsa-ring-2 hover:elsa-ring-offset-2 hover:elsa-ring-blue-500 focus:elsa-outline-none focus:elsa-ring-2 focus:elsa-ring-offset-2 focus:elsa-ring-blue-500 elsa-z-10`}>
|
||||
onClick={this.toggle}
|
||||
class={`${expanded ? "hidden" : expandPositionClass} workflow-settings-button elsa-fixed elsa-top-20 elsa-inline-flex elsa-items-center elsa-p-2 elsa-rounded-full elsa-border elsa-border-transparent elsa-bg-white shadow elsa-text-gray-400 hover:elsa-text-blue-500 focus:elsa-text-blue-500 hover:elsa-ring-2 hover:elsa-ring-offset-2 hover:elsa-ring-blue-500 focus:elsa-outline-none focus:elsa-ring-2 focus:elsa-ring-offset-2 focus:elsa-ring-blue-500 elsa-z-10`}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="elsa-h-8 elsa-w-8" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 19l-7-7 7-7m8 14l-7-7 7-7"/>
|
||||
</svg>
|
||||
</button>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderPanel() {
|
||||
|
||||
const t = (x, params?) => this.i18next.t(x, params);
|
||||
const {workflowDefinition} = this;
|
||||
const name = workflowDefinition.name || this.i18next.t("Untitled");
|
||||
const {isPublished} = workflowDefinition;
|
||||
const tabs = this.tabs;
|
||||
const selectedTabId = this.selectedTabId;
|
||||
const selectedTab = tabs.find(x => x.id === selectedTabId);
|
||||
|
||||
return (
|
||||
<section class={`${this.expanded ? '' : 'hidden'} elsa-fixed elsa-top-4 elsa-right-0 elsa-bottom-0 elsa-overflow-hidden`} aria-labelledby="slide-over-title" role="dialog" aria-modal="true">
|
||||
<section
|
||||
class={`${this.expanded ? '' : 'hidden'} elsa-fixed elsa-top-4 elsa-right-0 elsa-bottom-0 elsa-overflow-hidden`}
|
||||
aria-labelledby="slide-over-title" role="dialog" aria-modal="true">
|
||||
<div class="elsa-absolute elsa-inset-0 elsa-overflow-hidden">
|
||||
<div class="elsa-absolute elsa-inset-0" aria-hidden="true"/>
|
||||
<div class="elsa-fixed elsa-inset-y-0 elsa-top-16 elsa-right-0 max-elsa-w-full elsa-flex">
|
||||
|
||||
<div ref={el => this.el = el}
|
||||
data-transition-enter="elsa-transform elsa-transition elsa-ease-in-out elsa-duration-300 sm:elsa-duration-700"
|
||||
data-transition-enter-start="elsa-translate-x-full"
|
||||
data-transition-enter-end="elsa-translate-x-0"
|
||||
data-transition-leave="elsa-transform elsa-transition elsa-ease-in-out elsa-duration-300 sm:elsa-duration-700"
|
||||
data-transition-leave-start="elsa-translate-x-0"
|
||||
data-transition-leave-end="elsa-translate-x-full"
|
||||
class="elsa-w-screen elsa-max-w-lg elsa-h-full">
|
||||
<button type="button"
|
||||
onClick={this.toggle}
|
||||
class="workflow-settings-button elsa-absolute elsa-top-4 elsa-left-2 elsa-inline-flex elsa-items-center elsa-p-2 elsa-rounded-full elsa-border elsa-border-transparent elsa-bg-white shadow elsa-text-gray-400 hover:elsa-text-blue-500 focus:elsa-text-blue-500 hover:elsa-ring-2 hover:elsa-ring-offset-2 hover:elsa-ring-blue-500 focus:elsa-outline-none focus:elsa-ring-2 focus:elsa-ring-offset-2 focus:elsa-ring-blue-500 elsa-z-10">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="elsa-h-8 elsa-w-8" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M13 5l7 7-7 7M5 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div
|
||||
class="elsa-h-full elsa-flex elsa-flex-col elsa-py-6 elsa-bg-white elsa-shadow-xl elsa-overflow-y-scroll elsa-bg-white">
|
||||
<div class="elsa-h-full">
|
||||
<div class="elsa-mt-16 elsa-p-6">
|
||||
<div class="elsa-font-medium elsa-leading-8 elsa-overflow-hidden">
|
||||
<p
|
||||
class="elsa-overflow-ellipsis">{t('Properties', {name: workflowDefinition.displayName || name})}</p>
|
||||
</div>
|
||||
<div>
|
||||
<dl
|
||||
class="elsa-mt-2 elsa-border-t elsa-border-b elsa-border-gray-200 elsa-divide-y elsa-divide-gray-200">
|
||||
<div class="elsa-py-3 elsa-flex elsa-justify-between elsa-text-sm elsa-font-medium">
|
||||
<dt class="elsa-text-gray-500">{t('Name')}</dt>
|
||||
<dd class="elsa-text-gray-900">{name}</dd>
|
||||
</div>
|
||||
<div class="elsa-py-3 elsa-flex elsa-justify-between elsa-text-sm elsa-font-medium">
|
||||
<dt class="elsa-text-gray-500">{t('Id')}</dt>
|
||||
<dd class="elsa-text-gray-900 elsa-break-all">{workflowDefinition.definitionId || '-'}</dd>
|
||||
</div>
|
||||
<div class="elsa-py-3 elsa-flex elsa-justify-between elsa-text-sm elsa-font-medium">
|
||||
<dt class="elsa-text-gray-500">{t('Version')}</dt>
|
||||
<dd class="elsa-text-gray-900">{workflowDefinition.version}</dd>
|
||||
</div>
|
||||
<div class="elsa-py-3 elsa-flex elsa-justify-between elsa-text-sm elsa-font-medium">
|
||||
<dt class="elsa-text-gray-500">{t('PublishedVersion')}</dt>
|
||||
<dd class="elsa-text-gray-900">{this.publishedVersion || '-'}</dd>
|
||||
</div>
|
||||
<div class="elsa-py-3 elsa-flex elsa-justify-between elsa-text-sm elsa-font-medium">
|
||||
<dt class="elsa-text-gray-500">{t('Status')}</dt>
|
||||
<dd
|
||||
class={`${isPublished ? 'elsa-text-green-600' : 'elsa-text-yellow-700'}`}>{isPublished ? t('Published') : t('Draft')}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="elsa-fixed elsa-inset-y-0 elsa-top-16 elsa-right-0 max-elsa-w-full elsa-flex">
|
||||
<div
|
||||
ref={el => this.el = el}
|
||||
data-transition-enter="elsa-transform elsa-transition elsa-ease-in-out elsa-duration-300 sm:elsa-duration-700"
|
||||
data-transition-enter-start="elsa-translate-x-full"
|
||||
data-transition-enter-end="elsa-translate-x-0"
|
||||
data-transition-leave="elsa-transform elsa-transition elsa-ease-in-out elsa-duration-300 sm:elsa-duration-700"
|
||||
data-transition-leave-start="elsa-translate-x-0"
|
||||
data-transition-leave-end="elsa-translate-x-full"
|
||||
class="elsa-w-screen elsa-max-w-lg elsa-h-full">
|
||||
<div class="elsa-h-full elsa-flex elsa-flex-col elsa-py-6 elsa-bg-white elsa-shadow-xl elsa-overflow-y-scroll">
|
||||
<div class="elsa-px-4 sm:elsa-px-6">
|
||||
<div class="elsa-flex elsa-flex-col elsa-items-end">
|
||||
<div class="elsa-ml-3 h-7 elsa-flex elsa-items-center"></div>
|
||||
<button type="button"
|
||||
onClick={this.toggle}
|
||||
class="workflow-settings-button elsa-absolute elsa-top-4 elsa-left-2 elsa-inline-flex elsa-items-center elsa-p-2 elsa-rounded-full elsa-border elsa-border-transparent elsa-bg-white shadow elsa-text-gray-400 hover:elsa-text-blue-500 focus:elsa-text-blue-500 hover:elsa-ring-2 hover:elsa-ring-offset-2 hover:elsa-ring-blue-500 focus:elsa-outline-none focus:elsa-ring-2 focus:elsa-ring-offset-2 focus:elsa-ring-blue-500 elsa-z-10">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="elsa-h-8 elsa-w-8" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M13 5l7 7-7 7M5 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="elsa-border-b elsa-border-gray-200">
|
||||
<nav class="-elsa-mb-px elsa-flex elsa-space-x-8" aria-label="Tabs">
|
||||
{tabs.map(tab => {
|
||||
const className = tab.id == selectedTabId ? 'elsa-border-blue-500 elsa-text-blue-600' : 'elsa-border-transparent elsa-text-gray-500 hover:elsa-text-gray-700 hover:elsa-border-gray-300';
|
||||
return <a href="#" onClick={e => this.onTabClick(e, tab)} class={`${className} elsa-whitespace-nowrap elsa-py-4 elsa-px-1 elsa-border-b-2 elsa-font-medium elsa-text-sm`}>{tab.text}</a>;
|
||||
})}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="elsa-mt-6 elsa-relative elsa-flex-1 elsa-px-4 sm:elsa-px-6">
|
||||
<div class="elsa-absolute elsa-inset-0 elsa-px-4 sm:elsa-px-6">
|
||||
{selectedTab.view()}
|
||||
|
|
@ -260,7 +258,8 @@ export class ElsaWorkflowPropertiesPanel {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>);
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderTestTab = () => {
|
||||
|
|
@ -279,7 +278,6 @@ export class ElsaWorkflowPropertiesPanel {
|
|||
</button>
|
||||
</div>
|
||||
{this.renderActivityMessage()}
|
||||
{this.renderUseAsSchemaButton()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -289,52 +287,87 @@ export class ElsaWorkflowPropertiesPanel {
|
|||
|
||||
renderActivityMessage() {
|
||||
|
||||
const t = (x, params?) => this.i18next.t(x, params);
|
||||
const {testActivity} = this;
|
||||
|
||||
if (testActivity)
|
||||
if (testActivity == undefined)
|
||||
return
|
||||
|
||||
return (
|
||||
<dl
|
||||
class="elsa-mt-2 elsa-border-t elsa-border-b elsa-border-gray-200 elsa-divide-y elsa-divide-gray-200">
|
||||
<div class="elsa-py-3 elsa-flex elsa-justify-between elsa-text-sm elsa-font-medium">
|
||||
<dt class="elsa-text-gray-500">{t('Instance Id')}</dt>
|
||||
<dd class="elsa-text-gray-900">{testActivity.workflowInstanceId}</dd>
|
||||
const t = (x, params?) => this.i18next.t(x, params);
|
||||
const filteredData = {};
|
||||
const wellKnownDataKeys = {State: true, Input: null, Outcomes: true, Exception: true};
|
||||
let dataKey = null;
|
||||
|
||||
for (const key in testActivity.data) {
|
||||
if (!testActivity.data.hasOwnProperty(key))
|
||||
continue;
|
||||
|
||||
if (!!wellKnownDataKeys[key])
|
||||
continue;
|
||||
|
||||
const value = testActivity.data[key];
|
||||
|
||||
if (!value && value != 0)
|
||||
continue;
|
||||
|
||||
let valueText = null;
|
||||
dataKey = key;
|
||||
|
||||
if (typeof value == 'string')
|
||||
valueText = value;
|
||||
else if (typeof value == 'object')
|
||||
valueText = JSON.stringify(value, null, 1);
|
||||
else if (typeof value == 'undefined')
|
||||
valueText = null;
|
||||
else
|
||||
valueText = value.toString();
|
||||
|
||||
filteredData[key] = valueText;
|
||||
}
|
||||
|
||||
const isInboundRequest = dataKey === "Inbound Request";
|
||||
|
||||
return (
|
||||
<dl
|
||||
class="elsa-mt-2 elsa-border-t elsa-border-b elsa-border-gray-200 elsa-divide-y elsa-divide-gray-200">
|
||||
<div class="elsa-py-3 elsa-flex elsa-justify-between elsa-text-sm elsa-font-medium">
|
||||
<dt class="elsa-text-gray-500">{t('Instance Id')}</dt>
|
||||
<dd class="elsa-text-gray-900">{testActivity.workflowInstanceId}</dd>
|
||||
</div>
|
||||
<div class="elsa-py-3 elsa-flex elsa-justify-between elsa-text-sm elsa-font-medium">
|
||||
<dt class="elsa-text-gray-500">{t('Correlation Id')}</dt>
|
||||
<dd class="elsa-text-gray-900">{testActivity.correlationId}</dd>
|
||||
</div>
|
||||
<div class="elsa-py-3 elsa-flex elsa-justify-between elsa-text-sm elsa-font-medium">
|
||||
<dt class="elsa-text-gray-500">{t('Activity Id')}</dt>
|
||||
<dd class="elsa-text-gray-900">{testActivity.activityId}</dd>
|
||||
</div>
|
||||
<div class="elsa-py-3 elsa-flex elsa-justify-between elsa-text-sm elsa-font-medium">
|
||||
<dt class="elsa-text-gray-500">{t('Status')}</dt>
|
||||
<dd class="elsa-text-gray-900 elsa-break-all">{testActivity.status || '-'}</dd>
|
||||
</div>
|
||||
{collection.map(filteredData, (v, k) => (
|
||||
<div>
|
||||
<div class="sm:elsa-col-span-2">
|
||||
<dt class="elsa-text-sm elsa-font-medium elsa-text-gray-500 elsa-capitalize">{k}</dt>
|
||||
<dd class="elsa-mt-1 elsa-text-sm elsa-text-gray-900 elsa-mb-2 elsa-overflow-x-auto">{v}</dd>
|
||||
</div>
|
||||
{isInboundRequest ?
|
||||
<div class="elsa-px-4 elsa-py-3 elsa-bg-gray-50 elsa-text-left sm:px-6">
|
||||
<button type="button"
|
||||
onClick={() => this.onUseAsSchemaClick()}
|
||||
class="elsa-ml-0 elsa-w-full elsa-inline-flex elsa-justify-center elsa-rounded-md elsa-border elsa-border-transparent elsa-shadow-sm elsa-px-4 elsa-py-2 elsa-bg-blue-600 elsa-text-base elsa-font-medium elsa-text-white hover:elsa-bg-blue-700 focus:elsa-outline-none focus:elsa-ring-2 focus:elsa-ring-offset-2 focus:elsa-ring-blue-500 sm:elsa-ml-3 sm:elsa-w-auto sm:elsa-text-sm">
|
||||
{t('UseAsSchema')}
|
||||
</button>
|
||||
</div>
|
||||
:
|
||||
null
|
||||
}
|
||||
</div>
|
||||
<div class="elsa-py-3 elsa-flex elsa-justify-between elsa-text-sm elsa-font-medium">
|
||||
<dt class="elsa-text-gray-500">{t('Correlation Id')}</dt>
|
||||
<dd class="elsa-text-gray-900">{testActivity.correlationId}</dd>
|
||||
</div>
|
||||
<div class="elsa-py-3 elsa-flex elsa-justify-between elsa-text-sm elsa-font-medium">
|
||||
<dt class="elsa-text-gray-500">{t('Activity Id')}</dt>
|
||||
<dd class="elsa-text-gray-900">{testActivity.activityId}</dd>
|
||||
</div>
|
||||
<div class="elsa-py-3 elsa-flex elsa-justify-between elsa-text-sm elsa-font-medium">
|
||||
<dt class="elsa-text-gray-500">{t('Status')}</dt>
|
||||
<dd class="elsa-text-gray-900 elsa-break-all">{testActivity.status || '-'}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
))}
|
||||
</dl>
|
||||
);
|
||||
}
|
||||
|
||||
renderUseAsSchemaButton() {
|
||||
|
||||
const t = (x, params?) => this.i18next.t(x, params);
|
||||
const {testActivity} = this;
|
||||
|
||||
if (testActivity)
|
||||
|
||||
return (
|
||||
<div class="elsa-px-4 elsa-py-3 elsa-bg-gray-50 elsa-text-left sm:px-6">
|
||||
<button type="button"
|
||||
onClick={() => this.onUseAsSchemaClick()}
|
||||
class="elsa-ml-0 elsa-w-full elsa-inline-flex elsa-justify-center elsa-rounded-md elsa-border elsa-border-transparent elsa-shadow-sm elsa-px-4 elsa-py-2 elsa-bg-blue-600 elsa-text-base elsa-font-medium elsa-text-white hover:elsa-bg-blue-700 focus:elsa-outline-none focus:elsa-ring-2 focus:elsa-ring-offset-2 focus:elsa-ring-blue-500 sm:elsa-ml-3 sm:elsa-w-auto sm:elsa-text-sm">
|
||||
{t('UseAsSchema')}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
createClient() {
|
||||
return createElsaClient(this.serverUrl);
|
||||
}
|
||||
|
|
@ -361,4 +394,4 @@ export class ElsaWorkflowPropertiesPanel {
|
|||
}
|
||||
}
|
||||
|
||||
Tunnel.injectProps(ElsaWorkflowPropertiesPanel, ['serverUrl', 'culture']);
|
||||
Tunnel.injectProps(ElsaWorkflowPropertiesPanel, ['serverUrl', 'culture']);
|
||||
|
|
@ -328,6 +328,7 @@ export interface WorkflowTestActivityMessage {
|
|||
correlationId: string;
|
||||
activityId: string;
|
||||
status: string;
|
||||
data?: any;
|
||||
}
|
||||
|
||||
export const getVersionOptionsString = (versionOptions?: VersionOptions) => {
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ export const createElsaClient = function (serverUrl: string): ElsaClient {
|
|||
},
|
||||
workflowDefinitionsApi: {
|
||||
list: async (page?: number, pageSize?: number, versionOptions?: VersionOptions) => {
|
||||
debugger
|
||||
const versionOptionsString = getVersionOptionsString(versionOptions);
|
||||
const response = await httpClient.get<PagedList<WorkflowDefinitionSummary>>(`v1/workflow-definitions?version=${versionOptionsString}`);
|
||||
return response.data;
|
||||
|
|
@ -114,39 +115,19 @@ export const createElsaClient = function (serverUrl: string): ElsaClient {
|
|||
return response.data;
|
||||
}
|
||||
},
|
||||
workflowsApi: {
|
||||
test: async (workflowDefinitionId, version, signalRConnectionId) => {
|
||||
|
||||
httpClient.interceptors.request.use(function (config) {
|
||||
config.headers['Accept'] = 'application/json';
|
||||
config.data = Object.assign({}, config.data, {});
|
||||
return config;
|
||||
})
|
||||
const response = await httpClient.post<void>(`v1/workflows/${workflowDefinitionId}/${version}/${signalRConnectionId}/test`);
|
||||
return response.data;
|
||||
}
|
||||
},
|
||||
webhookDefinitionsApi: {
|
||||
list: async (page?: number, pageSize?: number) => {
|
||||
const response = await httpClient.get<PagedList<WebhookDefinitionSummary>>(`v1/webhook-definitions`);
|
||||
return response.data;
|
||||
},
|
||||
getByWebhookId: async (webhookId: string) => {
|
||||
const response = await httpClient.get<WebhookDefinition>(`v1/webhook-definitions/${webhookId}`);
|
||||
return response.data;
|
||||
workflowTestApi: {
|
||||
execute: async (request) => {
|
||||
httpClient.interceptors.request.use(function (config) {
|
||||
config.headers['Accept'] = 'application/json';
|
||||
config.data = Object.assign({}, config.data, {});
|
||||
return config;
|
||||
})
|
||||
await httpClient.post<void>(`v1/workflow-test/execute`, request);
|
||||
},
|
||||
save: async request => {
|
||||
const response = await httpClient.post<WebhookDefinition>('v1/webhook-definitions', request);
|
||||
return response.data;
|
||||
},
|
||||
update: async request => {
|
||||
const response = await httpClient.put<WebhookDefinition>('v1/webhook-definitions', request);
|
||||
return response.data;
|
||||
},
|
||||
delete: async webhookId => {
|
||||
await httpClient.delete(`v1/webhook-definitions/${webhookId}`);
|
||||
},
|
||||
},
|
||||
await httpClient.post<void>(`v1/workflow-test/save`, request);
|
||||
},
|
||||
},
|
||||
workflowRegistryApi: {
|
||||
list: async (page?: number, pageSize?: number, versionOptions?: VersionOptions): Promise<PagedList<WorkflowBlueprintSummary>> => {
|
||||
const versionOptionsString = getVersionOptionsString(versionOptions);
|
||||
|
|
@ -276,7 +257,7 @@ export interface ElsaClient {
|
|||
activityStatsApi: ActivityStatsApi;
|
||||
workflowStorageProvidersApi: WorkflowStorageProvidersApi;
|
||||
workflowChannelsApi: WorkflowChannelsApi;
|
||||
workflowsApi: WorkflowsApi;
|
||||
workflowTestApi: WorkflowTestApi;
|
||||
}
|
||||
|
||||
export interface ActivitiesApi {
|
||||
|
|
@ -302,22 +283,11 @@ export interface WorkflowDefinitionsApi {
|
|||
import(workflowDefinitionId: string, file: File): Promise<WorkflowDefinition>;
|
||||
}
|
||||
|
||||
export interface WorkflowsApi {
|
||||
export interface WorkflowTestApi {
|
||||
|
||||
test(workflowDefinitionId: string, version: number, signalRConnectionId: string): Promise<void>;
|
||||
}
|
||||
execute(request: WorkflowTestExecuteRequest): Promise<void>;
|
||||
|
||||
export interface WebhookDefinitionsApi {
|
||||
|
||||
list(page?: number, pageSize?: number): Promise<PagedList<WebhookDefinitionSummary>>;
|
||||
|
||||
getByWebhookId(webhookId: string): Promise<WebhookDefinition>;
|
||||
|
||||
save(request: SaveWebhookDefinitionRequest): Promise<WebhookDefinition>;
|
||||
|
||||
update(request: SaveWebhookDefinitionRequest): Promise<WebhookDefinition>;
|
||||
|
||||
delete(webhookId: string): Promise<void>;
|
||||
save(request: WorkflowTestSaveRequest): Promise<void>;
|
||||
}
|
||||
|
||||
export interface WorkflowRegistryApi {
|
||||
|
|
@ -403,6 +373,19 @@ export interface SaveWorkflowDefinitionRequest {
|
|||
connections: Array<ConnectionDefinition>;
|
||||
}
|
||||
|
||||
export interface WorkflowTestExecuteRequest {
|
||||
workflowDefinitionId?: string,
|
||||
version?: number,
|
||||
signalRConnectionId?: string
|
||||
}
|
||||
|
||||
export interface WorkflowTestSaveRequest {
|
||||
workflowDefinitionId?: string,
|
||||
version?: number,
|
||||
activityId?: string,
|
||||
json?: any
|
||||
}
|
||||
|
||||
export interface ExportWorkflowResponse {
|
||||
fileName: string;
|
||||
data: Blob;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.9" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
|
||||
<PackageReference Include="System.Text.Encodings.Web" Version="5.0.1" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="NJsonSchema" Version="10.5.2" />
|
||||
<PackageReference Include="NodaTime.Serialization.SystemTextJson" Version="1.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.4" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.1.4" />
|
||||
|
|
|
|||
54
src/server/Elsa.Server.Api/Endpoints/WorkflowTest/Execute.cs
Normal file
54
src/server/Elsa.Server.Api/Endpoints/WorkflowTest/Execute.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
using Elsa.Server.Api.ActionFilters;
|
||||
using Elsa.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Swashbuckle.AspNetCore.Annotations;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Models;
|
||||
using Elsa.Server.Api.Endpoints.Workflows;
|
||||
|
||||
namespace Elsa.Server.Api.Endpoints.WorkflowTest
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1")]
|
||||
[Route("v{apiVersion:apiVersion}/workflow-test/execute")]
|
||||
[Produces("application/json")]
|
||||
public class Execute : Controller
|
||||
{
|
||||
private readonly IWorkflowLaunchpad _workflowLaunchpad;
|
||||
private readonly ITenantAccessor _tenantAccessor;
|
||||
|
||||
public Execute(IWorkflowLaunchpad workflowLaunchpad, ITenantAccessor tenantAccessor)
|
||||
{
|
||||
_workflowLaunchpad = workflowLaunchpad;
|
||||
_tenantAccessor = tenantAccessor;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ElsaJsonFormatter]
|
||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WorkflowTestExecuteRequest))]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[SwaggerOperation(
|
||||
Summary = "Executes the specified workflow definition version in test mode.",
|
||||
Description = "Executes the specified workflow definition vesrion in test mode.",
|
||||
OperationId = "WorkflowTest.Execute",
|
||||
Tags = new[] { "WorkflowTest" })
|
||||
]
|
||||
public async Task<IActionResult> Handle([FromBody] WorkflowTestExecuteRequest request, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var tenantId = await _tenantAccessor.GetTenantIdAsync(cancellationToken);
|
||||
var testableWorkflow = await _workflowLaunchpad.FindTestableWorkflowAsync(request.WorkflowDefinitionId!, request.Version, null, null, null, tenantId, request.SignalRConnectionId, cancellationToken);
|
||||
|
||||
if (testableWorkflow == null)
|
||||
return NotFound();
|
||||
|
||||
var result = await _workflowLaunchpad.ExecuteTestableWorkflowAsync(testableWorkflow, null, cancellationToken);
|
||||
|
||||
if (Response.HasStarted)
|
||||
return new EmptyResult();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/server/Elsa.Server.Api/Endpoints/WorkflowTest/Models.cs
Normal file
17
src/server/Elsa.Server.Api/Endpoints/WorkflowTest/Models.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
namespace Elsa.Server.Api.Endpoints.WorkflowTest
|
||||
{
|
||||
public sealed record WorkflowTestExecuteRequest
|
||||
{
|
||||
public string? WorkflowDefinitionId { get; init; }
|
||||
public int Version { get; init; }
|
||||
public string? SignalRConnectionId { get; init; }
|
||||
}
|
||||
|
||||
public sealed record WorkflowTestSaveRequest
|
||||
{
|
||||
public string? WorkflowDefinitionId { get; init; }
|
||||
public int Version { get; init; }
|
||||
public string? ActivityId { get; init; }
|
||||
public object? Json { get; init; }
|
||||
}
|
||||
}
|
||||
69
src/server/Elsa.Server.Api/Endpoints/WorkflowTest/Save.cs
Normal file
69
src/server/Elsa.Server.Api/Endpoints/WorkflowTest/Save.cs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Events;
|
||||
using Elsa.Server.Api.ActionFilters;
|
||||
using Elsa.Services;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using NJsonSchema;
|
||||
using Swashbuckle.AspNetCore.Annotations;
|
||||
|
||||
namespace Elsa.Server.Api.Endpoints.WorkflowTest
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1")]
|
||||
[Route("v{apiVersion:apiVersion}/workflow-test/save")]
|
||||
[Produces("application/json")]
|
||||
public class Save : Controller
|
||||
{
|
||||
private readonly IWorkflowPublisher _workflowPublisher;
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public Save(IWorkflowPublisher workflowPublisher, IMediator mediator)
|
||||
{
|
||||
_workflowPublisher = workflowPublisher;
|
||||
_mediator = mediator;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ElsaJsonFormatter]
|
||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WorkflowTestSaveRequest))]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[SwaggerOperation(
|
||||
Summary = "Updates Json Schema for the specified workflow definition version activity in test mode.",
|
||||
Description = "Updates Json Schema the specified workflow definition vesrion activity in test mode.",
|
||||
OperationId = "WorkflowTest.Save",
|
||||
Tags = new[] { "WorkflowTest" })
|
||||
]
|
||||
public async Task<IActionResult> Handle([FromBody] WorkflowTestSaveRequest request, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var workflowDefinitionId = request.WorkflowDefinitionId;
|
||||
var workflowDefinition = !string.IsNullOrWhiteSpace(workflowDefinitionId) ? await _workflowPublisher.GetAsync(workflowDefinitionId, request.Version, cancellationToken) : default;
|
||||
|
||||
if (workflowDefinition == null)
|
||||
return NotFound();
|
||||
|
||||
var schema = JsonSchema.FromSampleJson(JsonConvert.SerializeObject(request.Json));
|
||||
var schemaJson = schema.ToJson();
|
||||
|
||||
var activity = workflowDefinition.Activities.FirstOrDefault(x => x.ActivityId == request.ActivityId);
|
||||
if (activity != null)
|
||||
{
|
||||
var property = activity.Properties.FirstOrDefault(x => x.Name == "Schema");
|
||||
if (property != null)
|
||||
{
|
||||
property.Expressions.Remove("Literal");
|
||||
property.Expressions.Add("Literal", schemaJson);
|
||||
}
|
||||
}
|
||||
|
||||
workflowDefinition = await _workflowPublisher.SaveDraftAsync(workflowDefinition, cancellationToken);
|
||||
await _mediator.Publish(new WorkflowDefinitionUpdated(workflowDefinition), cancellationToken);
|
||||
|
||||
return CreatedAtAction("Handle", "GetByVersionId", new { versionId = workflowDefinition.Id }, workflowDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -55,6 +55,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
services.AddSingleton<IEndpointContentSerializerSettingsProvider, EndpointContentSerializerSettingsProvider>();
|
||||
services.AddAutoMapperProfile<AutoMapperProfile>();
|
||||
services.AddNotificationHandlersFrom<ActivityExecutionResultExecutedHandler>();
|
||||
services.AddNotificationHandlersFrom<WorkflowRegistryCacheHandler>();
|
||||
services.AddSignalR();
|
||||
return services;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Events;
|
||||
using Elsa.Models;
|
||||
using Elsa.Server.Api.Models;
|
||||
using Elsa.Server.Api.Services;
|
||||
using MediatR;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Elsa.Server.Api.Handlers
|
||||
{
|
||||
|
|
@ -19,16 +21,29 @@ namespace Elsa.Server.Api.Handlers
|
|||
|
||||
public async Task Handle(ActivityExecutionResultExecuted notification, CancellationToken cancellationToken)
|
||||
{
|
||||
var signalRConnectionId = notification.ActivityExecutionContext.WorkflowExecutionContext.WorkflowBlueprint.SignalRConnectionId;
|
||||
var context = notification.ActivityExecutionContext;
|
||||
|
||||
var signalRConnectionId = context.WorkflowExecutionContext.WorkflowBlueprint.SignalRConnectionId;
|
||||
if (string.IsNullOrWhiteSpace(signalRConnectionId)) return;
|
||||
|
||||
var data = new JObject
|
||||
{
|
||||
["Outcomes"] = JToken.FromObject(context.Outcomes)
|
||||
};
|
||||
|
||||
foreach (var entry in context.JournalData)
|
||||
data[entry.Key] = entry.Value != null ? JToken.FromObject(entry.Value) : JValue.CreateNull();
|
||||
|
||||
var message = new WorkflowTestMessage
|
||||
{
|
||||
SignalRConnectionId = signalRConnectionId,
|
||||
WorkflowInstanceId = notification.ActivityExecutionContext.WorkflowInstance.Id,
|
||||
CorrelationId = notification.ActivityExecutionContext.CorrelationId,
|
||||
ActivityId = notification.ActivityExecutionContext.ActivityId,
|
||||
Status = notification.ActivityExecutionContext.WorkflowExecutionContext.Status.ToString()
|
||||
WorkflowInstanceId = context.WorkflowInstance.Id,
|
||||
CorrelationId = context.CorrelationId,
|
||||
ActivityId = context.ActivityId,
|
||||
Status = context.WorkflowExecutionContext.Status == WorkflowStatus.Running
|
||||
? "Executed"
|
||||
: context.WorkflowExecutionContext.Status.ToString(),
|
||||
Data = JsonConvert.SerializeObject(data, Formatting.Indented)
|
||||
};
|
||||
|
||||
await _workflowTestService.DispatchMessage(message);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Caching;
|
||||
using Elsa.Events;
|
||||
using Elsa.Services.Workflows;
|
||||
using MediatR;
|
||||
|
||||
namespace Elsa.Server.Api.Handlers
|
||||
{
|
||||
public class WorkflowRegistryCacheHandler : INotificationHandler<WorkflowDefinitionUpdated>
|
||||
{
|
||||
private readonly ICacheSignal _cacheSignal;
|
||||
|
||||
public WorkflowRegistryCacheHandler(ICacheSignal cacheSignal)
|
||||
{
|
||||
_cacheSignal = cacheSignal;
|
||||
}
|
||||
|
||||
public async Task Handle(WorkflowDefinitionUpdated notification, CancellationToken cancellationToken)
|
||||
{
|
||||
await _cacheSignal.TriggerTokenAsync(ActivityTypeService.CacheKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Elsa.Server.Api.Models
|
||||
{
|
||||
public class WorkflowTestMessage
|
||||
|
|
@ -7,5 +9,6 @@ namespace Elsa.Server.Api.Models
|
|||
public string CorrelationId { get; set; } = default!;
|
||||
public string ActivityId { get; set; } = default!;
|
||||
public string Status { get; set; } = default!;
|
||||
public object? Data { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.22" />
|
||||
<PackageReference Include="Hangfire.InMemory" Version="0.3.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.9" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@
|
|||
<PackageReference Include="AutoFixture" Version="4.17.0" />
|
||||
<PackageReference Include="Autofixture.AutoMoq" Version="4.17.0" />
|
||||
<PackageReference Include="autofixture.xunit2" Version="4.17.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="NodaTime" Version="3.0.5" />
|
||||
<PackageReference Include="System.ComponentModel.TypeConverter" Version="4.3.0" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.4.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.9" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
Loading…
Reference in a new issue