Expose HTTP client creation events

This allows applications to configure axios middleware to set e.g. authorization headers for outgoing HTTP requests made to Elsa Server

Fixes #1164
This commit is contained in:
Sipke Schoorstra 2021-06-28 13:56:09 +02:00
parent efec65e819
commit f50674a6b4
9 changed files with 549 additions and 6884 deletions

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,11 @@
"name": "@elsa-workflows/elsa-workflows-studio",
"version": "2.0.1",
"description": "Elsa Workflow Studio is a collection of web components to manage Elsa workflows stored on an Elsa Workflows server.",
"repository": {
"type": "git",
"url": "https://github.com/elsa-workflows/elsa-core.git",
"directory": "src/designer/elsa-workflows-studio"
},
"main": "dist/index.cjs.js",
"module": "dist/custom-elements/index.js",
"es2015": "dist/esm/index.mjs",
@ -40,6 +45,7 @@
"@tailwindcss/typography": "^0.4.0",
"autoprefixer": "10.2.5",
"axios": "^0.21.1",
"axios-middleware": "^0.3.1",
"cssnano": "^4.1.10",
"d3": "^6.6.2",
"dagre-d3": "^0.6.4",
@ -53,7 +59,7 @@
"postcss-import": "^14.0.0",
"postcss-preset-env": "^6.7.0",
"stencil-click-outside": "^1.7.1",
"tailwindcss": "2.1.1",
"tailwindcss": "^2.2.4",
"tslint": "6.1.3",
"tslint-stencil": "1.0.1",
"typescript": "4.2.3",

View file

