JavaScript intellisense (#3639)
* Incremental work * Incremental work * Incremental work * Initial support for intellisense * Implement variable type definition provider
This commit is contained in:
parent
6759a0d022
commit
28e9922060
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "@elsa-workflows/elsa-workflows-designer",
|
||||
"version": "3.0.2-preview.36",
|
||||
"version": "3.0.2-preview.40",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@elsa-workflows/elsa-workflows-designer",
|
||||
"version": "3.0.2-preview.2",
|
||||
"version": "3.0.2-preview.40",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@antv/layout": "^0.3.11",
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
"@stencil/core": "^2.20.0",
|
||||
"@stencil/state-tunnel": "^1.0.1",
|
||||
"@stencil/store": "^2.0.1",
|
||||
"async-mutex": "^0.4.0",
|
||||
"axios": "^0.27.2",
|
||||
"axios-middleware": "^0.3.1",
|
||||
"el-transition": "^0.0.7",
|
||||
|
|
@ -392,6 +393,14 @@
|
|||
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/async-mutex": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.4.0.tgz",
|
||||
"integrity": "sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
|
|
@ -1614,6 +1623,11 @@
|
|||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||
},
|
||||
"node_modules/typedi": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/typedi/-/typedi-0.10.0.tgz",
|
||||
|
|
@ -1980,6 +1994,14 @@
|
|||
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
||||
"dev": true
|
||||
},
|
||||
"async-mutex": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.4.0.tgz",
|
||||
"integrity": "sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==",
|
||||
"requires": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
|
|
@ -2850,6 +2872,11 @@
|
|||
"is-number": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||
},
|
||||
"typedi": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/typedi/-/typedi-0.10.0.tgz",
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
"@stencil/core": "^2.20.0",
|
||||
"@stencil/state-tunnel": "^1.0.1",
|
||||
"@stencil/store": "^2.0.1",
|
||||
"async-mutex": "^0.4.0",
|
||||
"axios": "^0.27.2",
|
||||
"axios-middleware": "^0.3.1",
|
||||
"el-transition": "^0.0.7",
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@ import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
|
|||
import { ActivityDefinition, ActivityDefinitionPropsUpdatedArgs, ActivityDefinitionSummary, ActivityDefinitionUpdatedArgs } from "./modules/activity-definitions/models";
|
||||
import { ActivityDriverRegistry } from "./services";
|
||||
import { Activity, ActivityDeletedArgs, ActivitySelectedArgs, ChildActivitySelectedArgs, ContainerSelectedArgs, EditChildActivityArgs, GraphUpdatedArgs, IntellisenseContext, SelectListItem, TabChangedArgs, TabDefinition, Variable, WorkflowInstance, WorkflowInstanceSummary, WorkflowUpdatedArgs } from "./models";
|
||||
import { WorkflowDefinition, WorkflowDefinitionSummary } from "./modules/workflow-definitions/models/entities";
|
||||
import { ActivityUpdatedArgs, DeleteActivityRequestedArgs, Widget, WorkflowDefinitionPropsUpdatedArgs, WorkflowDefinitionUpdatedArgs } from "./modules/workflow-definitions/models/ui";
|
||||
import { PublishClickedArgs } from "./modules/activity-definitions/components/publish-button";
|
||||
import { NotificationType } from "./modules/notifications/models";
|
||||
import { Button } from "./components/shared/button-group/models";
|
||||
import { ActivityInputContext } from "./services/node-input-driver";
|
||||
import { ActivityInputContext } from "./services/activity-input-driver";
|
||||
import { ContextMenuAnchorPoint, ContextMenuItem } from "./components/shared/context-menu/models";
|
||||
import { DropdownButtonItem, DropdownButtonOrigin } from "./components/shared/dropdown-button/models";
|
||||
import { WorkflowDefinition, WorkflowDefinitionSummary } from "./modules/workflow-definitions/models/entities";
|
||||
import { Graph } from "@antv/x6";
|
||||
import { AddActivityArgs, FlowchartPathItem, LayoutDirection, RenameActivityArgs, UpdateActivityArgs } from "./modules/flowchart/models";
|
||||
import { OutNode } from "@antv/layout";
|
||||
|
|
@ -25,7 +25,7 @@ import { ExpressionChangedArs } from "./components/designer/input-control-switch
|
|||
import { SignedInArgs } from "./modules/login/models";
|
||||
import { ModalActionClickArgs, ModalActionDefinition, ModalDialogInstance } from "./components/shared/modal-dialog/models";
|
||||
import { ModalType } from "./components/shared/modal-dialog/modal-type";
|
||||
import { MonacoLib, MonacoValueChangedArgs } from "./components/shared/monaco-editor/monaco-editor";
|
||||
import { MonacoValueChangedArgs } from "./components/shared/monaco-editor/monaco-editor";
|
||||
import { PagerData } from "./components/shared/pager/pager";
|
||||
import { PanelPosition, PanelStateChangedArgs } from "./components/panel/models";
|
||||
import { PublishClickedArgs as PublishClickedArgs1 } from "./modules/workflow-definitions/components/publish-button";
|
||||
|
|
@ -54,10 +54,11 @@ export namespace Components {
|
|||
}
|
||||
interface ElsaActivityPropertiesEditor {
|
||||
"activity"?: Activity;
|
||||
"containerId": string;
|
||||
"containerType": string;
|
||||
"hide": () => Promise<void>;
|
||||
"show": () => Promise<void>;
|
||||
"variables": Array<Variable>;
|
||||
"workflowDefinition": WorkflowDefinition;
|
||||
}
|
||||
interface ElsaActivityPublishButton {
|
||||
"publishing": boolean;
|
||||
|
|
@ -135,8 +136,11 @@ export namespace Components {
|
|||
interface ElsaHomePage {
|
||||
}
|
||||
interface ElsaInputControlSwitch {
|
||||
"activityType": string;
|
||||
"codeEditorHeight": string;
|
||||
"codeEditorSingleLineMode": boolean;
|
||||
"containerId": string;
|
||||
"containerType": string;
|
||||
"context"?: IntellisenseContext;
|
||||
"defaultSyntax": string;
|
||||
"expression"?: string;
|
||||
|
|
@ -145,6 +149,7 @@ export namespace Components {
|
|||
"hint": string;
|
||||
"isReadOnly"?: boolean;
|
||||
"label": string;
|
||||
"propertyName": string;
|
||||
"supportedSyntaxes": Array<string>;
|
||||
"syntax"?: string;
|
||||
}
|
||||
|
|
@ -180,11 +185,11 @@ export namespace Components {
|
|||
interface ElsaModalDialogContainer {
|
||||
}
|
||||
interface ElsaMonacoEditor {
|
||||
"addJavaScriptLib": (libSource: string, libUri: string) => Promise<void>;
|
||||
"editorHeight": string;
|
||||
"language": string;
|
||||
"monacoLibPath"?: string;
|
||||
"padding": string;
|
||||
"setJavaScriptLibs": (libs: Array<MonacoLib>) => Promise<void>;
|
||||
"setValue": (value: string) => Promise<void>;
|
||||
"singleLineMode": boolean;
|
||||
"value": string;
|
||||
}
|
||||
|
|
@ -879,10 +884,11 @@ declare namespace LocalJSX {
|
|||
}
|
||||
interface ElsaActivityPropertiesEditor {
|
||||
"activity"?: Activity;
|
||||
"containerId"?: string;
|
||||
"containerType"?: string;
|
||||
"onActivityUpdated"?: (event: ElsaActivityPropertiesEditorCustomEvent<ActivityUpdatedArgs>) => void;
|
||||
"onDeleteActivityRequested"?: (event: ElsaActivityPropertiesEditorCustomEvent<DeleteActivityRequestedArgs>) => void;
|
||||
"variables"?: Array<Variable>;
|
||||
"workflowDefinition"?: WorkflowDefinition;
|
||||
}
|
||||
interface ElsaActivityPublishButton {
|
||||
"onExportClicked"?: (event: ElsaActivityPublishButtonCustomEvent<any>) => void;
|
||||
|
|
@ -962,8 +968,11 @@ declare namespace LocalJSX {
|
|||
interface ElsaHomePage {
|
||||
}
|
||||
interface ElsaInputControlSwitch {
|
||||
"activityType"?: string;
|
||||
"codeEditorHeight"?: string;
|
||||
"codeEditorSingleLineMode"?: boolean;
|
||||
"containerId"?: string;
|
||||
"containerType"?: string;
|
||||
"context"?: IntellisenseContext;
|
||||
"defaultSyntax"?: string;
|
||||
"expression"?: string;
|
||||
|
|
@ -974,6 +983,7 @@ declare namespace LocalJSX {
|
|||
"label"?: string;
|
||||
"onExpressionChanged"?: (event: ElsaInputControlSwitchCustomEvent<ExpressionChangedArs>) => void;
|
||||
"onSyntaxChanged"?: (event: ElsaInputControlSwitchCustomEvent<string>) => void;
|
||||
"propertyName"?: string;
|
||||
"supportedSyntaxes"?: Array<string>;
|
||||
"syntax"?: string;
|
||||
}
|
||||
|
|
@ -1016,7 +1026,6 @@ declare namespace LocalJSX {
|
|||
interface ElsaMonacoEditor {
|
||||
"editorHeight"?: string;
|
||||
"language"?: string;
|
||||
"monacoLibPath"?: string;
|
||||
"onValueChanged"?: (event: ElsaMonacoEditorCustomEvent<MonacoValueChangedArgs>) => void;
|
||||
"padding"?: string;
|
||||
"singleLineMode"?: boolean;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import {MonacoValueChangedArgs} from "../../shared/monaco-editor/monaco-editor";
|
|||
import {Hint} from "../../shared/forms/hint";
|
||||
import {mapSyntaxToLanguage} from "../../../utils";
|
||||
import descriptorsStore from "../../../data/descriptors-store";
|
||||
import {Container} from "typedi";
|
||||
import {ElsaClient, ElsaClientProvider} from "../../../services/api-client/elsa-client";
|
||||
import InputControlSwitchContextState from "./state";
|
||||
|
||||
export interface ExpressionChangedArs {
|
||||
expression: string;
|
||||
|
|
@ -27,6 +30,12 @@ export class InputControlSwitch {
|
|||
this.onExpressionChangedDebounced = debounce(this.onExpressionChanged, 10);
|
||||
}
|
||||
|
||||
// Tunneled props.
|
||||
@Prop() containerType: string;
|
||||
@Prop() containerId: string;
|
||||
@Prop() activityType: string;
|
||||
@Prop() propertyName: string;
|
||||
|
||||
@Prop() label: string;
|
||||
@Prop() hideLabel: boolean;
|
||||
@Prop() hint: string;
|
||||
|
|
@ -53,10 +62,21 @@ export class InputControlSwitch {
|
|||
this.closeContextMenu();
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
async componentWillLoad() {
|
||||
this.currentExpression = this.expression;
|
||||
}
|
||||
|
||||
async componentDidLoad() {
|
||||
const elsaClient = await Container.get(ElsaClientProvider).getElsaClient();
|
||||
const containerType = this.containerType;
|
||||
const containerId = this.containerId;
|
||||
const activityTypeName = this.activityType;
|
||||
const propertyName = this.propertyName;
|
||||
const typeDefinitions = await elsaClient.scripting.javaScriptApi.getTypeDefinitions({containerType, containerId, activityTypeName, propertyName});
|
||||
const libUri = 'defaultLib:lib.es6.d.ts';
|
||||
await this.monacoEditor.addJavaScriptLib(typeDefinitions, libUri);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
if (this.hideLabel && !this.shouldRenderMonaco()) {
|
||||
|
|
@ -200,3 +220,5 @@ export class InputControlSwitch {
|
|||
this.expressionChanged.emit({expression, syntax: this.syntax});
|
||||
}
|
||||
}
|
||||
|
||||
InputControlSwitchContextState.injectProps(InputControlSwitch, ['containerType', 'containerId', 'activityType', 'propertyName'])
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
import {h} from "@stencil/core";
|
||||
import {createProviderConsumer} from "@stencil/state-tunnel";
|
||||
|
||||
export interface InputControlSwitchContextState {
|
||||
containerType: string;
|
||||
containerId: string;
|
||||
activityType: string;
|
||||
propertyName: string;
|
||||
}
|
||||
|
||||
export default createProviderConsumer<InputControlSwitchContextState>(
|
||||
{
|
||||
containerType: null,
|
||||
containerId: null,
|
||||
activityType: null,
|
||||
propertyName: null
|
||||
},
|
||||
(subscribe, child) => (<context-consumer subscribe={subscribe} renderer={child}/>)
|
||||
);
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import {Component, h, Prop, State} from '@stencil/core';
|
||||
import {uniq} from 'lodash'
|
||||
import {JsonExpression, LiteralExpression, SelectList, SyntaxNames} from "../../models";
|
||||
import {ActivityInputContext} from "../../services/node-input-driver";
|
||||
import {ActivityInputContext} from "../../services/activity-input-driver";
|
||||
import {getSelectListItems, getInputPropertyValue, parseJson} from "../../utils";
|
||||
import {ExpressionChangedArs} from "../designer/input-control-switch/input-control-switch";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {Component, Prop, h, State} from '@stencil/core';
|
||||
import {LiteralExpression, SyntaxNames} from "../../models";
|
||||
import {ActivityInputContext} from "../../services/node-input-driver";
|
||||
import {ActivityInputContext} from "../../services/activity-input-driver";
|
||||
import {getInputPropertyValue } from "../../utils";
|
||||
import {ExpressionChangedArs} from "../designer/input-control-switch/input-control-switch";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {Component, h, Prop} from '@stencil/core';
|
||||
import {EditorHeight, LiteralExpression, SyntaxNames} from "../../models";
|
||||
import {ActivityInputContext} from "../../services/node-input-driver";
|
||||
import {ActivityInputContext} from "../../services/activity-input-driver";
|
||||
import {getInputPropertyValue} from "../../utils";
|
||||
import {MonacoValueChangedArgs} from "../shared/monaco-editor/monaco-editor";
|
||||
import {ExpressionChangedArs} from "../designer/input-control-switch/input-control-switch";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {Component, Prop, h} from '@stencil/core';
|
||||
import {LiteralExpression, SelectList, SyntaxNames} from "../../models";
|
||||
import {ActivityInputContext} from "../../services/node-input-driver";
|
||||
import {ActivityInputContext} from "../../services/activity-input-driver";
|
||||
import {getInputPropertyValue, getSelectListItems} from "../../utils";
|
||||
import {ExpressionChangedArs} from "../designer/input-control-switch/input-control-switch";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {Component, Prop, h} from '@stencil/core';
|
||||
import {LiteralExpression, SyntaxNames} from "../../models";
|
||||
import {ActivityInputContext} from "../../services/node-input-driver";
|
||||
import {ActivityInputContext} from "../../services/activity-input-driver";
|
||||
import {getInputPropertyValue} from "../../utils";
|
||||
import {ExpressionChangedArs} from "../designer/input-control-switch/input-control-switch";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {Component, Prop, h} from '@stencil/core';
|
||||
import {JsonExpression, LiteralExpression, SyntaxNames} from "../../models";
|
||||
import {ActivityInputContext} from "../../services/node-input-driver";
|
||||
import {ActivityInputContext} from "../../services/activity-input-driver";
|
||||
import {getInputPropertyValue, parseJson} from "../../utils";
|
||||
import {ExpressionChangedArs} from "../designer/input-control-switch/input-control-switch";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {Component, h, Prop, State} from '@stencil/core';
|
||||
import {LiteralExpression, SelectList, SyntaxNames} from "../../models";
|
||||
import {ActivityInputContext} from "../../services/node-input-driver";
|
||||
import {ActivityInputContext} from "../../services/activity-input-driver";
|
||||
import {getSelectListItems, getInputPropertyValue} from "../../utils";
|
||||
import {ExpressionChangedArs} from "../designer/input-control-switch/input-control-switch";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {Component, h, Prop} from '@stencil/core';
|
||||
import {LiteralExpression, SyntaxNames} from "../../models";
|
||||
import {ActivityInputContext} from "../../services/node-input-driver";
|
||||
import {ActivityInputContext} from "../../services/activity-input-driver";
|
||||
import {getInputPropertyValue} from "../../utils";
|
||||
import {ExpressionChangedArs} from "../designer/input-control-switch/input-control-switch";
|
||||
import descriptorsStore from "../../data/descriptors-store";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import {Component, Prop, h} from '@stencil/core';
|
||||
import {groupBy} from 'lodash';
|
||||
import {LiteralExpression, SyntaxNames} from "../../models";
|
||||
import {ActivityInputContext} from "../../services/node-input-driver";
|
||||
import {ActivityInputContext} from "../../services/activity-input-driver";
|
||||
import {getInputPropertyValue} from "../../utils";
|
||||
import descriptorsStore from '../../data/descriptors-store';
|
||||
import {VariableDescriptor} from "../../services/api-client/variable-descriptors-api";
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import {Component, Prop, h} from '@stencil/core';
|
||||
import {SyntaxNames, Variable} from "../../models";
|
||||
import {ActivityInputContext} from "../../services/node-input-driver";
|
||||
import {ActivityInputContext} from "../../services/activity-input-driver";
|
||||
import {getPropertyValue} from "../../utils";
|
||||
import {FormEntry} from "../shared/forms/form-entry";
|
||||
import WorkflowDefinitionTunnel from "../../state/workflow-definition-state";
|
||||
import WorkflowDefinitionTunnel from "../../modules/workflow-definitions/state";
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-variable-picker-input',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import {Component, Event, EventEmitter, h, Host, Method, Prop, Watch} from '@stencil/core';
|
||||
import {initializeMonacoWorker, Monaco} from "./utils";
|
||||
import { Component, Event, EventEmitter, h, Host, Method, Prop, Watch } from '@stencil/core';
|
||||
import { initializeMonacoWorker, Monaco, EditorVariables } from "./utils";
|
||||
import monacoStore from '../../../data/monaco-store';
|
||||
|
||||
export interface MonacoValueChangedArgs {
|
||||
|
|
@ -7,28 +7,23 @@ export interface MonacoValueChangedArgs {
|
|||
markers: Array<any>;
|
||||
}
|
||||
|
||||
export interface MonacoLib {
|
||||
content: string;
|
||||
filePath?: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-monaco-editor',
|
||||
styleUrl: 'monaco-editor.scss',
|
||||
shadow: false
|
||||
})
|
||||
export class MonacoEditor {
|
||||
export class ElsaMonaco {
|
||||
private monaco: Monaco;
|
||||
private container: HTMLElement;
|
||||
private editor: any;
|
||||
|
||||
@Prop() public monacoLibPath?: string;
|
||||
@Prop() public editorHeight: string = '10em';
|
||||
@Prop() public value: string;
|
||||
@Prop() public language: string;
|
||||
@Prop() public singleLineMode: boolean = false;
|
||||
@Prop() public padding: string;
|
||||
@Event() public valueChanged: EventEmitter<MonacoValueChangedArgs>;
|
||||
@Prop({ attribute: 'editor-height', reflect: true }) editorHeight: string = '5em';
|
||||
@Prop() value: string;
|
||||
@Prop() language: string;
|
||||
@Prop({ attribute: 'single-line', reflect: true }) singleLineMode: boolean = false;
|
||||
@Prop() padding: string;
|
||||
@Event({ eventName: 'valueChanged' }) valueChanged: EventEmitter<MonacoValueChangedArgs>;
|
||||
|
||||
container: HTMLElement;
|
||||
editor: any;
|
||||
|
||||
@Watch('language')
|
||||
languageChangeHandler(newValue: string) {
|
||||
|
|
@ -40,35 +35,46 @@ export class MonacoEditor {
|
|||
}
|
||||
|
||||
@Method()
|
||||
async setJavaScriptLibs(libs: Array<MonacoLib>) {
|
||||
async setValue(value: string) {
|
||||
if (!this.editor)
|
||||
return;
|
||||
|
||||
const model = this.editor.getModel();
|
||||
model.setValue(value || '');
|
||||
}
|
||||
|
||||
@Method()
|
||||
async addJavaScriptLib(libSource: string, libUri: string) {
|
||||
const monaco = this.monaco;
|
||||
const editor = this.editor;
|
||||
|
||||
const defaultLib: MonacoLib = {
|
||||
content: "<reference lib=\"es5\" />",
|
||||
filePath: "lib.d.ts"
|
||||
};
|
||||
monaco.languages.typescript.javascriptDefaults.setExtraLibs([{
|
||||
filePath: "lib.es5.d.ts"
|
||||
}, {
|
||||
content: libSource,
|
||||
filePath: libUri
|
||||
}]);
|
||||
|
||||
libs = [...libs];
|
||||
monaco.languages.typescript.javascriptDefaults.setExtraLibs(libs);
|
||||
const oldModel = monaco.editor.getModel(libUri);
|
||||
|
||||
for (const lib of libs) {
|
||||
if (oldModel)
|
||||
oldModel.dispose();
|
||||
|
||||
const oldModel = editor.getModel(lib.filePath);
|
||||
const newModel = monaco.editor.createModel(libSource, 'typescript', monaco.Uri.parse(libUri));
|
||||
const matches = libSource.matchAll(/declare const (\w+): (string|number)/g);
|
||||
|
||||
if (oldModel)
|
||||
oldModel.dispose();
|
||||
EditorVariables.splice(0, EditorVariables.length);
|
||||
|
||||
const newModel = monaco.editor.createModel(lib.content, 'typescript', monaco.Uri.parse(lib.filePath));
|
||||
for(const match of matches) {
|
||||
EditorVariables.push({variableName: match[1], type: match[2] });
|
||||
}
|
||||
}
|
||||
|
||||
public async componentWillLoad() {
|
||||
const monacoLibPath = this.monacoLibPath || monacoStore.monacoLibPath;
|
||||
async componentWillLoad() {
|
||||
const monacoLibPath = monacoStore.monacoLibPath;
|
||||
this.monaco = await initializeMonacoWorker(monacoLibPath);
|
||||
}
|
||||
|
||||
public async componentDidLoad() {
|
||||
componentDidLoad() {
|
||||
const monaco = this.monaco;
|
||||
const language = this.language;
|
||||
|
||||
|
|
@ -118,21 +124,18 @@ export class MonacoEditor {
|
|||
lineNumbersMinChars: 0,
|
||||
folding: false,
|
||||
scrollBeyondLastColumn: 0,
|
||||
scrollbar: {horizontal: 'hidden', vertical: 'hidden'},
|
||||
find: {addExtraSpaceOnTop: false, autoFindInSelection: 'never', seedSearchStringFromSelection: false},
|
||||
scrollbar: { horizontal: 'hidden', vertical: 'hidden' },
|
||||
find: { addExtraSpaceOnTop: false, autoFindInSelection: 'never', seedSearchStringFromSelection: false },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.editor = monaco.editor.create(this.container, options);
|
||||
|
||||
await this.setJavaScriptLibs([]);
|
||||
this.registerLiquid();
|
||||
|
||||
this.editor.onDidChangeModelContent(e => {
|
||||
const value = this.editor.getValue();
|
||||
const markers = monaco.editor.getModelMarkers({owner: language});
|
||||
this.valueChanged.emit({value: value, markers: markers});
|
||||
const markers = monaco.editor.getModelMarkers({ owner: language });
|
||||
this.valueChanged.emit({ value: value, markers: markers });
|
||||
});
|
||||
|
||||
if (this.singleLineMode) {
|
||||
|
|
@ -166,47 +169,13 @@ export class MonacoEditor {
|
|||
editor.dispose();
|
||||
}
|
||||
|
||||
registerLiquid() {
|
||||
const monaco = this.monaco;
|
||||
monaco.languages.register({id: 'liquid'});
|
||||
|
||||
monaco.languages.registerCompletionItemProvider('liquid', {
|
||||
provideCompletionItems: () => {
|
||||
const autocompleteProviderItems = [];
|
||||
const keywords = ['assign', 'capture', 'endcapture', 'increment', 'decrement',
|
||||
'if', 'else', 'elsif', 'endif', 'for', 'endfor', 'break',
|
||||
'continue', 'limit', 'offset', 'range', 'reversed', 'cols',
|
||||
'case', 'endcase', 'when', 'block', 'endblock', 'true', 'false',
|
||||
'in', 'unless', 'endunless', 'cycle', 'tablerow', 'endtablerow',
|
||||
'contains', 'startswith', 'endswith', 'comment', 'endcomment',
|
||||
'raw', 'endraw', 'editable', 'endentitylist', 'endentityview', 'endinclude',
|
||||
'endmarker', 'entitylist', 'entityview', 'forloop', 'image', 'include',
|
||||
'marker', 'outputcache', 'plugin', 'style', 'text', 'widget',
|
||||
'abs', 'append', 'at_least', 'at_most', 'capitalize', 'ceil', 'compact',
|
||||
'concat', 'date', 'default', 'divided_by', 'downcase', 'escape',
|
||||
'escape_once', 'first', 'floor', 'join', 'last', 'lstrip', 'map',
|
||||
'minus', 'modulo', 'newline_to_br', 'plus', 'prepend', 'remove',
|
||||
'remove_first', 'replace', 'replace_first', 'reverse', 'round',
|
||||
'rstrip', 'size', 'slice', 'sort', 'sort_natural', 'split', 'strip',
|
||||
'strip_html', 'strip_newlines', 'times', 'truncate', 'truncatewords',
|
||||
'uniq', 'upcase', 'url_decode', 'url_encode'];
|
||||
|
||||
for (let i = 0; i < keywords.length; i++) {
|
||||
autocompleteProviderItems.push({'label': keywords[i], kind: monaco.languages.CompletionItemKind.Keyword});
|
||||
}
|
||||
|
||||
return {suggestions: autocompleteProviderItems};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const padding = this.padding || 'pt-1.5 pl-1';
|
||||
return (
|
||||
<Host
|
||||
class="monaco-editor-host border focus:ring-blue-500 focus:border-blue-500 block w-full min-w-0 rounded-md sm:text-sm border-gray-300 p-4"
|
||||
style={{'min-height': this.editorHeight}}>
|
||||
<div ref={el => this.container = el} class={`monaco-editor-container ${padding}`}/>
|
||||
style={{ 'min-height': this.editorHeight }}>
|
||||
<div ref={el => this.container = el} class={`monaco-editor-container ${padding}`} />
|
||||
</Host>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
const win = window as any;
|
||||
import { Mutex } from "async-mutex";
|
||||
const win = window as any;
|
||||
const require = win.require;
|
||||
|
||||
export interface Monaco {
|
||||
|
|
@ -8,32 +9,102 @@ export interface Monaco {
|
|||
Uri: any;
|
||||
}
|
||||
|
||||
export function initializeMonacoWorker(libPath?: string): Promise<Monaco> {
|
||||
export interface EditorVariable {
|
||||
variableName: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
if (win.monaco)
|
||||
return win.monaco;
|
||||
export var EditorVariables: Array<EditorVariable> = [];
|
||||
|
||||
if (!libPath)
|
||||
return win.monaco;
|
||||
let isInitialized: boolean;
|
||||
const mutex = new Mutex();
|
||||
|
||||
const origin = document.location.origin;
|
||||
const baseUrl = libPath.startsWith('http') ? libPath : `${origin}/${libPath}`;
|
||||
export async function initializeMonacoWorker(libPath?: string): Promise<Monaco> {
|
||||
|
||||
require.config({paths: {'vs': `${baseUrl}/vs`}});
|
||||
win.MonacoEnvironment = {getWorkerUrl: () => proxy};
|
||||
return await mutex.runExclusive(async () => {
|
||||
|
||||
let proxy = URL.createObjectURL(new Blob([`
|
||||
if (isInitialized) {
|
||||
|
||||
return win.monaco;
|
||||
}
|
||||
|
||||
const origin = document.location.origin;
|
||||
const baseUrl = libPath.startsWith('http') ? libPath : `${origin}/${libPath}`;
|
||||
|
||||
require.config({ paths: { 'vs': `${baseUrl}/vs` } });
|
||||
win.MonacoEnvironment = { getWorkerUrl: () => proxy };
|
||||
|
||||
let proxy = URL.createObjectURL(new Blob([`
|
||||
self.MonacoEnvironment = {
|
||||
baseUrl: '${baseUrl}'
|
||||
};
|
||||
importScripts('${baseUrl}/vs/base/worker/workerMain.js');
|
||||
`], {type: 'text/javascript'}));
|
||||
`], { type: 'text/javascript' }));
|
||||
|
||||
return new Promise(resolve => {
|
||||
require(["vs/editor/editor.main"], () => {
|
||||
resolve(win.monaco);
|
||||
return new Promise(resolve => {
|
||||
require(["vs/editor/editor.main"], () => {
|
||||
isInitialized = true;
|
||||
registerLiquid(win.monaco);
|
||||
registerSql(win.monaco);
|
||||
resolve(win.monaco);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export const DefaultLibUri: string = 'defaultLib:lib.es6.d.ts';
|
||||
function registerLiquid(monaco: any) {
|
||||
monaco.languages.register({ id: 'liquid' });
|
||||
|
||||
monaco.languages.registerCompletionItemProvider('liquid', {
|
||||
provideCompletionItems: () => {
|
||||
const autocompleteProviderItems = [];
|
||||
const keywords = ['assign', 'capture', 'endcapture', 'increment', 'decrement',
|
||||
'if', 'else', 'elsif', 'endif', 'for', 'endfor', 'break',
|
||||
'continue', 'limit', 'offset', 'range', 'reversed', 'cols',
|
||||
'case', 'endcase', 'when', 'block', 'endblock', 'true', 'false',
|
||||
'in', 'unless', 'endunless', 'cycle', 'tablerow', 'endtablerow',
|
||||
'contains', 'startswith', 'endswith', 'comment', 'endcomment',
|
||||
'raw', 'endraw', 'editable', 'endentitylist', 'endentityview', 'endinclude',
|
||||
'endmarker', 'entitylist', 'entityview', 'forloop', 'image', 'include',
|
||||
'marker', 'outputcache', 'plugin', 'style', 'text', 'widget',
|
||||
'abs', 'append', 'at_least', 'at_most', 'capitalize', 'ceil', 'compact',
|
||||
'concat', 'date', 'default', 'divided_by', 'downcase', 'escape',
|
||||
'escape_once', 'first', 'floor', 'join', 'last', 'lstrip', 'map',
|
||||
'minus', 'modulo', 'newline_to_br', 'plus', 'prepend', 'remove',
|
||||
'remove_first', 'replace', 'replace_first', 'reverse', 'round',
|
||||
'rstrip', 'size', 'slice', 'sort', 'sort_natural', 'split', 'strip',
|
||||
'strip_html', 'strip_newlines', 'times', 'truncate', 'truncatewords',
|
||||
'uniq', 'upcase', 'url_decode', 'url_encode'];
|
||||
|
||||
for (let i = 0; i < keywords.length; i++) {
|
||||
autocompleteProviderItems.push({ 'label': keywords[i], kind: monaco.languages.CompletionItemKind.Keyword });
|
||||
}
|
||||
|
||||
return { suggestions: autocompleteProviderItems };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function registerSql(monaco: any) {
|
||||
|
||||
monaco.languages.registerCompletionItemProvider('sql', {
|
||||
triggerCharacters: ["@"],
|
||||
provideCompletionItems: (model, position) => {
|
||||
|
||||
const word = model.getWordUntilPosition(position)
|
||||
|
||||
const autocompleteProviderItems = [];
|
||||
for (const variable of EditorVariables) {
|
||||
autocompleteProviderItems.push({
|
||||
label: `${variable.variableName}: ${variable.type}`,
|
||||
kind: monaco.languages.CompletionItemKind.Variable,
|
||||
insertText: variable.variableName
|
||||
});
|
||||
}
|
||||
|
||||
return { suggestions: autocompleteProviderItems };
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import {NodeInputDriver, ActivityInputContext} from "../../services/node-input-driver";
|
||||
import {ActivityInputDriver, ActivityInputContext} from "../../services/activity-input-driver";
|
||||
import {Container} from "typedi";
|
||||
import {InputControlRegistry} from "../../services/input-control-registry";
|
||||
import {h} from "@stencil/core";
|
||||
import {FormEntry} from "../../components/shared/forms/form-entry";
|
||||
|
||||
// A standard input driver that determines the UI to be displayed based on the UI hint of the activity input property.
|
||||
export class DefaultInputDriver implements NodeInputDriver {
|
||||
export class DefaultInputDriver implements ActivityInputDriver {
|
||||
private inputControlRegistry: InputControlRegistry;
|
||||
|
||||
constructor() {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@ import 'reflect-metadata';
|
|||
|
||||
export {Components, JSX} from './components';
|
||||
export {Container} from 'typedi';
|
||||
export {ElsaApiClientProvider} from './services/api-client/api-client';
|
||||
export {ElsaClientProvider} from './services/api-client/elsa-client';
|
||||
export {stripActivityNameSpace} from './utils';
|
||||
export {PluginRegistry, ActivityNameFormatter} from './services';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import {ActivityInputContext} from "../services/node-input-driver";
|
||||
import {ActivityInputContext} from "../services/activity-input-driver";
|
||||
import {Activity, ActivityDescriptor} from "./core";
|
||||
|
||||
export interface TabDefinition {
|
||||
|
|
|
|||
|
|
@ -153,6 +153,8 @@ export class Editor {
|
|||
private renderSelectedObject = () => {
|
||||
if (!!this.selectedActivity)
|
||||
return <elsa-activity-properties-editor
|
||||
containerType="activity-definition"
|
||||
containerId={this.activityDefinition.definitionId}
|
||||
activity={this.selectedActivity}
|
||||
variables={this.activityDefinitionState.variables}
|
||||
onActivityUpdated={e => this.onActivityUpdated(e)}/>;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import {Service} from "typedi";
|
||||
import {ElsaApiClientProvider} from "../../../services";
|
||||
import {
|
||||
ActivityDefinition,
|
||||
ActivityDefinitionSummary,
|
||||
|
|
@ -12,12 +11,13 @@ import {
|
|||
} from "../models";
|
||||
import {getVersionOptionsString, serializeQueryString} from "../../../utils";
|
||||
import {PagedList} from "../../../models";
|
||||
import {ElsaClientProvider} from "../../../services";
|
||||
|
||||
@Service()
|
||||
export class ActivityDefinitionsApi {
|
||||
private provider: ElsaApiClientProvider;
|
||||
private provider: ElsaClientProvider;
|
||||
|
||||
constructor(provider: ElsaApiClientProvider) {
|
||||
constructor(provider: ElsaClientProvider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {Component, h, Prop, State, Watch} from "@stencil/core";
|
||||
import {camelCase} from 'lodash';
|
||||
import {ActivityInputContext} from "../../services/node-input-driver";
|
||||
import {ActivityInputContext} from "../../services/activity-input-driver";
|
||||
import {mapSyntaxToLanguage} from "../../utils";
|
||||
import {SyntaxNames} from "../../models";
|
||||
import {MonacoValueChangedArgs} from "../../components/shared/monaco-editor/monaco-editor";
|
||||
|
|
@ -28,7 +28,7 @@ export class FlowSwitchEditor {
|
|||
|
||||
private updateCases(){
|
||||
const inputContext = this.inputContext;
|
||||
const activity = this.inputContext.node;
|
||||
const activity = this.inputContext.activity;
|
||||
const inputDescriptor = inputContext.inputDescriptor;
|
||||
const propertyName = inputDescriptor.name;
|
||||
const camelCasePropertyName = camelCase(propertyName);
|
||||
|
|
@ -67,7 +67,7 @@ export class FlowSwitchEditor {
|
|||
|
||||
private updateActivity = () => {
|
||||
const inputContext = this.inputContext;
|
||||
const activity = this.inputContext.node;
|
||||
const activity = this.inputContext.activity;
|
||||
const inputDescriptor = inputContext.inputDescriptor;
|
||||
const propertyName = inputDescriptor.name;
|
||||
const camelCasePropertyName = camelCase(propertyName);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import {List} from "../../models";
|
||||
import {Label} from "./models";
|
||||
import {ElsaApiClientProvider} from "../../services";
|
||||
import {ElsaClientProvider} from "../../services";
|
||||
import {Service} from "typedi";
|
||||
|
||||
@Service()
|
||||
export class LabelsApi {
|
||||
private provider: ElsaApiClientProvider;
|
||||
private provider: ElsaClientProvider;
|
||||
|
||||
constructor(provider: ElsaApiClientProvider) {
|
||||
constructor(provider: ElsaClientProvider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
|
|
@ -38,9 +38,9 @@ export class LabelsApi {
|
|||
|
||||
@Service()
|
||||
export class WorkflowDefinitionLabelsApi {
|
||||
private provider: ElsaApiClientProvider;
|
||||
private provider: ElsaClientProvider;
|
||||
|
||||
constructor(provider: ElsaApiClientProvider) {
|
||||
constructor(provider: ElsaClientProvider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {h} from "@stencil/core";
|
|||
import {Service as MiddlewareService} from 'axios-middleware';
|
||||
import {EventTypes, Plugin} from "../../models";
|
||||
import {Container, Service} from "typedi";
|
||||
import {StudioService, AuthContext, EventBus, ElsaClient, ElsaApiClientProvider} from "../../services";
|
||||
import {StudioService, AuthContext, EventBus, ElsaClient, ElsaClientProvider} from "../../services";
|
||||
import descriptorsStore from '../../data/descriptors-store';
|
||||
import {SignedInArgs} from "./models";
|
||||
import {AxiosInstance} from "axios";
|
||||
|
|
@ -32,7 +32,7 @@ export class LoginPlugin implements Plugin {
|
|||
}
|
||||
|
||||
private loadDescriptors = async (): Promise<void> => {
|
||||
const elsaClientProvider = Container.get(ElsaApiClientProvider);
|
||||
const elsaClientProvider = Container.get(ElsaClientProvider);
|
||||
this.elsaClient = await elsaClientProvider.getElsaClient();
|
||||
|
||||
const activityDescriptors = await this.elsaClient.descriptors.activities.list();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import 'reflect-metadata';
|
||||
import {Container, Service} from "typedi";
|
||||
import {ElsaApiClientProvider, EventBus, ServerSettings} from "../../services";
|
||||
import {ElsaClientProvider, EventBus, ServerSettings} from "../../services";
|
||||
import {LoginResponse} from "./models";
|
||||
import axios, {AxiosError, AxiosRequestConfig} from "axios";
|
||||
import {EventTypes} from "../../models";
|
||||
|
|
@ -8,7 +8,7 @@ import {EventTypes} from "../../models";
|
|||
@Service()
|
||||
export class LoginApi {
|
||||
|
||||
constructor(private provider: ElsaApiClientProvider, private serverSettings: ServerSettings) {
|
||||
constructor(private provider: ElsaClientProvider, private serverSettings: ServerSettings) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {Component, h, Prop, State, Watch} from "@stencil/core";
|
||||
import {camelCase} from 'lodash';
|
||||
import {ActivityInputContext} from "../../services/node-input-driver";
|
||||
import {ActivityInputContext} from "../../services/activity-input-driver";
|
||||
import {mapSyntaxToLanguage} from "../../utils";
|
||||
import {SyntaxNames} from "../../models";
|
||||
import {SwitchCase} from "./models";
|
||||
|
|
@ -29,7 +29,7 @@ export class SwitchEditor {
|
|||
|
||||
private updateCases(){
|
||||
const inputContext = this.inputContext;
|
||||
const activity = this.inputContext.node;
|
||||
const activity = this.inputContext.activity;
|
||||
const inputDescriptor = inputContext.inputDescriptor;
|
||||
const propertyName = inputDescriptor.name;
|
||||
const camelCasePropertyName = camelCase(propertyName);
|
||||
|
|
@ -68,7 +68,7 @@ export class SwitchEditor {
|
|||
|
||||
private updateActivity = () => {
|
||||
const inputContext = this.inputContext;
|
||||
const activity = this.inputContext.node;
|
||||
const activity = this.inputContext.activity;
|
||||
const inputDescriptor = inputContext.inputDescriptor;
|
||||
const propertyName = inputDescriptor.name;
|
||||
const camelCasePropertyName = camelCase(propertyName);
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ import {v4 as uuid} from 'uuid';
|
|||
import {Activity, ActivityDescriptor, ActivityInput, ActivityKind, ActivityOutput, Expression, InputDescriptor, OutputDescriptor, PropertyDescriptor, RenderActivityInputContext, RenderActivityPropsContext, TabChangedArgs, TabDefinition, Variable} from '../../../models';
|
||||
import {InputDriverRegistry} from "../../../services";
|
||||
import {Container} from "typedi";
|
||||
import {ActivityInputContext} from "../../../services/node-input-driver";
|
||||
import {ActivityInputContext} from "../../../services/activity-input-driver";
|
||||
import {CheckboxFormEntry, FormEntry} from "../../../components/shared/forms/form-entry";
|
||||
import {isNullOrWhitespace} from "../../../utils";
|
||||
import descriptorsStore from "../../../data/descriptors-store";
|
||||
import {ActivityUpdatedArgs, DeleteActivityRequestedArgs} from "../models/ui";
|
||||
import {WorkflowDefinition} from "../models/entities";
|
||||
import InputControlSwitchContextState from "../../../components/designer/input-control-switch/state";
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-activity-properties-editor',
|
||||
|
|
@ -23,7 +23,8 @@ export class ActivityPropertiesEditor {
|
|||
this.inputDriverRegistry = Container.get(InputDriverRegistry);
|
||||
}
|
||||
|
||||
@Prop() workflowDefinition: WorkflowDefinition;
|
||||
@Prop() containerType: string;
|
||||
@Prop() containerId: string;
|
||||
@Prop() activity?: Activity;
|
||||
@Prop() variables: Array<Variable> = [];
|
||||
|
||||
|
|
@ -59,15 +60,23 @@ export class ActivityPropertiesEditor {
|
|||
|
||||
const renderInputPropertyContexts: Array<RenderActivityInputContext> = activityDescriptor.inputs.map(inputDescriptor => {
|
||||
const renderInputContext: ActivityInputContext = {
|
||||
node: activity,
|
||||
nodeDescriptor: activityDescriptor,
|
||||
activity: activity,
|
||||
activityDescriptor: activityDescriptor,
|
||||
inputDescriptor,
|
||||
notifyInputChanged: () => onInputChanged(inputDescriptor),
|
||||
inputChanged: (v, s) => this.onInputPropertyEditorChanged(inputDescriptor, v, s)
|
||||
};
|
||||
|
||||
const driver = driverRegistry.get(renderInputContext);
|
||||
const control = driver?.renderInput(renderInputContext);
|
||||
const containerType = this.containerType;
|
||||
const containerId = this.containerId;
|
||||
const activityType = activityDescriptor.typeName;
|
||||
const propertyName = inputDescriptor.name;
|
||||
|
||||
const control =
|
||||
<InputControlSwitchContextState.Provider state={{containerType, containerId, activityType, propertyName}}>
|
||||
{driver?.renderInput(renderInputContext)}
|
||||
</InputControlSwitchContextState.Provider>;
|
||||
|
||||
return {
|
||||
inputContext: renderInputContext,
|
||||
|
|
@ -86,6 +95,7 @@ export class ActivityPropertiesEditor {
|
|||
render() {
|
||||
const {activity, activityDescriptor} = this.renderContext;
|
||||
const isTask = activityDescriptor.kind == ActivityKind.Task;
|
||||
const activityType = activityDescriptor.typeName;
|
||||
|
||||
const commonTab: TabDefinition = {
|
||||
displayText: 'General',
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {MonacoEditorSettings} from "../../../services/monaco-editor-settings";
|
|||
import {ActivityPropertyChangedEventArgs, WorkflowDefinitionPropsUpdatedArgs, WorkflowDefinitionUpdatedArgs, ActivityUpdatedArgs, WorkflowEditorEventTypes} from "../models/ui";
|
||||
import {WorkflowDefinition} from "../models/entities";
|
||||
import {WorkflowDefinitionsApi} from "../services/api"
|
||||
import WorkflowDefinitionTunnel, {WorkflowDefinitionState} from "../../../state/workflow-definition-state";
|
||||
import WorkflowDefinitionTunnel, {WorkflowDefinitionState} from "../state";
|
||||
import {LayoutDirection} from "../../flowchart/models";
|
||||
import { cloneDeep } from '@antv/x6/lib/util/object/object';
|
||||
import { removeGuidsFromPortNames } from '../../../utils/graph';
|
||||
|
|
@ -36,15 +36,13 @@ export class WorkflowDefinitionEditor {
|
|||
private readonly emitActivityChangedDebounced: (e: ActivityPropertyChangedEventArgs) => void;
|
||||
private readonly saveChangesDebounced: () => void;
|
||||
private readonly workflowDefinitionApi: WorkflowDefinitionsApi;
|
||||
private currentFlowchartConnections;
|
||||
|
||||
constructor() {
|
||||
this.eventBus = Container.get(EventBus);
|
||||
this.pluginRegistry = Container.get(PluginRegistry);
|
||||
this.activityNameFormatter = Container.get(ActivityNameFormatter);
|
||||
this.portProviderRegistry = Container.get(PortProviderRegistry);
|
||||
//this.emitActivityChangedDebounced = debounce(this.emitActivityChanged, 100);
|
||||
this.emitActivityChangedDebounced = e => this.emitActivityChanged(e.activity, e.propertyName);
|
||||
this.emitActivityChangedDebounced = debounce(this.emitActivityChanged, 10);
|
||||
this.saveChangesDebounced = debounce(this.saveChanges, 1000);
|
||||
this.workflowDefinitionApi = Container.get(WorkflowDefinitionsApi);
|
||||
}
|
||||
|
|
@ -174,9 +172,10 @@ export class WorkflowDefinitionEditor {
|
|||
private renderSelectedObject = () => {
|
||||
if (!!this.selectedActivity)
|
||||
return <elsa-activity-properties-editor
|
||||
workflowDefinition={this.workflowDefinitionState}
|
||||
activity={this.selectedActivity}
|
||||
variables={this.workflowDefinitionState.variables}
|
||||
containerType="workflow-definition"
|
||||
containerId={this.workflowDefinition.definitionId}
|
||||
onActivityUpdated={e => this.onActivityUpdated(e)}/>;
|
||||
}
|
||||
|
||||
|
|
@ -203,7 +202,7 @@ export class WorkflowDefinitionEditor {
|
|||
}
|
||||
};
|
||||
|
||||
// To prevent redundant post requests to server, save changes only if there is a difference
|
||||
// To prevent redundant post requests to server, save changes only if there is a difference
|
||||
// between existing workflow definition on server side and updated workflow definition on client side.
|
||||
private hasWorkflowDefinitionAnyUpdatedData = async (updatedWorkflowDefinition : WorkflowDefinition): Promise<boolean> => {
|
||||
const existingWorkflowDefinition = await this.workflowDefinitionApi.get({definitionId: updatedWorkflowDefinition.definitionId, versionOptions: {version: updatedWorkflowDefinition.version}});
|
||||
|
|
|
|||
|
|
@ -2,16 +2,16 @@ import {getVersionOptionsString, serializeQueryString} from '../../../utils';
|
|||
import {WorkflowDefinition, WorkflowDefinitionSummary, WorkflowOptions} from "../models/entities";
|
||||
import {Activity, PagedList, Variable, VersionedEntity, VersionOptions} from "../../../models";
|
||||
import {Service} from "typedi";
|
||||
import {ElsaApiClientProvider} from "../../../services";
|
||||
import {AxiosResponse} from "axios";
|
||||
import { removeGuidsFromPortNames, addGuidsToPortNames } from '../../../utils/graph';
|
||||
import { cloneDeep } from '@antv/x6/lib/util/object/object';
|
||||
import {ElsaClientProvider} from "../../../services";
|
||||
|
||||
@Service()
|
||||
export class WorkflowDefinitionsApi {
|
||||
private provider: ElsaApiClientProvider;
|
||||
private provider: ElsaClientProvider;
|
||||
|
||||
constructor(provider: ElsaApiClientProvider) {
|
||||
constructor(provider: ElsaClientProvider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {h} from "@stencil/core";
|
||||
import {createProviderConsumer} from "@stencil/state-tunnel";
|
||||
import {WorkflowDefinition} from "../modules/workflow-definitions/models/entities";
|
||||
import {WorkflowDefinition} from "./models/entities";
|
||||
|
||||
export interface WorkflowDefinitionState {
|
||||
workflowDefinition: WorkflowDefinition;
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
import {OrderBy, OrderDirection, PagedList, VersionOptions, WorkflowExecutionLogRecord, WorkflowInstance, WorkflowInstanceSummary, WorkflowStatus, WorkflowSubStatus} from "../../../models";
|
||||
import {getVersionOptionsString, serializeQueryString} from "../../../utils";
|
||||
import {ElsaApiClientProvider} from "../../../services";
|
||||
import {Service} from "typedi";
|
||||
import {ElsaClientProvider} from "../../../services";
|
||||
|
||||
@Service()
|
||||
export class WorkflowInstancesApi {
|
||||
|
||||
private provider: ElsaApiClientProvider;
|
||||
private provider: ElsaClientProvider;
|
||||
|
||||
constructor(provider: ElsaApiClientProvider) {
|
||||
constructor(provider: ElsaClientProvider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import 'reflect-metadata';
|
||||
import {Container, Service} from "typedi";
|
||||
import {ElsaApiClientProvider} from "./api-client/api-client";
|
||||
import descriptorsStore from "../data/descriptors-store";
|
||||
import {ElsaClientProvider} from "./api-client/elsa-client";
|
||||
|
||||
@Service()
|
||||
export class ActivityDescriptorManager {
|
||||
private readonly elsaClientProvider: ElsaApiClientProvider;
|
||||
private readonly elsaClientProvider: ElsaClientProvider;
|
||||
|
||||
constructor() {
|
||||
this.elsaClientProvider = Container.get(ElsaApiClientProvider);
|
||||
this.elsaClientProvider = Container.get(ElsaClientProvider);
|
||||
}
|
||||
|
||||
async refresh(): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import {Activity, ActivityDescriptor, InputDescriptor} from "../models";
|
||||
|
||||
export interface ActivityInputContext {
|
||||
node: Activity;
|
||||
nodeDescriptor: ActivityDescriptor;
|
||||
activity: Activity;
|
||||
activityDescriptor: ActivityDescriptor;
|
||||
inputDescriptor: InputDescriptor;
|
||||
notifyInputChanged: () => void;
|
||||
inputChanged: (value: any, syntax: string) => void;
|
||||
}
|
||||
|
||||
export interface NodeInputDriver {
|
||||
export interface ActivityInputDriver {
|
||||
supportsInput(context: ActivityInputContext): boolean;
|
||||
|
||||
get priority(): number;
|
||||
|
|
@ -1,11 +1,7 @@
|
|||
import {RuntimeSelectListApi} from "./runtime-select-list-api";
|
||||
import {AxiosInstance} from "axios";
|
||||
|
||||
export interface DesignerApi {
|
||||
runtimeSelectListApi: RuntimeSelectListApi;
|
||||
}
|
||||
|
||||
export class DesignerApiImpl implements DesignerApi {
|
||||
export class DesignerApi {
|
||||
runtimeSelectListApi: RuntimeSelectListApi;
|
||||
private httpClient: AxiosInstance;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,27 +1,28 @@
|
|||
import axios, {AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse} from "axios";
|
||||
import axios, {AxiosInstance, AxiosRequestConfig} from "axios";
|
||||
import {Service as MiddlewareService} from 'axios-middleware';
|
||||
import {Container, Service} from 'typedi';
|
||||
import {EventBus} from '../event-bus';
|
||||
import 'reflect-metadata';
|
||||
import {ServerSettings} from '../server-settings';
|
||||
import {DescriptorsApi} from "./descriptors-api";
|
||||
import {DesignerApi, DesignerApiImpl} from "./designer-api";
|
||||
import {DesignerApi} from "./designer-api";
|
||||
import {EventTypes} from "../../models";
|
||||
import {ScriptingApi} from "./scripting-api";
|
||||
|
||||
export class ElsaClient {
|
||||
httpClient: AxiosInstance;
|
||||
descriptors: DescriptorsApi;
|
||||
designer: DesignerApi;
|
||||
scripting: ScriptingApi;
|
||||
|
||||
constructor(httpClient: AxiosInstance) {
|
||||
this.httpClient = httpClient;
|
||||
this.descriptors = new DescriptorsApi(httpClient);
|
||||
this.designer = new DesignerApiImpl(httpClient);
|
||||
this.designer = new DesignerApi(httpClient);
|
||||
this.scripting = new ScriptingApi(httpClient);
|
||||
}
|
||||
}
|
||||
|
||||
@Service()
|
||||
export class ElsaApiClientProvider {
|
||||
export class ElsaClientProvider {
|
||||
private httpClient: AxiosInstance;
|
||||
private elsaClient: ElsaClient;
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import {AxiosInstance} from "axios";
|
||||
|
||||
export class JavaScriptApi {
|
||||
|
||||
constructor(private httpClient: AxiosInstance) {
|
||||
}
|
||||
|
||||
async getTypeDefinitions(request: GetTypeDefinitionsRequest): Promise<string> {
|
||||
const response = await this.httpClient.post(`scripting/javascript/type-definitions/${request.containerId}`, request);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface GetTypeDefinitionsRequest {
|
||||
containerType: string;
|
||||
containerId: string;
|
||||
activityTypeName?: string;
|
||||
propertyName?: string;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import {AxiosInstance} from "axios";
|
||||
import {JavaScriptApi} from "./javascript-api";
|
||||
|
||||
export class ScriptingApi {
|
||||
javaScriptApi: JavaScriptApi;
|
||||
|
||||
constructor(httpClient: AxiosInstance) {
|
||||
this.javaScriptApi = new JavaScriptApi(httpClient);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ export * from './activity-driver-registry';
|
|||
export * from './activity-icon-registry';
|
||||
export * from './auth';
|
||||
export * from './custom-event-bus';
|
||||
export * from './api-client/api-client';
|
||||
export * from './api-client/elsa-client';
|
||||
export * from './event-bus';
|
||||
export * from './input-control-registry';
|
||||
export * from './input-driver-registry';
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
import _ from 'lodash';
|
||||
import {Service} from 'typedi';
|
||||
import {NodeInputDriver, ActivityInputContext} from './node-input-driver';
|
||||
import {ActivityInputDriver, ActivityInputContext} from './activity-input-driver';
|
||||
import {DefaultInputDriver} from "../drivers/input/default-input-driver";
|
||||
|
||||
@Service()
|
||||
export class InputDriverRegistry {
|
||||
private drivers: Array<NodeInputDriver> = [new DefaultInputDriver()];
|
||||
private drivers: Array<ActivityInputDriver> = [new DefaultInputDriver()];
|
||||
|
||||
public add(driver: NodeInputDriver) {
|
||||
const drivers: Array<NodeInputDriver> = [...this.drivers, driver]
|
||||
public add(driver: ActivityInputDriver) {
|
||||
const drivers: Array<ActivityInputDriver> = [...this.drivers, driver]
|
||||
this.drivers = _.orderBy(drivers, x => x.priority, 'desc');
|
||||
}
|
||||
|
||||
public get(context: ActivityInputContext): NodeInputDriver {
|
||||
public get(context: ActivityInputContext): ActivityInputDriver {
|
||||
return this.drivers.find(x => x.supportsInput(context));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import {
|
|||
RuntimeSelectListProviderSettings,
|
||||
SelectList,
|
||||
} from "../models";
|
||||
import {ElsaApiClientProvider} from "../services";
|
||||
import {Container} from "typedi";
|
||||
import {ElsaClientProvider} from "../services";
|
||||
|
||||
async function fetchRuntimeItems(options: RuntimeSelectListProviderSettings): Promise<SelectList> {
|
||||
const elsaClientProvider = Container.get(ElsaApiClientProvider);
|
||||
const elsaClientProvider = Container.get(ElsaClientProvider);
|
||||
const elsaClient = await elsaClientProvider.getElsaClient();
|
||||
return await elsaClient.designer.runtimeSelectListApi.get(options.runtimeSelectListProviderType, options.context || {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import moment from 'moment';
|
||||
import _, {camelCase} from 'lodash';
|
||||
import {ActivityInput, VersionOptions} from '../models';
|
||||
import {ActivityInputContext} from '../services/node-input-driver';
|
||||
import {ActivityInputContext} from '../services/activity-input-driver';
|
||||
|
||||
export interface Hash<TValue> {
|
||||
[key: string]: TValue;
|
||||
|
|
@ -71,12 +71,12 @@ export const getInputPropertyName = (inputContext: ActivityInputContext) => {
|
|||
|
||||
export const getInputPropertyValue = (inputContext: ActivityInputContext): ActivityInput => {
|
||||
const propName = getInputPropertyName(inputContext);
|
||||
return inputContext.node[propName] as ActivityInput;
|
||||
return inputContext.activity[propName] as ActivityInput;
|
||||
};
|
||||
|
||||
export const getPropertyValue = (inputContext: ActivityInputContext): any => {
|
||||
const propName = getInputPropertyName(inputContext);
|
||||
return inputContext.node[propName] as any
|
||||
return inputContext.activity[propName] as any
|
||||
};
|
||||
|
||||
export const stripActivityNameSpace = (name: string): string => {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@
|
|||
"emitDecoratorMetadata": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2017",
|
||||
"es2018.promise"
|
||||
"ES2020"
|
||||
],
|
||||
"moduleResolution": "node",
|
||||
"module": "esnext",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
using Elsa.Expressions.Services;
|
||||
using Elsa.Extensions;
|
||||
|
||||
namespace Elsa.Expressions.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extends <see cref="IWellKnownTypeRegistry"/>.
|
||||
/// </summary>
|
||||
public static class WellKnowTypeRegistryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Register type <see cref="T"/> with the specified alias.
|
||||
/// </summary>
|
||||
public static void RegisterType<T>(this IWellKnownTypeRegistry registry, string alias) => registry.RegisterType(typeof(T), alias);
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to return a type with the specified alias.
|
||||
/// </summary>
|
||||
public static bool TryGetTypeOrDefault(this IWellKnownTypeRegistry registry, string alias, out Type type)
|
||||
{
|
||||
if (registry.TryGetType(alias, out type))
|
||||
return true;
|
||||
|
||||
var t = Type.GetType(alias);
|
||||
|
||||
if (t == null)
|
||||
return false;
|
||||
|
||||
type = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the alias for the specified type. If no alias was found, the assembly qualified type name is returned instead.
|
||||
/// </summary>
|
||||
public static string GetAliasOrDefault(this IWellKnownTypeRegistry registry, Type type) =>
|
||||
registry.TryGetAlias(type, out var alias) ? alias : type.GetSimpleAssemblyQualifiedName();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the type associated with the specified alias. If no type was found, the alias is interpreted as a type name/
|
||||
/// </summary>
|
||||
public static Type GetTypeOrDefault(this IWellKnownTypeRegistry registry, string alias) => registry.TryGetType(alias, out var type) ? type : Type.GetType(alias)!;
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ using System.ComponentModel;
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Elsa.Expressions.Exceptions;
|
||||
using Elsa.Expressions.Extensions;
|
||||
using Elsa.Expressions.Models;
|
||||
using Elsa.Expressions.Services;
|
||||
using Elsa.Extensions;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using Elsa.Expressions.Extensions;
|
||||
using Elsa.Expressions.Services;
|
||||
|
||||
namespace Elsa.Expressions.Implementations;
|
||||
|
|
@ -41,4 +42,7 @@ public class WellKnownTypeRegistry : IWellKnownTypeRegistry
|
|||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetType(string alias, out Type type) => _aliasTypeDictionary.TryGetValue(alias, out type!);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<Type> ListTypes() => _typeAliasDictionary.Keys;
|
||||
}
|
||||
|
|
@ -1,34 +1,27 @@
|
|||
using Elsa.Extensions;
|
||||
|
||||
namespace Elsa.Expressions.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A central repository of well known types.
|
||||
/// </summary>
|
||||
public interface IWellKnownTypeRegistry
|
||||
{
|
||||
/// <summary>
|
||||
/// Register a type with an alias.
|
||||
/// </summary>
|
||||
void RegisterType(Type type, string alias);
|
||||
bool TryGetAlias(Type type, out string alias);
|
||||
bool TryGetType(string alias, out Type type);
|
||||
}
|
||||
|
||||
public static class WellKnowTypeRegistryExtensions
|
||||
{
|
||||
public static void RegisterType<T>(this IWellKnownTypeRegistry registry, string alias) => registry.RegisterType(typeof(T), alias);
|
||||
|
||||
public static bool TryGetTypeOrDefault(this IWellKnownTypeRegistry registry, string alias, out Type type)
|
||||
{
|
||||
if (registry.TryGetType(alias, out type))
|
||||
return true;
|
||||
|
||||
var t = Type.GetType(alias);
|
||||
|
||||
if (t == null)
|
||||
return false;
|
||||
|
||||
type = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string GetAliasOrDefault(this IWellKnownTypeRegistry registry, Type type) =>
|
||||
registry.TryGetAlias(type, out var alias) ? alias : type.GetSimpleAssemblyQualifiedName();
|
||||
|
||||
public static Type GetTypeOrDefault(this IWellKnownTypeRegistry registry, string alias) => registry.TryGetType(alias, out var type) ? type : Type.GetType(alias)!;
|
||||
/// <summary>
|
||||
/// Attempts to get an alias for the specified type.
|
||||
/// </summary>
|
||||
bool TryGetAlias(Type type, out string alias);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get the type associated with the specified alias.
|
||||
/// </summary>
|
||||
bool TryGetType(string alias, out Type type);
|
||||
|
||||
/// <summary>
|
||||
/// Returns all registered types.
|
||||
/// </summary>
|
||||
IEnumerable<Type> ListTypes();
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
using Elsa.JavaScript.Builders;
|
||||
using Elsa.JavaScript.Models;
|
||||
using Elsa.JavaScript.Services;
|
||||
|
||||
namespace Elsa.JavaScript.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// A base class for function definition providers.
|
||||
/// </summary>
|
||||
public abstract class FunctionDefinitionProvider : IFunctionDefinitionProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a list of type definitions to the system.
|
||||
/// </summary>
|
||||
protected virtual ValueTask<IEnumerable<FunctionDefinition>> GetFunctionDefinitionsAsync(TypeDefinitionContext context)
|
||||
{
|
||||
var functions = GetFunctionDefinitions(context);
|
||||
return new(functions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of type definitions to the system.
|
||||
/// </summary>
|
||||
protected virtual IEnumerable<FunctionDefinition> GetFunctionDefinitions(TypeDefinitionContext context)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
async ValueTask<IEnumerable<FunctionDefinition>> IFunctionDefinitionProvider.GetFunctionDefinitionsAsync(TypeDefinitionContext context) => await GetFunctionDefinitionsAsync(context);
|
||||
|
||||
/// <summary>
|
||||
/// Provides a fluid API to build a function definition.
|
||||
/// </summary>
|
||||
protected FunctionDefinition CreateFunctionDefinition(Action<FunctionDefinitionBuilder> setup)
|
||||
{
|
||||
var builder = new FunctionDefinitionBuilder();
|
||||
setup(builder);
|
||||
return builder.BuildFunctionDefinition();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
using Elsa.JavaScript.Builders;
|
||||
using Elsa.JavaScript.Models;
|
||||
using Elsa.JavaScript.Services;
|
||||
|
||||
namespace Elsa.JavaScript.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// A base class for type definition providers.
|
||||
/// </summary>
|
||||
public abstract class TypeDefinitionProvider : ITypeDefinitionProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a list of type definitions to the system.
|
||||
/// </summary>
|
||||
protected virtual ValueTask<IEnumerable<TypeDefinition>> GetTypeDefinitionsAsync(TypeDefinitionContext context)
|
||||
{
|
||||
var functions = GetTypeDefinitions(context);
|
||||
return new(functions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of type definitions to the system.
|
||||
/// </summary>
|
||||
protected virtual IEnumerable<TypeDefinition> GetTypeDefinitions(TypeDefinitionContext context)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
async ValueTask<IEnumerable<TypeDefinition>> ITypeDefinitionProvider.GetTypeDefinitionsAsync(TypeDefinitionContext context) => await GetTypeDefinitionsAsync(context);
|
||||
|
||||
/// <summary>
|
||||
/// Provides a fluid API to build a type definition.
|
||||
/// </summary>
|
||||
protected TypeDefinition CreateTypeDefinition(Action<TypeDefinitionBuilder> setup)
|
||||
{
|
||||
var builder = new TypeDefinitionBuilder();
|
||||
setup(builder);
|
||||
return builder.BuildTypeDefinition();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
using Elsa.JavaScript.Models;
|
||||
|
||||
namespace Elsa.JavaScript.Builders;
|
||||
|
||||
/// <summary>
|
||||
/// A builder API for building <see cref="FunctionDefinition"/>s.
|
||||
/// </summary>
|
||||
public class FunctionDefinitionBuilder
|
||||
{
|
||||
private readonly FunctionDefinition _functionDefinition = new();
|
||||
|
||||
/// <summary>
|
||||
/// Set the name of the function.
|
||||
/// </summary>
|
||||
public FunctionDefinitionBuilder Name(string name)
|
||||
{
|
||||
_functionDefinition.Name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the return type of the function.
|
||||
/// </summary>
|
||||
public FunctionDefinitionBuilder ReturnType(string? type)
|
||||
{
|
||||
_functionDefinition.ReturnType = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a parameter to the function.
|
||||
/// </summary>
|
||||
public FunctionDefinitionBuilder Parameter(string name, string? type, bool isOptional = false)
|
||||
{
|
||||
_functionDefinition.Parameters.Add(new (name, type, isOptional));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build a <see cref="FunctionDefinition"/> using the collected information.
|
||||
/// </summary>
|
||||
public FunctionDefinition BuildFunctionDefinition() => new(_functionDefinition.Name, _functionDefinition.ReturnType, _functionDefinition.Parameters);
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using Elsa.JavaScript.Models;
|
||||
|
||||
namespace Elsa.JavaScript.Builders;
|
||||
|
||||
/// <summary>
|
||||
/// A builder API for building <see cref="TypeDefinition"/>s.
|
||||
/// </summary>
|
||||
public class TypeDefinitionBuilder
|
||||
{
|
||||
private readonly TypeDefinition _typeDefinition = new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Build a <see cref="TypeDefinition"/> using the collected information.
|
||||
/// </summary>
|
||||
public TypeDefinition BuildTypeDefinition() => new();
|
||||
}
|
||||
|
|
@ -4,7 +4,13 @@ using Jint.Runtime.Interop;
|
|||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extends <see cref="Engine"/>.
|
||||
/// </summary>
|
||||
public static class EngineExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Register type <see cref="T"/> with the engine.
|
||||
/// </summary>
|
||||
public static void RegisterType<T>(this Engine engine) => engine.SetValue(typeof(T).Name, TypeReference.CreateTypeReference(engine, typeof(T)));
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
using Elsa.Extensions;
|
||||
using Elsa.JavaScript.Services;
|
||||
|
||||
namespace Elsa.JavaScript.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extends <see cref="ITypeAliasRegistry"/>.
|
||||
/// </summary>
|
||||
public static class TypeAliasRegistryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Register type <see cref="T"/> with the specified alias.
|
||||
/// </summary>
|
||||
public static void RegisterType<T>(this ITypeAliasRegistry registry, string alias) => registry.RegisterType(typeof(T), alias);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the alias for the specified type. If no alias was found, the assembly qualified type name is returned instead.
|
||||
/// </summary>
|
||||
public static string GetAliasOrDefault(this ITypeAliasRegistry registry, Type type) =>
|
||||
registry.TryGetAlias(type, out var alias) ? alias : type.GetSimpleAssemblyQualifiedName();
|
||||
}
|
||||
|
|
@ -34,9 +34,18 @@ public class JavaScriptFeature : FeatureBase
|
|||
.AddSingleton<IExpressionSyntaxProvider, JavaScriptExpressionSyntaxProvider>()
|
||||
.AddSingleton<IJavaScriptEvaluator, JintJavaScriptEvaluator>()
|
||||
.AddSingleton<IActivityPropertyOptionsProvider, RunJavaScriptOptionsProvider>()
|
||||
.AddSingleton<ITypeDefinitionService, TypeDefinitionService>()
|
||||
.AddExpressionHandler<JavaScriptExpressionHandler, JavaScriptExpression>()
|
||||
;
|
||||
|
||||
Services
|
||||
.AddSingleton<ITypeDefinitionService, TypeDefinitionService>()
|
||||
.AddSingleton<ITypeDescriber, TypeDescriber>()
|
||||
.AddSingleton<ITypeDefinitionDocumentRenderer, TypeDefinitionDocumentRenderer>()
|
||||
.AddSingleton<ITypeAliasRegistry, TypeAliasRegistry>()
|
||||
.AddSingleton<IFunctionDefinitionProvider, CommonFunctionDefinitionProvider>()
|
||||
.AddSingleton<ITypeDefinitionProvider, VariableTypeDefinitionProvider>();
|
||||
|
||||
Module.UseWorkflowManagement(management => management.AddActivitiesFrom<JavaScriptFeature>());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
using Elsa.JavaScript.Extensions;
|
||||
using Elsa.JavaScript.Services;
|
||||
|
||||
namespace Elsa.JavaScript.Implementations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class TypeAliasRegistry : ITypeAliasRegistry
|
||||
{
|
||||
private readonly IDictionary<Type, string> _typeAliasDictionary = new Dictionary<Type, string>();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public TypeAliasRegistry()
|
||||
{
|
||||
this.RegisterType<object>("any");
|
||||
this.RegisterType<string>("string");
|
||||
this.RegisterType<bool>("boolean");
|
||||
this.RegisterType<short>("number");
|
||||
this.RegisterType<int>("number");
|
||||
this.RegisterType<long>("number");
|
||||
this.RegisterType<decimal>("Decimal");
|
||||
this.RegisterType<float>("Single");
|
||||
this.RegisterType<double>("Double");
|
||||
this.RegisterType<DateTime>("Date");
|
||||
this.RegisterType<DateTimeOffset>("Date");
|
||||
this.RegisterType<DateOnly>("Date");
|
||||
this.RegisterType<TimeOnly>("Date");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterType(Type type, string alias)
|
||||
{
|
||||
_typeAliasDictionary[type] = alias;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetAlias(Type type, out string alias) => _typeAliasDictionary.TryGetValue(type, out alias!);
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
using System.Text;
|
||||
using Elsa.JavaScript.Extensions;
|
||||
using Elsa.JavaScript.Models;
|
||||
using Elsa.JavaScript.Services;
|
||||
|
||||
namespace Elsa.JavaScript.Implementations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class TypeDefinitionDocumentRenderer : ITypeDefinitionDocumentRenderer
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Render(TypeDefinitionsDocument document)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
|
||||
foreach (var functionDefinition in document.Functions)
|
||||
Render(functionDefinition, stringBuilder);
|
||||
|
||||
foreach (var typeDefinition in document.Types)
|
||||
Render(typeDefinition, stringBuilder);
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
private void Render(FunctionDefinition functionDefinition, StringBuilder output)
|
||||
{
|
||||
string RenderParameter(ParameterDefinition parameter) => $"{parameter.Name}{(parameter.IsOptional ? "?" : "")}: {parameter.Type}";
|
||||
string RenderParameters(IEnumerable<ParameterDefinition> parameters) => string.Join(", ", parameters.Select(RenderParameter));
|
||||
|
||||
var returnType = functionDefinition.ReturnType != null ? $": {functionDefinition.ReturnType}" : "";
|
||||
output.AppendLine($"declare function {functionDefinition.Name}({RenderParameters(functionDefinition.Parameters)}){returnType}");
|
||||
}
|
||||
|
||||
private void Render(TypeDefinition typeDefinition, StringBuilder output)
|
||||
{
|
||||
output.AppendLine($"declare {typeDefinition.DeclarationKeyword} {typeDefinition.Name} {{");
|
||||
|
||||
foreach (var property in typeDefinition.Properties)
|
||||
Render(property, output);
|
||||
|
||||
output.AppendLine("}");
|
||||
}
|
||||
|
||||
private void Render(PropertyDefinition property, StringBuilder output) => output.AppendLine($"{property.Name}{(property.IsOptional ? "?" : "")}: {property.Type}");
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
using Elsa.JavaScript.Models;
|
||||
using Elsa.JavaScript.Services;
|
||||
|
||||
namespace Elsa.JavaScript.Implementations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class TypeDefinitionService : ITypeDefinitionService
|
||||
{
|
||||
private readonly IEnumerable<ITypeDefinitionProvider> _typeDefinitionProviders;
|
||||
private readonly IEnumerable<IFunctionDefinitionProvider> _functionDefinitionProviders;
|
||||
private readonly ITypeDefinitionDocumentRenderer _typeDefinitionDocumentRenderer;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public TypeDefinitionService(
|
||||
IEnumerable<ITypeDefinitionProvider> typeDefinitionProviders,
|
||||
IEnumerable<IFunctionDefinitionProvider> functionDefinitionProviders,
|
||||
ITypeDefinitionDocumentRenderer typeDefinitionDocumentRenderer)
|
||||
{
|
||||
_typeDefinitionProviders = typeDefinitionProviders;
|
||||
_functionDefinitionProviders = functionDefinitionProviders;
|
||||
_typeDefinitionDocumentRenderer = typeDefinitionDocumentRenderer;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> GenerateTypeDefinitionsAsync(TypeDefinitionContext context)
|
||||
{
|
||||
var typeDefinitions = await GetTypeDefinitionsAsync(context).ToListAsync(context.CancellationToken);
|
||||
var functionDefinitions = await GetFunctionDefinitionsAsync(context).ToListAsync(context.CancellationToken);
|
||||
|
||||
var document = new TypeDefinitionsDocument
|
||||
{
|
||||
Functions = functionDefinitions,
|
||||
Types = typeDefinitions
|
||||
};
|
||||
|
||||
return _typeDefinitionDocumentRenderer.Render(document);
|
||||
}
|
||||
|
||||
private async IAsyncEnumerable<FunctionDefinition> GetFunctionDefinitionsAsync(TypeDefinitionContext context)
|
||||
{
|
||||
foreach (var provider in _functionDefinitionProviders)
|
||||
{
|
||||
var definitions = await provider.GetFunctionDefinitionsAsync(context);
|
||||
|
||||
foreach (var definition in definitions)
|
||||
yield return definition;
|
||||
}
|
||||
}
|
||||
|
||||
private async IAsyncEnumerable<TypeDefinition> GetTypeDefinitionsAsync(TypeDefinitionContext context)
|
||||
{
|
||||
foreach (var provider in _typeDefinitionProviders)
|
||||
{
|
||||
var definitions = await provider.GetTypeDefinitionsAsync(context);
|
||||
|
||||
foreach (var definition in definitions)
|
||||
yield return definition;
|
||||
}
|
||||
}
|
||||
}
|
||||
90
src/modules/Elsa.JavaScript/Implementations/TypeDescriber.cs
Normal file
90
src/modules/Elsa.JavaScript/Implementations/TypeDescriber.cs
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
using System.Reflection;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.JavaScript.Extensions;
|
||||
using Elsa.JavaScript.Models;
|
||||
using Elsa.JavaScript.Services;
|
||||
|
||||
namespace Elsa.JavaScript.Implementations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class TypeDescriber : ITypeDescriber
|
||||
{
|
||||
private readonly ITypeAliasRegistry _typeAliasRegistry;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public TypeDescriber(ITypeAliasRegistry typeAliasRegistry)
|
||||
{
|
||||
_typeAliasRegistry = typeAliasRegistry;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public TypeDefinition DescribeType(Type type)
|
||||
{
|
||||
var typeDefinition = new TypeDefinition
|
||||
{
|
||||
DeclarationKeyword = GetDeclarationKeyword(type),
|
||||
Name = type.Name,
|
||||
Properties = GetPropertyDefinitions(type).ToList(),
|
||||
Methods = GetMethodDefinitions(type).ToList()
|
||||
};
|
||||
|
||||
return typeDefinition;
|
||||
}
|
||||
|
||||
private IEnumerable<FunctionDefinition> GetMethodDefinitions(Type type)
|
||||
{
|
||||
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static).Where(x => !x.IsSpecialName).ToList();
|
||||
|
||||
foreach (var method in methods)
|
||||
{
|
||||
yield return new FunctionDefinition
|
||||
{
|
||||
Name = method.Name,
|
||||
Parameters = GetMethodParameters(method).ToList(),
|
||||
ReturnType = _typeAliasRegistry.TryGetAlias(method.ReturnType, out var alias) ? alias : "any"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<ParameterDefinition> GetMethodParameters(MethodInfo method)
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
yield return new ParameterDefinition
|
||||
{
|
||||
Name = parameter.Name!,
|
||||
Type = _typeAliasRegistry.TryGetAlias(parameter.ParameterType, out var alias) ? alias : "any",
|
||||
IsOptional = parameter.IsOptional
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<PropertyDefinition> GetPropertyDefinitions(Type type)
|
||||
{
|
||||
var properties = type.GetProperties();
|
||||
|
||||
foreach (var property in properties)
|
||||
{
|
||||
yield return new PropertyDefinition
|
||||
{
|
||||
Name = property.Name,
|
||||
Type = _typeAliasRegistry.TryGetAlias(property.PropertyType, out var alias) ? alias : "any",
|
||||
IsOptional = property.PropertyType.IsNullableType()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetDeclarationKeyword(Type type) =>
|
||||
type switch
|
||||
{
|
||||
{ IsInterface: true } => "interface",
|
||||
{ IsClass: true } => "class",
|
||||
{ IsValueType: true } => "class",
|
||||
{ IsEnum: true } => "enum",
|
||||
_ => "interface"
|
||||
};
|
||||
}
|
||||
48
src/modules/Elsa.JavaScript/Models/FunctionDefinition.cs
Normal file
48
src/modules/Elsa.JavaScript/Models/FunctionDefinition.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
namespace Elsa.JavaScript.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A method definition.
|
||||
/// </summary>
|
||||
public class FunctionDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public FunctionDefinition()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public FunctionDefinition(string name, IEnumerable<ParameterDefinition> parameters)
|
||||
{
|
||||
Name = name;
|
||||
Parameters = parameters.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public FunctionDefinition(string name, string? returnType, IEnumerable<ParameterDefinition> parameters)
|
||||
{
|
||||
Name = name;
|
||||
ReturnType = returnType;
|
||||
Parameters = parameters.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the method.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The return type name of the method.
|
||||
/// </summary>
|
||||
public string? ReturnType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The parameters of the method.
|
||||
/// </summary>
|
||||
public ICollection<ParameterDefinition> Parameters { get; set; } = new List<ParameterDefinition>();
|
||||
}
|
||||
39
src/modules/Elsa.JavaScript/Models/ParameterDefinition.cs
Normal file
39
src/modules/Elsa.JavaScript/Models/ParameterDefinition.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
namespace Elsa.JavaScript.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A method parameter definition.
|
||||
/// </summary>
|
||||
public class ParameterDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public ParameterDefinition()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public ParameterDefinition(string name, string? type, bool isOptional = false)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
IsOptional = isOptional;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the parameter.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The type name of the parameter.
|
||||
/// </summary>
|
||||
public string? Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A value indicating whether the parameter is optional or not.
|
||||
/// </summary>
|
||||
public bool IsOptional { get; set; }
|
||||
}
|
||||
22
src/modules/Elsa.JavaScript/Models/PropertyDefinition.cs
Normal file
22
src/modules/Elsa.JavaScript/Models/PropertyDefinition.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
namespace Elsa.JavaScript.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A property definition.
|
||||
/// </summary>
|
||||
public class PropertyDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the property.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The type name of the property.
|
||||
/// </summary>
|
||||
public string Type { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// A value indicating whether the property is optional or not.
|
||||
/// </summary>
|
||||
public bool IsOptional { get; set; }
|
||||
}
|
||||
28
src/modules/Elsa.JavaScript/Models/TypeDefinition.cs
Normal file
28
src/modules/Elsa.JavaScript/Models/TypeDefinition.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
namespace Elsa.JavaScript.Models;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A base type that represents a type.
|
||||
/// </summary>
|
||||
public sealed class TypeDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// The keyword that declares this type. E.g. "class", "type" or "interface".
|
||||
/// </summary>
|
||||
public string DeclarationKeyword { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the type.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// A list of fields.
|
||||
/// </summary>
|
||||
public ICollection<PropertyDefinition> Properties { get; set; } = new List<PropertyDefinition>();
|
||||
|
||||
/// <summary>
|
||||
/// A list of methods.
|
||||
/// </summary>
|
||||
public ICollection<FunctionDefinition> Methods { get; set; } = new List<FunctionDefinition>();
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
using Elsa.Workflows.Core.Models;
|
||||
|
||||
namespace Elsa.JavaScript.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Provides context to intellisense providers.
|
||||
/// </summary>
|
||||
public record TypeDefinitionContext(ICollection<Variable> Variables, string? ActivityTypeName, string? PropertyName, CancellationToken CancellationToken);
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
namespace Elsa.JavaScript.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a type definition document model.
|
||||
/// </summary>
|
||||
public sealed class TypeDefinitionsDocument
|
||||
{
|
||||
public ICollection<FunctionDefinition> Functions { get; set; } = new List<FunctionDefinition>();
|
||||
public ICollection<TypeDefinition> Types { get; set; } = new List<TypeDefinition>();
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
using Elsa.Extensions;
|
||||
using Elsa.JavaScript.Abstractions;
|
||||
using Elsa.JavaScript.Extensions;
|
||||
using Elsa.JavaScript.Models;
|
||||
using Elsa.JavaScript.Services;
|
||||
using Humanizer;
|
||||
|
||||
namespace Elsa.JavaScript.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Produces <see cref="FunctionDefinition"/>s for common functions.
|
||||
/// </summary>
|
||||
internal class CommonFunctionDefinitionProvider : FunctionDefinitionProvider
|
||||
{
|
||||
private readonly ITypeAliasRegistry _typeAliasRegistry;
|
||||
|
||||
public CommonFunctionDefinitionProvider(ITypeAliasRegistry typeAliasRegistry)
|
||||
{
|
||||
_typeAliasRegistry = typeAliasRegistry;
|
||||
}
|
||||
|
||||
protected override IEnumerable<FunctionDefinition> GetFunctionDefinitions(TypeDefinitionContext context)
|
||||
{
|
||||
yield return CreateFunctionDefinition(builder => builder
|
||||
.Name("setVariable")
|
||||
.Parameter("name", "string")
|
||||
.Parameter("value", "any"));
|
||||
|
||||
yield return CreateFunctionDefinition(builder => builder
|
||||
.Name("getVariable")
|
||||
.Parameter("name", "string")
|
||||
.ReturnType("any"));
|
||||
|
||||
yield return CreateFunctionDefinition(builder => builder
|
||||
.Name("isNullOrWhiteSpace")
|
||||
.Parameter("value", "string")
|
||||
.ReturnType("boolean"));
|
||||
|
||||
yield return CreateFunctionDefinition(builder => builder
|
||||
.Name("isNullOrEmpty")
|
||||
.Parameter("value", "string")
|
||||
.ReturnType("boolean"));
|
||||
|
||||
// Variable getter and setters.
|
||||
foreach (var variable in context.Variables)
|
||||
{
|
||||
var pascalName = variable.Name.Pascalize();
|
||||
var variableType = variable.GetVariableType();
|
||||
var typeAlias = _typeAliasRegistry.TryGetAlias(variableType, out var alias) ? alias : "any";
|
||||
|
||||
// get{Variable}.
|
||||
yield return CreateFunctionDefinition(builder => builder.Name($"get{pascalName}").ReturnType(typeAlias));
|
||||
|
||||
// set{Variable}.
|
||||
yield return CreateFunctionDefinition(builder => builder.Name($"set{pascalName}").Parameter("value", typeAlias));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,9 +5,9 @@ using Elsa.Workflows.Core.Services;
|
|||
|
||||
namespace Elsa.JavaScript.Providers;
|
||||
|
||||
public class JavaScriptExpressionSyntaxProvider : IExpressionSyntaxProvider
|
||||
internal class JavaScriptExpressionSyntaxProvider : IExpressionSyntaxProvider
|
||||
{
|
||||
public const string SyntaxName = "JavaScript";
|
||||
private const string SyntaxName = "JavaScript";
|
||||
private readonly IIdentityGenerator _identityGenerator;
|
||||
|
||||
public JavaScriptExpressionSyntaxProvider(IIdentityGenerator identityGenerator)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
using System.Dynamic;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.JavaScript.Abstractions;
|
||||
using Elsa.JavaScript.Models;
|
||||
using Elsa.JavaScript.Services;
|
||||
|
||||
namespace Elsa.JavaScript.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Produces <see cref="TypeDefinition"/>s for variable types.
|
||||
/// </summary>
|
||||
internal class VariableTypeDefinitionProvider : TypeDefinitionProvider
|
||||
{
|
||||
private readonly ITypeDescriber _typeDescriber;
|
||||
|
||||
public VariableTypeDefinitionProvider(ITypeDescriber typeDescriber)
|
||||
{
|
||||
_typeDescriber = typeDescriber;
|
||||
}
|
||||
|
||||
protected override IEnumerable<TypeDefinition> GetTypeDefinitions(TypeDefinitionContext context)
|
||||
{
|
||||
var excludedTypes = new[] { typeof(ExpandoObject), typeof(IDictionary<string, object>) };
|
||||
|
||||
var variableTypeQuery =
|
||||
from variable in context.Variables
|
||||
let variableType = variable.GetVariableType()
|
||||
where (variableType.IsClass || variableType.IsInterface) && !variableType.IsPrimitive && !excludedTypes.Any(x => x.IsAssignableFrom(variableType))
|
||||
select variableType;
|
||||
|
||||
var variableTypes = variableTypeQuery.Distinct();
|
||||
|
||||
foreach (var variableType in variableTypes)
|
||||
{
|
||||
yield return _typeDescriber.DescribeType(variableType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
using Elsa.JavaScript.Models;
|
||||
|
||||
namespace Elsa.JavaScript.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides <see cref="FunctionDefinition"/>s to the type definition document being constructed.
|
||||
/// </summary>
|
||||
public interface IFunctionDefinitionProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a list of type definitions to the system.
|
||||
/// </summary>
|
||||
ValueTask<IEnumerable<FunctionDefinition>> GetFunctionDefinitionsAsync(TypeDefinitionContext context);
|
||||
}
|
||||
17
src/modules/Elsa.JavaScript/Services/ITypeAliasRegistry.cs
Normal file
17
src/modules/Elsa.JavaScript/Services/ITypeAliasRegistry.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
namespace Elsa.JavaScript.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A central registry of type aliases.
|
||||
/// </summary>
|
||||
public interface ITypeAliasRegistry
|
||||
{
|
||||
/// <summary>
|
||||
/// Register a type with an alias.
|
||||
/// </summary>
|
||||
void RegisterType(Type type, string alias);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get an alias for the specified type.
|
||||
/// </summary>
|
||||
bool TryGetAlias(Type type, out string alias);
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
using Elsa.JavaScript.Models;
|
||||
|
||||
namespace Elsa.JavaScript.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Renders a <see cref="TypeDefinitionsDocument"/> to a string.
|
||||
/// </summary>
|
||||
public interface ITypeDefinitionDocumentRenderer
|
||||
{
|
||||
/// Renders a <see cref="TypeDefinitionsDocument"/> to a string.
|
||||
string Render(TypeDefinitionsDocument document);
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
using Elsa.JavaScript.Models;
|
||||
|
||||
namespace Elsa.JavaScript.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides <see cref="TypeDefinition"/>s to the type definition document being constructed.
|
||||
/// </summary>
|
||||
public interface ITypeDefinitionProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a list of type definitions to the system.
|
||||
/// </summary>
|
||||
ValueTask<IEnumerable<TypeDefinition>> GetTypeDefinitionsAsync(TypeDefinitionContext context);
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
using Elsa.JavaScript.Models;
|
||||
|
||||
namespace Elsa.JavaScript.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Generates type definitions for the specified context.
|
||||
/// </summary>
|
||||
public interface ITypeDefinitionService
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates type definitions for the specified context.
|
||||
/// </summary>
|
||||
Task<string> GenerateTypeDefinitionsAsync(TypeDefinitionContext context);
|
||||
}
|
||||
14
src/modules/Elsa.JavaScript/Services/ITypeDescriber.cs
Normal file
14
src/modules/Elsa.JavaScript/Services/ITypeDescriber.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
using Elsa.JavaScript.Models;
|
||||
|
||||
namespace Elsa.JavaScript.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="TypeDefinition"/> from a given <see cref="Type"/>.
|
||||
/// </summary>
|
||||
public interface ITypeDescriber
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a <see cref="TypeDefinition"/> from a given <see cref="Type"/>.
|
||||
/// </summary>
|
||||
TypeDefinition DescribeType(Type type);
|
||||
}
|
||||
|
|
@ -13,8 +13,14 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\common\Elsa.Api.Common\Elsa.Api.Common.csproj" />
|
||||
<ProjectReference Include="..\Elsa.ActivityDefinitions\Elsa.ActivityDefinitions.csproj" />
|
||||
<ProjectReference Include="..\Elsa.JavaScript\Elsa.JavaScript.csproj" />
|
||||
<ProjectReference Include="..\Elsa.Workflows.Management\Elsa.Workflows.Management.csproj" />
|
||||
<ProjectReference Include="..\Elsa.Workflows.Runtime\Elsa.Workflows.Runtime.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Endpoints\Scripting" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
using System.Text;
|
||||
using Elsa.ActivityDefinitions.Services;
|
||||
using Elsa.Common.Models;
|
||||
using Elsa.JavaScript.Models;
|
||||
using Elsa.JavaScript.Services;
|
||||
using Elsa.Workflows.Core.Models;
|
||||
using Elsa.Workflows.Runtime.Services;
|
||||
using FastEndpoints;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.Api.Endpoints.Scripting.JavaScript.TypeDefinitions;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a TypeScript definition that is used by the Monaco editor to display intellisense for JavaScript expressions.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
internal class Get : Endpoint<Request>
|
||||
{
|
||||
private readonly ITypeDefinitionService _typeDefinitionService;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Get(ITypeDefinitionService typeDefinitionService, IServiceProvider serviceProvider)
|
||||
{
|
||||
_typeDefinitionService = typeDefinitionService;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Configure()
|
||||
{
|
||||
Post("scripting/javascript/type-definitions/{containerId}");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task HandleAsync(Request request, CancellationToken cancellationToken)
|
||||
{
|
||||
var variables = await GetVariables(request.ContainerType, request.ContainerId, cancellationToken);
|
||||
var intellisenseContext = new TypeDefinitionContext(variables, request.ActivityTypeName, request.PropertyName, cancellationToken);
|
||||
var typeDefinitions = await _typeDefinitionService.GenerateTypeDefinitionsAsync(intellisenseContext);
|
||||
var fileName = $"elsa.{request.ContainerId}.d.ts";
|
||||
var data = Encoding.UTF8.GetBytes(typeDefinitions);
|
||||
|
||||
await SendBytesAsync(data, fileName, "application/x-typescript", cancellation: cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<ICollection<Variable>> GetVariables(string containerType, string containerId, CancellationToken cancellationToken)
|
||||
{
|
||||
// TODO: Make this extensible.
|
||||
|
||||
switch (containerType)
|
||||
{
|
||||
case "workflow-definition":
|
||||
{
|
||||
var workflowDefinitionService = _serviceProvider.GetService<IWorkflowDefinitionService>();
|
||||
var workflowDefinition = workflowDefinitionService != null ? await workflowDefinitionService.FindAsync(containerId, VersionOptions.Latest, cancellationToken) : default;
|
||||
var x = workflowDefinition;
|
||||
return workflowDefinition?.Variables ?? new List<Variable>();
|
||||
}
|
||||
case "activity-definition":
|
||||
{
|
||||
var activityDefinitionStore = _serviceProvider.GetService<IActivityDefinitionStore>();
|
||||
var activityDefinition = activityDefinitionStore != null ? await activityDefinitionStore.FindByDefinitionIdAsync(containerId, VersionOptions.Latest, cancellationToken) : default;
|
||||
return activityDefinition?.Variables ?? new List<Variable>();
|
||||
}
|
||||
}
|
||||
|
||||
return new List<Variable>();
|
||||
}
|
||||
}
|
||||
|
||||
internal record Request(string ContainerType, string ContainerId, string? ActivityTypeName, string? PropertyName)
|
||||
{
|
||||
public Request() : this(default!, default!, default, default)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -53,4 +53,13 @@ public static class VariableExtensions
|
|||
var genericType = variable.GetType().GenericTypeArguments.FirstOrDefault();
|
||||
return genericType == null ? value : value.ConvertTo(genericType)!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the type of the variable.
|
||||
/// </summary>
|
||||
public static Type GetVariableType(this Variable variable)
|
||||
{
|
||||
var variableType = variable.GetType();
|
||||
return variableType.GenericTypeArguments.Any() ? variableType.GetGenericArguments().First() : typeof(object);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Elsa.Expressions.Extensions;
|
||||
using Elsa.Expressions.Helpers;
|
||||
using Elsa.Expressions.Services;
|
||||
using Elsa.Extensions;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using Elsa.Expressions.Extensions;
|
||||
using Elsa.Expressions.Helpers;
|
||||
using Elsa.Expressions.Services;
|
||||
using Elsa.Extensions;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Elsa.Expressions.Extensions;
|
||||
using Elsa.Expressions.Services;
|
||||
using Elsa.Workflows.Core.Models;
|
||||
using Elsa.Workflows.Core.Services;
|
||||
|
|
|
|||
Loading…
Reference in a new issue