This commit is contained in:
axeleron007 2021-09-21 14:04:37 +03:00
parent 1f4f5f695b
commit 04bacaa281
13 changed files with 145 additions and 39 deletions

View file

@ -64,6 +64,12 @@ namespace Elsa.Activities.Http
OptionsProvider = typeof(HttpEndpoint))]
public string? Schema { get; set; }
[ActivityInput(
Category = PropertyCategories.Advanced,
DefaultValue = "https://extendsclass.com/json-schema-validator.html",
UIHint = ActivityInputUIHints.Url)]
public string? OnlineConverter { get; set; }
[ActivityInput(
Hint = "Check to allow authenticated requests only",
SupportedSyntaxes = new[] { SyntaxNames.Literal, SyntaxNames.JavaScript, SyntaxNames.Liquid },

View file

@ -10,6 +10,7 @@ namespace Elsa.Design
public const string Dropdown = "dropdown";
public const string MultiText = "multi-text";
public const string CodeEditor = "code-editor";
public const string Url = "url";
/// <summary>
/// An editor that allows the user to write a blob of JSON.

View file

@ -228,6 +228,10 @@ export namespace Components {
"hide": () => Promise<void>;
"show": (options: ToastNotificationOptions) => Promise<void>;
}
interface ElsaUrlProperty {
"propertyDescriptor": ActivityPropertyDescriptor;
"propertyModel": ActivityDefinitionProperty;
}
interface ElsaWebhookDefinitionEditorNotifications {
}
interface ElsaWebhookDefinitionEditorScreen {
@ -575,6 +579,12 @@ declare global {
prototype: HTMLElsaToastNotificationElement;
new (): HTMLElsaToastNotificationElement;
};
interface HTMLElsaUrlPropertyElement extends Components.ElsaUrlProperty, HTMLStencilElement {
}
var HTMLElsaUrlPropertyElement: {
prototype: HTMLElsaUrlPropertyElement;
new (): HTMLElsaUrlPropertyElement;
};
interface HTMLElsaWebhookDefinitionEditorNotificationsElement extends Components.ElsaWebhookDefinitionEditorNotifications, HTMLStencilElement {
}
var HTMLElsaWebhookDefinitionEditorNotificationsElement: {
@ -718,6 +728,7 @@ declare global {
"elsa-tab-content": HTMLElsaTabContentElement;
"elsa-tab-header": HTMLElsaTabHeaderElement;
"elsa-toast-notification": HTMLElsaToastNotificationElement;
"elsa-url-property": HTMLElsaUrlPropertyElement;
"elsa-webhook-definition-editor-notifications": HTMLElsaWebhookDefinitionEditorNotificationsElement;
"elsa-webhook-definition-editor-screen": HTMLElsaWebhookDefinitionEditorScreenElement;
"elsa-webhook-definitions-list-screen": HTMLElsaWebhookDefinitionsListScreenElement;
@ -950,6 +961,10 @@ declare namespace LocalJSX {
}
interface ElsaToastNotification {
}
interface ElsaUrlProperty {
"propertyDescriptor"?: ActivityPropertyDescriptor;
"propertyModel"?: ActivityDefinitionProperty;
}
interface ElsaWebhookDefinitionEditorNotifications {
}
interface ElsaWebhookDefinitionEditorScreen {
@ -1091,6 +1106,7 @@ declare namespace LocalJSX {
"elsa-tab-content": ElsaTabContent;
"elsa-tab-header": ElsaTabHeader;
"elsa-toast-notification": ElsaToastNotification;
"elsa-url-property": ElsaUrlProperty;
"elsa-webhook-definition-editor-notifications": ElsaWebhookDefinitionEditorNotifications;
"elsa-webhook-definition-editor-screen": ElsaWebhookDefinitionEditorScreen;
"elsa-webhook-definitions-list-screen": ElsaWebhookDefinitionsListScreen;
@ -1154,6 +1170,7 @@ declare module "@stencil/core" {
"elsa-tab-content": LocalJSX.ElsaTabContent & JSXBase.HTMLAttributes<HTMLElsaTabContentElement>;
"elsa-tab-header": LocalJSX.ElsaTabHeader & JSXBase.HTMLAttributes<HTMLElsaTabHeaderElement>;
"elsa-toast-notification": LocalJSX.ElsaToastNotification & JSXBase.HTMLAttributes<HTMLElsaToastNotificationElement>;
"elsa-url-property": LocalJSX.ElsaUrlProperty & JSXBase.HTMLAttributes<HTMLElsaUrlPropertyElement>;
"elsa-webhook-definition-editor-notifications": LocalJSX.ElsaWebhookDefinitionEditorNotifications & JSXBase.HTMLAttributes<HTMLElsaWebhookDefinitionEditorNotificationsElement>;
"elsa-webhook-definition-editor-screen": LocalJSX.ElsaWebhookDefinitionEditorScreen & JSXBase.HTMLAttributes<HTMLElsaWebhookDefinitionEditorScreenElement>;
"elsa-webhook-definitions-list-screen": LocalJSX.ElsaWebhookDefinitionsListScreen & JSXBase.HTMLAttributes<HTMLElsaWebhookDefinitionsListScreenElement>;

View file

@ -3,7 +3,8 @@
export enum WorkflowDesignerMode {
Edit,
Instance,
Blueprint
Blueprint,
Test
}
export interface ActivityContextMenuState {

View file

@ -0,0 +1,49 @@
import {Component, h, Prop, State} from '@stencil/core';
import {ActivityDefinitionProperty, ActivityPropertyDescriptor, SyntaxNames} from "../../../../models";
@Component({
tag: 'elsa-url-property',
shadow: false,
})
export class ElsaUrlProperty {
@Prop() propertyDescriptor: ActivityPropertyDescriptor;
@Prop() propertyModel: ActivityDefinitionProperty;
@State() currentValue: string;
componentWillLoad() {
const defaultSyntax = this.propertyDescriptor.defaultSyntax || SyntaxNames.Literal;
this.currentValue = this.propertyModel.expressions[defaultSyntax] || undefined;
}
onUrlClick(e: MouseEvent, url: string) {
e.preventDefault();
window.open(url, '_blank', 'noopener,noreferrer');
}
render() {
const propertyDescriptor = this.propertyDescriptor;
const propertyModel = this.propertyModel;
const propertyName = propertyDescriptor.name;
const fieldLabel = propertyDescriptor.label || propertyName;
let value = this.currentValue;
if (value == undefined) {
const defaultValue = this.propertyDescriptor.defaultValue;
value = defaultValue ? defaultValue.toString() : undefined;
}
return (
<elsa-property-editor propertyDescriptor={propertyDescriptor}
propertyModel={propertyModel}
single-line={true}
showLabel={false}>
<a href="#"
onClick={e => this.onUrlClick(e, value)}
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">
{fieldLabel}
</a>
</elsa-property-editor>
)
}
}

View file

@ -46,6 +46,7 @@ export class ElsaWorkflowDefinitionEditorScreen {
@State() imported: boolean;
@State() networkError: string;
@State() selectedActivityId?: string;
@State() workflowDesignerMode: WorkflowDesignerMode.Edit;
@State() activityContextMenuState: ActivityContextMenuState = {
shown: false,
@ -435,7 +436,7 @@ export class ElsaWorkflowDefinitionEditorScreen {
return (
<div class="elsa-flex-1 elsa-flex elsa-relative">
<elsa-designer-tree model={this.workflowModel}
mode={WorkflowDesignerMode.Edit}
mode={this.workflowDesignerMode}
activityContextMenuButton={activityContextMenuButton}
onActivityContextMenuButtonClicked={e => this.onActivityContextMenuButtonClicked(e)}
onConnectionContextMenuButtonClicked={e => this.onConnectionContextMenuButtonClicked(e)}

View file

@ -1,19 +1,14 @@
import {Component, Prop, h, Method, State, Watch, Host} from '@stencil/core';
import {Component, Prop, h, State, Watch, Host} from '@stencil/core';
import {HubConnection, HubConnectionBuilder} from '@microsoft/signalr';
import * as collection from 'lodash/collection';
import {EventTypes, WorkflowDefinition, WorkflowDefinitionSummary, WorkflowTestActivityMessage, WorkflowTestUpdateRequest} from "../../../../models";
import {EventTypes, WorkflowDefinition, WorkflowTestActivityMessage, WorkflowTestUpdateRequest} from "../../../../models";
import {i18n} from "i18next";
import {loadTranslations} from "../../../i18n/i18n-loader";
import {resources} from "./localizations";
import {createElsaClient, eventBus, WorkflowTestExecuteRequest} from "../../../../services";
import Tunnel from "../../../../data/dashboard";
import {convert} from 'json-to-json-schema';
interface Tab {
id: string;
text: string;
view: () => any;
}
import {clip} from "../../../../utils/utils";
@Component({
tag: 'elsa-workflow-test-panel',
@ -39,7 +34,6 @@ export class ElsaWorkflowTestPanel {
@Watch('workflowTestActivityId')
async workflowTestActivityMessageChangedHandler(newMessage: string, oldMessage: string) {
const message = this.workflowTestActivityMessages.find(x => x.activityId == newMessage);
debugger;
this.message = !!message ? message : null;
}
@ -84,8 +78,9 @@ export class ElsaWorkflowTestPanel {
await elsaClient.workflowTestApi.execute(request);
}
async onUseAsSchemaClick() {
const value = this.message.data["Inbound Request"];
async onUseAsSchemaClick() {
debugger
const value = this.message.data["Body"];
const request: WorkflowTestUpdateRequest = {
activityId: this.message.activityId,
jsonSchema: JSON.stringify(convert(value), null, 4)
@ -100,11 +95,15 @@ export class ElsaWorkflowTestPanel {
return (
<Host>
<button type="button"
onClick={() => this.onExecuteWorkflowClick()}
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('ExecuteWorkflow')}
</button>
<dl class="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">
<button type="button"
onClick={() => this.onExecuteWorkflowClick()}
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('ExecuteWorkflow')}
</button>
</div>
</dl>
{this.renderActivityTestMessage()}
</Host>
);
@ -150,30 +149,33 @@ export class ElsaWorkflowTestPanel {
filteredData[key] = valueText;
}
const isInboundRequest = dataKey === "Inbound Request";
const hasBody = dataKey === "Body";
return (
return (
<dl class="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">{'Correlation Id'}</dt>
<dd class="elsa-text-gray-900">{message.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('Status')}</dt>
<dd class="elsa-text-gray-900">{message.status}</dd>
</div>
{collection.map(filteredData, (v, k) => (
<div class="elsa-py-3 elsa-flex elsa-justify-between elsa-text-sm elsa-font-medium">
<dt class="elsa-text-gray-500">
<pre onClick={e => clip(e.currentTarget)}>{this.serverUrl + '/workflows' + message.path + '?correlation=' + message.correlationId}</pre>
</dt>
</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">{message.status}</dd>
</div>
{collection.map(filteredData, (v, k) => (
<div>
<div class="elsa-py-3 elsa-flex elsa-justify-between elsa-text-sm elsa-font-medium">
<dt class="elsa-text-gray-500">{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 ?
<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>
{hasBody ?
<div class="elsa-py-3 elsa-flex elsa-justify-between elsa-text-sm elsa-font-medium">
<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
}

View file

@ -9,3 +9,4 @@ export * from './null-property-driver';
export * from './radio-list-driver';
export * from './single-line-driver';
export * from './switch-case-builder-driver';
export * from './url-driver';

View file

@ -0,0 +1,12 @@
import {PropertyDisplayDriver} from "../services";
import {ActivityModel, ActivityPropertyDescriptor} from "../models";
import {h} from "@stencil/core";
import {getOrCreateProperty} from "../utils/utils";
export class UrlDriver implements PropertyDisplayDriver {
display(activity: ActivityModel, property: ActivityPropertyDescriptor) {
const prop = getOrCreateProperty(activity, property.name);
return <elsa-url-property propertyDescriptor={property} propertyModel={prop}/>;
}
}

View file

@ -324,6 +324,7 @@ export class SyntaxNames {
}
export interface WorkflowTestActivityMessage {
path?: string;
correlationId: string;
activityId: string;
status: string;

View file

@ -1,5 +1,5 @@
import {propertyDisplayManager, ElsaPlugin, PropertyDisplayDriver} from "../services";
import {JsonDriver, RadioListDriver, CheckboxDriver, CheckListDriver, CodeEditorDriver, DropdownDriver, MultilineDriver, MultiTextDriver, SingleLineDriver, SwitchCaseBuilderDriver} from "../drivers";
import {JsonDriver, RadioListDriver, CheckboxDriver, CheckListDriver, CodeEditorDriver, DropdownDriver, MultilineDriver, MultiTextDriver, SingleLineDriver, SwitchCaseBuilderDriver, UrlDriver} from "../drivers";
import {ElsaStudio} from "../models";
export class DefaultDriversPlugin implements ElsaPlugin {
@ -12,8 +12,9 @@ export class DefaultDriversPlugin implements ElsaPlugin {
this.addDriver('checkbox', () => new CheckboxDriver());
this.addDriver('dropdown', () => new DropdownDriver());
this.addDriver('multi-text', () => new MultiTextDriver());
this.addDriver('code-editor', () => new CodeEditorDriver());
this.addDriver('code-editor', () => new CodeEditorDriver());
this.addDriver('switch-case-builder', () => new SwitchCaseBuilderDriver());
this.addDriver('url', () => new UrlDriver());
}
addDriver<T extends PropertyDisplayDriver>(controlType: string, c: (elsaStudio: ElsaStudio) => T) {

View file

@ -1,3 +1,5 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Events;
@ -22,6 +24,7 @@ namespace Elsa.Server.Api.Handlers
public async Task Handle(ActivityExecutionResultExecuted notification, CancellationToken cancellationToken)
{
var context = notification.ActivityExecutionContext;
string? path = default;
var signalRConnectionId = context.WorkflowExecutionContext.WorkflowBlueprint.SignalRConnectionId;
if (string.IsNullOrWhiteSpace(signalRConnectionId)) return;
@ -31,11 +34,21 @@ namespace Elsa.Server.Api.Handlers
["Outcomes"] = JToken.FromObject(context.Outcomes)
};
foreach (var entry in context.JournalData)
data[entry.Key] = entry.Value != null ? JToken.FromObject(entry.Value) : JValue.CreateNull();
var body = context.Input != null? ((dynamic)context.Input).Body : null;
if (body != null)
data["Body"] = JToken.FromObject(body);
var activityData = context.WorkflowInstance.ActivityData.FirstOrDefault();
var pathProperty = activityData.Value.FirstOrDefault(x => x.Key == "Path");
if (pathProperty.Value != null)
{
path = pathProperty.Value.ToString();
}
var message = new WorkflowTestMessage
{
Path = path,
CorrelationId = context.CorrelationId,
ActivityId = context.ActivityId,
Status = context.WorkflowExecutionContext.Status == WorkflowStatus.Running

View file

@ -2,6 +2,7 @@ namespace Elsa.Server.Api.Models
{
public class WorkflowTestMessage
{
public string? Path { get; set; }
public string CorrelationId { get; set; } = default!;
public string ActivityId { get; set; } = default!;
public string Status { get; set; } = default!;