From f203e0cdef94a53962090b96fe48b463157eeee3 Mon Sep 17 00:00:00 2001 From: axeleron007 Date: Thu, 30 Sep 2021 12:00:44 +0300 Subject: [PATCH] PR change requests --- .../Middleware/HttpEndpointMiddleware.cs | 10 ++- .../Services/Workflows/IWorkflowRegistry.cs | 5 ++ .../Decorators/CachingWorkflowRegistry.cs | 8 +- .../Services/Workflows/WorkflowRegistry.cs | 3 + .../elsa-script-property.tsx | 83 ++++++++----------- ...elsa-workflow-definition-editor-screen.tsx | 30 +++---- .../src/models/domain.ts | 5 -- .../src/models/events.ts | 16 ++++ .../src/plugins/http-endpoint-plugin.tsx | 55 +++++++++++- .../Extensions/ServiceCollectionExtensions.cs | 19 +++-- 10 files changed, 144 insertions(+), 90 deletions(-) diff --git a/src/activities/Elsa.Activities.Http/Middleware/HttpEndpointMiddleware.cs b/src/activities/Elsa.Activities.Http/Middleware/HttpEndpointMiddleware.cs index 0477e0c61..17d333126 100644 --- a/src/activities/Elsa.Activities.Http/Middleware/HttpEndpointMiddleware.cs +++ b/src/activities/Elsa.Activities.Http/Middleware/HttpEndpointMiddleware.cs @@ -75,10 +75,12 @@ namespace Elsa.Activities.Http.Middleware return; } - var isTest = pendingWorkflowInstance.GetMetadata("isTest"); - var workflowBlueprint = (isTest != null && Convert.ToBoolean(isTest)) ? - await workflowRegistry.FindAsync(x => x.Id == pendingWorkflowInstance.DefinitionId, cancellationToken) : - await workflowRegistry.FindAsync(x => x.IsPublished && x.Id == pendingWorkflowInstance.DefinitionId && !x.IsDisabled, cancellationToken); + //var isTest = pendingWorkflowInstance.GetMetadata("isTest"); + //var workflowBlueprint = (isTest != null && Convert.ToBoolean(isTest)) ? + // await workflowRegistry.FindAsync(x => x.Id == pendingWorkflowInstance.DefinitionId, cancellationToken) : + // await workflowRegistry.FindAsync(x => x.IsPublished && x.Id == pendingWorkflowInstance.DefinitionId && !x.IsDisabled, cancellationToken); + + var workflowBlueprint = await workflowRegistry.FindActiveAsync(x => x.Id == pendingWorkflowInstance.DefinitionId && !x.IsDisabled, cancellationToken); if (workflowBlueprint is null) { diff --git a/src/core/Elsa.Abstractions/Services/Workflows/IWorkflowRegistry.cs b/src/core/Elsa.Abstractions/Services/Workflows/IWorkflowRegistry.cs index 05a95ba1f..ea190beb2 100644 --- a/src/core/Elsa.Abstractions/Services/Workflows/IWorkflowRegistry.cs +++ b/src/core/Elsa.Abstractions/Services/Workflows/IWorkflowRegistry.cs @@ -36,5 +36,10 @@ namespace Elsa.Services /// Returns a single workflow blueprint that fits the specified predicate. /// Task FindAsync(Func predicate, CancellationToken cancellationToken = default); + + /// + /// Returns a single workflow blueprint that fits the specified predicate. + /// + Task FindActiveAsync(Func predicate, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Decorators/CachingWorkflowRegistry.cs b/src/core/Elsa.Core/Decorators/CachingWorkflowRegistry.cs index eb8459c68..a4b5a3e0b 100644 --- a/src/core/Elsa.Core/Decorators/CachingWorkflowRegistry.cs +++ b/src/core/Elsa.Core/Decorators/CachingWorkflowRegistry.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; @@ -55,6 +55,12 @@ namespace Elsa.Decorators return workflows.FirstOrDefault(predicate); } + public async Task FindActiveAsync(Func predicate, CancellationToken cancellationToken) + { + var workflows = await ListActiveAsync(cancellationToken); + return workflows.FirstOrDefault(predicate); + } + private async Task> ListInternalAsync(CancellationToken cancellationToken) { return await _memoryCache.GetOrCreateAsync(CacheKey, async entry => diff --git a/src/core/Elsa.Core/Services/Workflows/WorkflowRegistry.cs b/src/core/Elsa.Core/Services/Workflows/WorkflowRegistry.cs index f8be793bd..aa7340df5 100644 --- a/src/core/Elsa.Core/Services/Workflows/WorkflowRegistry.cs +++ b/src/core/Elsa.Core/Services/Workflows/WorkflowRegistry.cs @@ -44,6 +44,9 @@ namespace Elsa.Services.Workflows public async Task FindAsync(Func predicate, CancellationToken cancellationToken) => (await ListAsync(cancellationToken).Where(predicate).OrderByDescending(x => x.Version)).FirstOrDefault(); + public async Task FindActiveAsync(Func predicate, CancellationToken cancellationToken) => + (await ListActiveAsync(cancellationToken).Where(predicate).OrderByDescending(x => x.Version)).FirstOrDefault(); + private async IAsyncEnumerable ListActiveInternalAsync([EnumeratorCancellation] CancellationToken cancellationToken) { var workflows = await ListAsync(cancellationToken); diff --git a/src/designer/elsa-workflows-studio/src/components/editors/properties/elsa-script-property/elsa-script-property.tsx b/src/designer/elsa-workflows-studio/src/components/editors/properties/elsa-script-property/elsa-script-property.tsx index 5db056b90..919ded464 100644 --- a/src/designer/elsa-workflows-studio/src/components/editors/properties/elsa-script-property/elsa-script-property.tsx +++ b/src/designer/elsa-workflows-studio/src/components/editors/properties/elsa-script-property/elsa-script-property.tsx @@ -1,9 +1,8 @@ import {Component, h, Prop, State} from '@stencil/core'; -import {ActivityDefinitionProperty, ActivityPropertyDescriptor} from "../../../../models"; -import {createElsaClient} from "../../../../services"; +import {ActivityDefinitionProperty, ActivityPropertyDescriptor, ActivityValidatingContext, EventTypes} from "../../../../models"; +import {createElsaClient, eventBus} from "../../../../services"; import Tunnel from '../../../../data/workflow-editor'; import {MonacoValueChangedArgs} from "../../../controls/elsa-monaco/elsa-monaco"; -import Ajv from "ajv" @Component({ tag: 'elsa-script-property', @@ -19,9 +18,10 @@ export class ElsaScriptProperty { @Prop() syntax?: string; @Prop({mutable: true}) serverUrl: string; @Prop({mutable: true}) workflowDefinitionId: string; - @State() currentValue?: string - @State() isJsonSchemaValid?: boolean; + @State() currentValue?: string; + monacoEditor: HTMLElsaMonacoElement; + private activityValidatingContext: ActivityValidatingContext = null; async componentWillLoad() { this.currentValue = this.propertyModel.expressions['Literal']; @@ -49,47 +49,24 @@ export class ElsaScriptProperty { onConvertToJsonSchemaClick(e: Event) { e.preventDefault(); - window.open('https://extendsclass.com/json-schema-validator.html'); + window.open('https://www.convertsimple.com/convert-json-to-json-schema/'); } onMonacoValueChanged(e: MonacoValueChangedArgs) { this.currentValue = e.value; - this.validate(e.value); + this.validate(this.currentValue); } validate(value: string) { - const propertyDescriptor = this.propertyDescriptor; - const propertyName = propertyDescriptor.name; - const fieldName = propertyName; - - if (fieldName === "Schema") - { - this.isJsonSchemaValid = true; - if (value == '') return; - - const ajv = new Ajv(); - let json: object; - try{ - json = JSON.parse(value) - } - catch (e){ - this.isJsonSchemaValid = false; - } - - if (json != undefined) - { - try { - const validate = ajv.compile(json); - const errors = validate.errors; - if (errors != null) - this.isJsonSchemaValid = false; - } - catch (e){ - const err = e; - this.isJsonSchemaValid = false; - } - } - } + this.activityValidatingContext = { + activityType: this.context, + prop: this.propertyDescriptor.name, + value: value, + data: null, + isValidated: false, + isPositiveResult: false + }; + eventBus.emit(EventTypes.ActivityPluginValidating, this, this.activityValidatingContext); } render() { @@ -104,6 +81,22 @@ export class ElsaScriptProperty { const value = this.currentValue; const isSchema = fieldName === "Schema"; + const renderValidationResult = () => { + if (this.activityValidatingContext == null || !this.activityValidatingContext.isValidated) + return; + + const isPositiveResult = this.activityValidatingContext.isPositiveResult; + const color = isPositiveResult ? 'green' : 'red'; + + return ( +
+