@ -10,6 +10,8 @@ import { LocationSegments, MatchResults, RouterHistory } from "@stencil/router";
import { MenuItem } from "./components/controls/elsa-context-menu/models";
import { ActivityContextMenuState, LayoutDirection, WorkflowDesignerMode } from "./components/designers/tree/elsa-designer-tree/models";
import { DropdownButtonItem, DropdownButtonOrigin } from "./components/controls/elsa-dropdown-button/models";
import { AxiosInstance, AxiosRequestConfig } from "axios";
import { Service } from "axios-middleware";
import { MonacoValueChangedArgs } from "./components/controls/elsa-monaco/elsa-monaco";
import { Map } from "./utils/utils";
import { ToastNotificationOptions } from "./components/shared/elsa-toast-notification/elsa-toast-notification";
@ -69,6 +71,8 @@ export namespace Components {
"singleLineMode": boolean;
"workflowDefinitionId": string;
}
interface ElsaExternalEvents {
}
interface ElsaInputTags {
"fieldId"?: string;
"fieldName"?: string;
@ -324,6 +328,12 @@ declare global {
prototype: HTMLElsaExpressionEditorElement;
new (): HTMLElsaExpressionEditorElement;
};
interface HTMLElsaExternalEventsElement extends Components.ElsaExternalEvents, HTMLStencilElement {
}
var HTMLElsaExternalEventsElement: {
prototype: HTMLElsaExternalEventsElement;
new (): HTMLElsaExternalEventsElement;
};
interface HTMLElsaInputTagsElement extends Components.ElsaInputTags, HTMLStencilElement {
}
var HTMLElsaInputTagsElement: {
@ -557,6 +567,7 @@ declare global {
"elsa-dropdown-button": HTMLElsaDropdownButtonElement;
"elsa-dropdown-property": HTMLElsaDropdownPropertyElement;
"elsa-expression-editor": HTMLElsaExpressionEditorElement;
"elsa-external-events": HTMLElsaExternalEventsElement;
"elsa-input-tags": HTMLElsaInputTagsElement;
"elsa-input-tags-dropdown": HTMLElsaInputTagsDropdownElement;
"elsa-modal-dialog": HTMLElsaModalDialogElement;
@ -652,6 +663,10 @@ declare namespace LocalJSX {
"singleLineMode"?: boolean;
"workflowDefinitionId"?: string;
}
interface ElsaExternalEvents {
"onHttpClientConfigCreated"?: (event: CustomEvent<AxiosRequestConfig>) => void;
"onHttpClientCreated"?: (event: CustomEvent<{ service: Service, axiosInstance: AxiosInstance }>) => void;
}
interface ElsaInputTags {
"fieldId"?: string;
"fieldName"?: string;
@ -851,6 +866,7 @@ declare namespace LocalJSX {
"elsa-dropdown-button": ElsaDropdownButton;
"elsa-dropdown-property": ElsaDropdownProperty;
"elsa-expression-editor": ElsaExpressionEditor;
"elsa-external-events": ElsaExternalEvents;
"elsa-input-tags": ElsaInputTags;
"elsa-input-tags-dropdown": ElsaInputTagsDropdown;
"elsa-modal-dialog": ElsaModalDialog;
@ -904,6 +920,7 @@ declare module "@stencil/core" {
"elsa-dropdown-button": LocalJSX.ElsaDropdownButton & JSXBase.HTMLAttributes<HTMLElsaDropdownButtonElement>;
"elsa-dropdown-property": LocalJSX.ElsaDropdownProperty & JSXBase.HTMLAttributes<HTMLElsaDropdownPropertyElement>;
"elsa-expression-editor": LocalJSX.ElsaExpressionEditor & JSXBase.HTMLAttributes<HTMLElsaExpressionEditorElement>;
"elsa-external-events": LocalJSX.ElsaExternalEvents & JSXBase.HTMLAttributes<HTMLElsaExternalEventsElement>;
"elsa-input-tags": LocalJSX.ElsaInputTags & JSXBase.HTMLAttributes<HTMLElsaInputTagsElement>;
"elsa-input-tags-dropdown": LocalJSX.ElsaInputTagsDropdown & JSXBase.HTMLAttributes<HTMLElsaInputTagsDropdownElement>;
"elsa-modal-dialog": LocalJSX.ElsaModalDialog & JSXBase.HTMLAttributes<HTMLElsaModalDialogElement>;

View file

@ -0,0 +1,37 @@
import {Component, h, Prop, Event, EventEmitter, State, Watch} from '@stencil/core';
import {AxiosInstance, AxiosRequestConfig} from "axios";
import {Service} from 'axios-middleware';
import {eventBus} from "../../../services/event-bus";
import {EventTypes} from "../../../models";
@Component({
tag: 'elsa-external-events',
shadow: false,
})
export class ElsaExternalEvents {
@Event() httpClientConfigCreated: EventEmitter<AxiosRequestConfig>;
@Event() httpClientCreated: EventEmitter<{ service: Service, axiosInstance: AxiosInstance }>;
connectedCallback() {
eventBus.on(EventTypes.HttpClientConfigCreated, this.onHttpClientConfigCreated);
eventBus.on(EventTypes.HttpClientCreated, this.onHttpClientCreated);
}
disconnectedCallback() {
eventBus.detach(EventTypes.HttpClientConfigCreated, this.onHttpClientConfigCreated);
eventBus.detach(EventTypes.HttpClientCreated, this.onHttpClientCreated);
}
private onHttpClientConfigCreated = (config: AxiosRequestConfig) => {
this.httpClientConfigCreated.emit(config);
};
private onHttpClientCreated = (service: Service, axiosInstance: AxiosInstance) => {
this.httpClientCreated.emit({service, axiosInstance});
};
render() {
return <slot/>
}
}

View file

@ -32,16 +32,20 @@ export class ElsaStudioRoot {
</div>
<div class="hidden md:elsa-block">
<div class="elsa-ml-10 elsa-flex elsa-items-baseline elsa-space-x-4">
<stencil-route-link url="/workflow-definitions" anchorClass="elsa-text-gray-300 hover:elsa-bg-gray-700 hover:elsa-text-white elsa-px-3 elsa-py-2 elsa-rounded-md elsa-text-sm elsa-font-medium" activeClass="elsa-text-white elsa-bg-gray-900">Workflow Definitions
<stencil-route-link url="/workflow-definitions" anchorClass="elsa-text-gray-300 hover:elsa-bg-gray-700 hover:elsa-text-white elsa-px-3 elsa-py-2 elsa-rounded-md elsa-text-sm elsa-font-medium"
activeClass="elsa-text-white elsa-bg-gray-900">Workflow Definitions
</stencil-route-link>
<stencil-route-link url="/workflow-instances" anchorClass="elsa-text-gray-300 hover:elsa-bg-gray-700 hover:elsa-text-white elsa-px-3 elsa-py-2 elsa-rounded-md elsa-text-sm elsa-font-medium" activeClass="elsa-text-white elsa-bg-gray-900">Workflow Instances
<stencil-route-link url="/workflow-instances" anchorClass="elsa-text-gray-300 hover:elsa-bg-gray-700 hover:elsa-text-white elsa-px-3 elsa-py-2 elsa-rounded-md elsa-text-sm elsa-font-medium"
activeClass="elsa-text-white elsa-bg-gray-900">Workflow Instances
</stencil-route-link>
<stencil-route-link url="/workflow-registry" anchorClass="elsa-text-gray-300 hover:elsa-bg-gray-700 hover:elsa-text-white elsa-px-3 elsa-py-2 elsa-rounded-md elsa-text-sm elsa-font-medium" activeClass="elsa-text-white elsa-bg-gray-900">Workflow Registry
<stencil-route-link url="/workflow-registry" anchorClass="elsa-text-gray-300 hover:elsa-bg-gray-700 hover:elsa-text-white elsa-px-3 elsa-py-2 elsa-rounded-md elsa-text-sm elsa-font-medium"
activeClass="elsa-text-white elsa-bg-gray-900">Workflow Registry
</stencil-route-link>
{/*<stencil-route-link url="/custom-activities" class="elsa-text-gray-300 hover:elsa-bg-gray-700 hover:elsa-text-white elsa-px-3 elsa-py-2 elsa-rounded-md elsa-text-sm elsa-font-medium">Custom Activities</stencil-route-link>*/}
<stencil-route-link url="/webhook-definitions" anchorClass="elsa-text-gray-300 hover:elsa-bg-gray-700 hover:elsa-text-white elsa-px-3 elsa-py-2 elsa-rounded-md elsa-text-sm elsa-font-medium" activeClass="elsa-text-white elsa-bg-gray-900">Webhook Definitions
</stencil-route-link>
<stencil-route-link url="/webhook-definitions" anchorClass="elsa-text-gray-300 hover:elsa-bg-gray-700 hover:elsa-text-white elsa-px-3 elsa-py-2 elsa-rounded-md elsa-text-sm elsa-font-medium"
activeClass="elsa-text-white elsa-bg-gray-900">Webhook Definitions
</stencil-route-link>
{/*<stencil-route-link url="/custom-activities" class="elsa-text-gray-300 hover:elsa-bg-gray-700 hover:elsa-text-white elsa-px-3 elsa-py-2 elsa-rounded-md elsa-text-sm elsa-font-medium">Custom Activities</stencil-route-link>*/}
</div>
</div>
@ -56,17 +60,18 @@ export class ElsaStudioRoot {
<stencil-route-switch scrollTopOffset={0}>
<stencil-route url="/" component="elsa-studio-home" exact={true}/>
<stencil-route url="/workflow-registry" component="elsa-studio-workflow-registry" componentProps={{'serverUrl': serverUrl}} exact={true}/>
<stencil-route url="/workflow-registry/:id" component="elsa-studio-workflow-blueprint-view" componentProps={{'serverUrl': serverUrl}} />
<stencil-route url="/workflow-registry/:id" component="elsa-studio-workflow-blueprint-view" componentProps={{'serverUrl': serverUrl}}/>
<stencil-route url="/workflow-definitions" component="elsa-studio-workflow-definitions-list" componentProps={{'serverUrl': serverUrl}} exact={true}/>
<stencil-route url="/workflow-definitions/:id" component="elsa-studio-workflow-definitions-edit" componentProps={{'serverUrl': serverUrl, 'monacoLibPath': monacoLibPath}}/>
<stencil-route url="/workflow-instances" component="elsa-studio-workflow-instances-list" componentProps={{'serverUrl': serverUrl}} exact={true}/>
<stencil-route url="/workflow-instances/:id" component="elsa-studio-workflow-instances-view" componentProps={{'serverUrl': serverUrl}}/>
<stencil-route url="/webhook-definitions" component="elsa-studio-webhook-definitions-list" componentProps={{'serverUrl': serverUrl}} exact={true}/>
<stencil-route url="/webhook-definitions/:id" component="elsa-studio-webhook-definitions-edit" componentProps={{'serverUrl': serverUrl}}/>
<stencil-route url="/webhook-definitions/:id" component="elsa-studio-webhook-definitions-edit" componentProps={{'serverUrl': serverUrl}}/>
</stencil-route-switch>
</stencil-router>
{/*</Tunnel.Provider>*/}
</main>
<elsa-external-events/>
</div>
);
}

View file

@ -274,7 +274,7 @@ export class ElsaWorkflowDefinitionEditorScreen {
mapActivityModel(source: ActivityDefinition): ActivityModel {
const activityDescriptors: Array<ActivityDescriptor> = state.activityDescriptors;
const activityDescriptor = activityDescriptors.find(x => x.type == source.type);
return {
activityId: source.activityId,
description: source.description,
@ -306,11 +306,6 @@ export class ElsaWorkflowDefinitionEditorScreen {
eventBus.emit(EventTypes.ShowWorkflowSettings);
}
private onUpdateWorkflowSettings = async (workflowDefinition: WorkflowDefinition) => {
this.updateWorkflowDefinition(workflowDefinition);
await this.saveWorkflowInternal(this.workflowModel);
}
async onPublishClicked() {
await this.publishWorkflow();
}
@ -343,6 +338,11 @@ export class ElsaWorkflowDefinitionEditorScreen {
this.activityContextMenuState = e.detail;
}
private onUpdateWorkflowSettings = async (workflowDefinition: WorkflowDefinition) => {
this.updateWorkflowDefinition(workflowDefinition);
await this.saveWorkflowInternal(this.workflowModel);
}
render() {
const tunnelState: WorkflowEditorState = {
serverUrl: this.serverUrl,

View file

@ -18,5 +18,32 @@
<!--<elsa-studio-root server-url="https://localhost:6080" monaco-lib-path="build/assets/js/monaco-editor/min"></elsa-studio-root>-->
<!--<elsa-studio-root server-url="https://skynet-workflow.azurewebsites.net" monaco-lib-path="build/assets/js/monaco-editor/min"></elsa-studio-root>-->
<script>
const elsaStudioRoot = document.querySelector('elsa-studio-root');
elsaStudioRoot.addEventListener('httpClientCreated', event => {
// Get access to the axios middleware service and register a sample middleware:
const { service } = event.detail;
// Register a sample middleware.
service.register({
onRequest(config) {
console.log('onRequest');
config.headers = {
'X-Client': 'Elsa Dashboard',
Authorization: 'Bearer 123'
};
return config;
},
onResponse(response) {
console.log('onResponse');
return response;
}
});
})
</script>
</body>
</html>

View file

@ -14,6 +14,8 @@ export const EventTypes = {
WorkflowPublished: 'workflow-published',
WorkflowRetracted: 'workflow-retracted',
WorkflowImported: 'workflow-imported',
HttpClientConfigCreated: 'http-client-config-created',
HttpClientCreated: 'http-client-created'
};
export interface AddActivityEventArgs {

View file

@ -1,35 +1,56 @@
import axios, {AxiosRequestConfig} from "axios";
import {Service} from 'axios-middleware';
import * as collection from 'lodash/collection';
import {eventBus} from './event-bus';
import {
ActivityDefinition,
ActivityDescriptor,
ConnectionDefinition,
getVersionOptionsString, OrderBy,
PagedList, SelectListItem,
EventTypes,
getVersionOptionsString,
OrderBy,
PagedList,
SelectListItem,
Variables,
VersionOptions, WorkflowBlueprint, WorkflowBlueprintSummary,
VersionOptions,
WorkflowBlueprint,
WorkflowBlueprintSummary,
WorkflowContextOptions,
WorkflowDefinition,
WorkflowDefinitionSummary, WorkflowExecutionLogRecord, WorkflowFault, WorkflowInstance, WorkflowInstanceSummary,
WorkflowDefinitionSummary,
WorkflowExecutionLogRecord,
WorkflowInstance,
WorkflowInstanceSummary,
WorkflowPersistenceBehavior,
WorkflowStatus,
WorkflowStorageDescriptor
} from "../models";
import { WebhookDefinition, WebhookDefinitionSummary } from "../models/webhook";
import {WebhookDefinition, WebhookDefinitionSummary} from "../models/webhook";
let _elsaClient: ElsaClient = null;
export const createElsaClient = function (serverUrl: string): ElsaClient {
if (!!_elsaClient)
return _elsaClient;
const config: AxiosRequestConfig = {
baseURL: serverUrl
};
const httpClient = axios.create(config);
eventBus.emit(EventTypes.HttpClientConfigCreated, this, config);
return {
activitiesApi: {
const httpClient = axios.create(config);
const service = new Service(httpClient);
eventBus.emit(EventTypes.HttpClientCreated, this, service, httpClient);
_elsaClient = {
activitiesApi: {
list: async () => {
const response = await httpClient.get<Array<ActivityDescriptor>>('v1/activities');
return response.data;
}
}
},
workflowDefinitionsApi: {
list: async (page?: number, pageSize?: number, versionOptions?: VersionOptions) => {
@ -80,7 +101,7 @@ export const createElsaClient = function (serverUrl: string): ElsaClient {
}
},
webhookDefinitionsApi: {
list: async (page?: number, pageSize?: number) => {
list: async (page?: number, pageSize?: number) => {
const response = await httpClient.get<PagedList<WebhookDefinitionSummary>>(`v1/webhook-definitions`);
return response.data;
},
@ -95,11 +116,11 @@ export const createElsaClient = function (serverUrl: string): ElsaClient {
update: async request => {
const response = await httpClient.put<WebhookDefinition>('v1/webhook-definitions', request);
return response.data;
},
},
delete: async webhookId => {
await httpClient.delete(`v1/webhook-definitions/${webhookId}`);
},
},
},
workflowRegistryApi: {
list: async (page?: number, pageSize?: number, versionOptions?: VersionOptions): Promise<PagedList<WorkflowBlueprintSummary>> => {
const versionOptionsString = getVersionOptionsString(versionOptions);
@ -140,7 +161,7 @@ export const createElsaClient = function (serverUrl: string): ElsaClient {
const response = await httpClient.get<PagedList<WorkflowInstanceSummary>>(`v1/workflow-instances${queryStringText}`);
return response.data;
},
get: async id => {
get: async id => {
const response = await httpClient.get(`v1/workflow-instances/${id}`);
return response.data;
},
@ -177,8 +198,8 @@ export const createElsaClient = function (serverUrl: string): ElsaClient {
return response.data;
}
},
designerApi: {
runtimeSelectItemsApi: {
designerApi: {
runtimeSelectItemsApi: {
get: async (providerTypeName: string, context?: any): Promise<Array<SelectListItem>> => {
const response = await httpClient.post('v1/designer/runtime-select-list-items', {providerTypeName: providerTypeName, context: context});
return response.data;
@ -198,6 +219,8 @@ export const createElsaClient = function (serverUrl: string): ElsaClient {
}
}
}
return _elsaClient;
}
export interface ElsaClient {
@ -241,8 +264,8 @@ export interface WebhookDefinitionsApi {
getByWebhookId(webhookId: string): Promise<WebhookDefinition>;
save(request: SaveWebhookDefinitionRequest): Promise<WebhookDefinition>;
update(request: SaveWebhookDefinitionRequest): Promise<WebhookDefinition>;
update(request: SaveWebhookDefinitionRequest): Promise<WebhookDefinition>;
delete(webhookId: string): Promise<void>;
}
@ -316,10 +339,10 @@ export interface SaveWorkflowDefinitionRequest {
export interface SaveWebhookDefinitionRequest {
id?: string;
name?: string;
path?: string;
path?: string;
description?: string;
payloadTypeName?: string;
isEnabled?: boolean;
isEnabled?: boolean;
}
export interface ExportWorkflowResponse {
@ -341,6 +364,6 @@ interface ActivityEventCount {
count: number;
}
interface ActivityFault{
interface ActivityFault {
message: string;
}