+ {this.activityValidatingContext.data} +

+
+ ) + } + return
@@ -133,18 +126,10 @@ export class ElsaScriptProperty { ref={el => this.monacoEditor = el}/>
{fieldHint ?

{fieldHint}

: undefined} - {this.isJsonSchemaValid == undefined ? undefined : this.isJsonSchemaValid ? -

- Json is valid -

- : -

- Json is invalid -

- } + {renderValidationResult()} {isSchema ? -
+
this.onConvertToJsonSchemaClick(e)} class="elsa-relative elsa-inline-flex elsa-items-center elsa-px-4 elsa-py-2 elsa-border elsa-border-gray-300 elsa-text-sm elsa-leading-5 elsa-font-medium elsa-rounded-md elsa-text-gray-700 elsa-bg-white hover:elsa-text-gray-500 focus:elsa-outline-none focus:elsa-shadow-outline-blue focus:elsa-border-blue-300 active:elsa-bg-gray-100 active:elsa-text-gray-700 elsa-transition elsa-ease-in-out elsa-duration-150"> diff --git a/src/designer/elsa-workflows-studio/src/components/screens/workflow-definition-editor/elsa-workflow-definition-editor-screen/elsa-workflow-definition-editor-screen.tsx b/src/designer/elsa-workflows-studio/src/components/screens/workflow-definition-editor/elsa-workflow-definition-editor-screen/elsa-workflow-definition-editor-screen.tsx index 1ed61bab3..a3316c888 100644 --- a/src/designer/elsa-workflows-studio/src/components/screens/workflow-definition-editor/elsa-workflow-definition-editor-screen/elsa-workflow-definition-editor-screen.tsx +++ b/src/designer/elsa-workflows-studio/src/components/screens/workflow-definition-editor/elsa-workflow-definition-editor-screen/elsa-workflow-definition-editor-screen.tsx @@ -4,16 +4,15 @@ import { ActivityDefinition, ActivityDescriptor, ActivityModel, + ActivityUpdatedContext, ConnectionDefinition, ConnectionModel, EventTypes, - SyntaxNames, VersionOptions, WorkflowDefinition, WorkflowModel, WorkflowPersistenceBehavior, - WorkflowTestActivityMessage, - WorkflowTestUpdateHttpEndpoint + WorkflowTestActivityMessage } from "../../../../models"; import {createElsaClient, eventBus, SaveWorkflowDefinitionRequest} from "../../../../services"; import state from '../../../../utils/store'; @@ -447,18 +446,7 @@ export class ElsaWorkflowDefinitionEditorScreen { this.workflowTestActivityMessages = []; this.render(); - }; - - async updateHttpEndpointJsonSchema(request: WorkflowTestUpdateHttpEndpoint) { - let workflowModel = {...this.workflowModel}; - const activity = workflowModel.activities.find(x => x.activityId == request.activityId); - if (activity) - { - const activityModel = activity.properties.find(x => x.name == 'Schema'); - activityModel.expressions[SyntaxNames.Literal] = request.jsonSchema; - eventBus.emit(EventTypes.UpdateActivity, this, activityModel); - } - }; + }; private onUpdateWorkflowSettings = async (workflowDefinition: WorkflowDefinition) => { this.updateWorkflowDefinition(workflowDefinition); @@ -588,12 +576,14 @@ export class ElsaWorkflowDefinitionEditorScreen { async onUseAsSchemaClick(message: WorkflowTestActivityMessage) { const value = message.data["Body"]; - const request: WorkflowTestUpdateHttpEndpoint = { - activityId: message.activityId, - jsonSchema: JSON.stringify(convert(value), null, 1) - }; + let workflowModel = {...this.workflowModel}; + const activityModel = workflowModel.activities.find(x => x.activityId == message.activityId); - await this.updateHttpEndpointJsonSchema(request); + const context: ActivityUpdatedContext = { + activityModel: activityModel, + data: JSON.stringify(convert(value), null, 1) + }; + eventBus.emit(EventTypes.ActivityPluginUpdated, this, context); } renderTestActivityMenu = () => { diff --git a/src/designer/elsa-workflows-studio/src/models/domain.ts b/src/designer/elsa-workflows-studio/src/models/domain.ts index f19a0839e..8e33eb79c 100644 --- a/src/designer/elsa-workflows-studio/src/models/domain.ts +++ b/src/designer/elsa-workflows-studio/src/models/domain.ts @@ -334,11 +334,6 @@ export interface WorkflowTestActivityMessage { error?: string; } -export interface WorkflowTestUpdateHttpEndpoint { - activityId?: string, - jsonSchema?: string -} - export const getVersionOptionsString = (versionOptions?: VersionOptions) => { if (!versionOptions) diff --git a/src/designer/elsa-workflows-studio/src/models/events.ts b/src/designer/elsa-workflows-studio/src/models/events.ts index e32e02c8f..fef1e1295 100644 --- a/src/designer/elsa-workflows-studio/src/models/events.ts +++ b/src/designer/elsa-workflows-studio/src/models/events.ts @@ -15,6 +15,8 @@ export const EventTypes = { WorkflowModelChanged: 'workflow-model-changed', ActivityDesignDisplaying: 'activity-design-displaying', ActivityDescriptorDisplaying: 'activity-descriptor-displaying', + ActivityPluginUpdated: 'activity-plugin-updated', + ActivityPluginValidating: 'activity-plugin-validating', WorkflowPublished: 'workflow-published', WorkflowRetracted: 'workflow-retracted', WorkflowImported: 'workflow-imported', @@ -57,6 +59,20 @@ export interface ActivityDesignDisplayContext { outcomes: Array; } +export interface ActivityUpdatedContext { + activityModel: ActivityModel, + data?: string +} + +export interface ActivityValidatingContext { + activityType: string + prop: string, + value?: string, + isValidated: boolean, + data: any, + isPositiveResult: boolean +} + export interface ActivityDescriptorDisplayContext { activityDescriptor: ActivityDescriptor; activityIcon: any; diff --git a/src/designer/elsa-workflows-studio/src/plugins/http-endpoint-plugin.tsx b/src/designer/elsa-workflows-studio/src/plugins/http-endpoint-plugin.tsx index bce15c710..4e7f525d0 100644 --- a/src/designer/elsa-workflows-studio/src/plugins/http-endpoint-plugin.tsx +++ b/src/designer/elsa-workflows-studio/src/plugins/http-endpoint-plugin.tsx @@ -1,11 +1,13 @@ import {eventBus, ElsaPlugin} from "../services"; -import {ActivityDesignDisplayContext, EventTypes, SyntaxNames} from "../models"; -import {h} from "@stencil/core"; +import {ActivityDesignDisplayContext, ActivityUpdatedContext, ActivityValidatingContext, EventTypes, SyntaxNames} from "../models"; import {htmlEncode} from "../utils/utils"; +import Ajv from "ajv" export class HttpEndpointPlugin implements ElsaPlugin { constructor() { eventBus.on(EventTypes.ActivityDesignDisplaying, this.onActivityDisplaying); + eventBus.on(EventTypes.ActivityPluginUpdated, this.onActivityUpdated); + eventBus.on(EventTypes.ActivityPluginValidating, this.onActivityValidating); } onActivityDisplaying(context: ActivityDesignDisplayContext) { @@ -20,4 +22,53 @@ export class HttpEndpointPlugin implements ElsaPlugin { const bodyDisplay = htmlEncode(path.expressions[syntax]); context.bodyDisplay = `

${bodyDisplay}

`; } + + onActivityUpdated(context: ActivityUpdatedContext) { + const activityModel = context.activityModel; + if (activityModel.type !== 'HttpEndpoint') + return; + + const props = activityModel.properties || []; + const prop = props.find(x => x.name == 'Schema') || { name: 'Schema', expressions: { 'Literal': '', syntax: SyntaxNames.Literal } }; + prop.expressions[SyntaxNames.Literal] = context.data; + eventBus.emit(EventTypes.UpdateActivity, this, prop); + } + + onActivityValidating(context: ActivityValidatingContext) { + + if (context.activityType !== 'HttpEndpoint' || context.prop !== 'Schema') + return; + + const jsonSchema = context.value; + let isValid = true; + + if (jsonSchema == '') return; + + const ajv = new Ajv(); + let json: object; + try{ + json = JSON.parse(jsonSchema) + } + catch (e){ + isValid = false; + } + + if (json != undefined) + { + try { + const validate = ajv.compile(json); + const errors = validate.errors; + if (errors != null) + isValid = false; + } + catch (e){ + const err = e; + isValid = false; + } + } + + context.isValidated = true; + context.isPositiveResult = isValid; + context.data = isValid ? 'Json is valid' : 'Json is invalid'; + } } diff --git a/src/server/Elsa.Server.Api/Extensions/ServiceCollectionExtensions.cs b/src/server/Elsa.Server.Api/Extensions/ServiceCollectionExtensions.cs index f8feeb3d7..2b81945da 100644 --- a/src/server/Elsa.Server.Api/Extensions/ServiceCollectionExtensions.cs +++ b/src/server/Elsa.Server.Api/Extensions/ServiceCollectionExtensions.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using Elsa; using Elsa.Models; using Elsa.Server.Api; @@ -7,6 +6,7 @@ using Elsa.Server.Api.Handlers; using Elsa.Server.Api.Mapping; using Elsa.Server.Api.Services; using Elsa.Server.Api.Swagger.Examples; +using Elsa.Services; using Microsoft.AspNetCore.Mvc; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; @@ -48,14 +48,15 @@ namespace Microsoft.Extensions.DependencyInjection options.AssumeDefaultVersionWhenUnspecified = true; }); - services.AddSingleton(); - services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); - services.AddSingleton(); - services.AddAutoMapperProfile(); - services.AddNotificationHandlersFrom(); - services.AddSignalR(); + services + .AddSingleton() + .AddSingleton() + .AddScoped() + .AddScoped() + .AddSingleton() + .AddAutoMapperProfile() + .AddNotificationHandlersFrom() + .AddSignalR(); return services; }