Merge pull request #376 from OneUptime/workflow-project

Workflow project
This commit is contained in:
Simon Larsen
2023-03-02 09:32:47 +00:00
committed by GitHub
36 changed files with 1094 additions and 213 deletions

View File

@@ -22,6 +22,7 @@ export enum ComponentInputType {
BaseModelArray = 'Database Records',
JSONArray = 'List of JSON',
LongText = 'Long Text',
HTML = 'HTML',
}
export enum ComponentType {

View File

@@ -4,6 +4,15 @@ enum ComponentID {
Schedule = 'schedule',
JavaScriptCode = 'javascript',
Manual = 'manual',
JsonToText = 'json-to-text',
TextToJson = 'text-to-json',
MergeJson = 'merge-json',
ApiGet = 'api-get',
ApiPut = 'api-put',
ApiPost = 'api-post',
ApiDelete = 'api-delete',
SendEmail = 'send-email',
IfElse = 'if-else',
}
export default ComponentID;

View File

@@ -1,4 +1,5 @@
import IconProp from '../../Icon/IconProp';
import ComponentID from '../ComponentID';
import ComponentMetadata, {
ComponentInputType,
ComponentType,
@@ -6,7 +7,7 @@ import ComponentMetadata, {
const components: Array<ComponentMetadata> = [
{
id: 'api-get',
id: ComponentID.ApiGet,
title: 'API Get (JSON)',
category: 'API',
description: 'Send Get API Request and get JSON Response',
@@ -26,17 +27,9 @@ const components: Array<ComponentMetadata> = [
name: 'Request Body',
description: 'Request Body in JSON',
type: ComponentInputType.JSON,
required: true,
placeholder: '{"key1": "value1", "key2": "value2", ....}',
},
{
id: 'query-string',
name: 'Query String',
description: 'Send query string params.',
type: ComponentInputType.StringDictionary,
required: false,
isAdvanced: true,
placeholder: '{"query1": "value1", "query2": "value2", ....}',
placeholder:
'Example: {"key1": "value1", "key2": "value2", ....}',
},
{
id: 'request-headers',
@@ -45,7 +38,8 @@ const components: Array<ComponentMetadata> = [
type: ComponentInputType.StringDictionary,
required: false,
isAdvanced: true,
placeholder: '{"header1": "value1", "header2": "value2", ....}',
placeholder:
'Example: {"header1": "value1", "header2": "value2", ....}',
},
],
returnValues: [
@@ -57,7 +51,7 @@ const components: Array<ComponentMetadata> = [
required: false,
},
{
id: 'status',
id: 'response-status',
name: 'Response Status',
description: 'Response Status (200, for example)',
type: ComponentInputType.Number,
@@ -101,7 +95,7 @@ const components: Array<ComponentMetadata> = [
],
},
{
id: 'api-post',
id: ComponentID.ApiPost,
title: 'API Post (JSON)',
category: 'API',
description: 'Send a POST Request and get JSON Response',
@@ -121,17 +115,9 @@ const components: Array<ComponentMetadata> = [
name: 'Request Body',
description: 'Request Body in JSON',
type: ComponentInputType.JSON,
required: true,
placeholder: '{"key1": "value1", "key2": "value2", ....}',
},
{
id: 'query-string',
name: 'Query String',
description: 'Send query string params.',
type: ComponentInputType.StringDictionary,
required: false,
isAdvanced: true,
placeholder: '{"query1": "value1", "query2": "value2", ....}',
placeholder:
'Example: {"key1": "value1", "key2": "value2", ....}',
},
{
id: 'request-headers',
@@ -140,7 +126,8 @@ const components: Array<ComponentMetadata> = [
type: ComponentInputType.StringDictionary,
required: false,
isAdvanced: true,
placeholder: '{"header1": "value1", "header2": "value2", ....}',
placeholder:
'Example: {"header1": "value1", "header2": "value2", ....}',
},
],
returnValues: [
@@ -152,7 +139,7 @@ const components: Array<ComponentMetadata> = [
required: false,
},
{
id: 'status',
id: 'response-status',
name: 'Response Status',
description: 'Response Status (200, for example)',
type: ComponentInputType.Number,
@@ -196,8 +183,8 @@ const components: Array<ComponentMetadata> = [
],
},
{
id: 'api-patch',
title: 'API Patch (JSON)',
id: ComponentID.ApiPut,
title: 'API Put (JSON)',
category: 'API',
description: 'Send a PATCH Request and get JSON Response',
iconProp: IconProp.Globe,
@@ -216,17 +203,9 @@ const components: Array<ComponentMetadata> = [
name: 'Request Body',
description: 'Request Body in JSON',
type: ComponentInputType.JSON,
required: true,
placeholder: '{"key1": "value1", "key2": "value2", ....}',
},
{
id: 'query-string',
name: 'Query String',
description: 'Send query string params.',
type: ComponentInputType.StringDictionary,
required: false,
isAdvanced: true,
placeholder: '{"query1": "value1", "query2": "value2", ....}',
placeholder:
'Example: {"key1": "value1", "key2": "value2", ....}',
},
{
id: 'request-headers',
@@ -235,7 +214,8 @@ const components: Array<ComponentMetadata> = [
type: ComponentInputType.StringDictionary,
required: false,
isAdvanced: true,
placeholder: '{"header1": "value1", "header2": "value2", ....}',
placeholder:
'Example: {"header1": "value1", "header2": "value2", ....}',
},
],
returnValues: [
@@ -247,7 +227,7 @@ const components: Array<ComponentMetadata> = [
required: false,
},
{
id: 'status',
id: 'response-status',
name: 'Response Status',
description: 'Response Status (200, for example)',
type: ComponentInputType.Number,
@@ -291,7 +271,7 @@ const components: Array<ComponentMetadata> = [
],
},
{
id: 'api-delete',
id: ComponentID.ApiDelete,
title: 'API Delete (JSON)',
category: 'API',
description: 'Send a PATCH Request and get JSON Response',
@@ -311,17 +291,9 @@ const components: Array<ComponentMetadata> = [
name: 'Request Body',
description: 'Request Body in JSON',
type: ComponentInputType.JSON,
required: true,
placeholder: '{"key1": "value1", "key2": "value2", ....}',
},
{
id: 'query-string',
name: 'Query String',
description: 'Send query string params.',
type: ComponentInputType.StringDictionary,
required: false,
isAdvanced: true,
placeholder: '{"query1": "value1", "query2": "value2", ....}',
placeholder:
'Example: {"key1": "value1", "key2": "value2", ....}',
},
{
id: 'request-headers',
@@ -330,7 +302,8 @@ const components: Array<ComponentMetadata> = [
type: ComponentInputType.StringDictionary,
required: false,
isAdvanced: true,
placeholder: '{"header1": "value1", "header2": "value2", ....}',
placeholder:
'Example: {"header1": "value1", "header2": "value2", ....}',
},
],
returnValues: [
@@ -342,7 +315,7 @@ const components: Array<ComponentMetadata> = [
required: false,
},
{
id: 'status',
id: 'response-status',
name: 'Response Status',
description: 'Response Status (200, for example)',
type: ComponentInputType.Number,

View File

@@ -30,6 +30,7 @@ export default class BaseModelComponent {
description: `Query on ${model.singularName}`,
required: true,
id: 'query',
placeholder: "Example: {'columnName': 'value', ...}",
},
{
type: ComponentInputType.Query,
@@ -37,6 +38,7 @@ export default class BaseModelComponent {
description: `Select on ${model.singularName}`,
required: true,
id: 'select',
placeholder: "Example: {'columnName': true, ...}",
},
],
returnValues: [
@@ -87,6 +89,7 @@ export default class BaseModelComponent {
description: 'Please fill out this query',
required: true,
id: 'query',
placeholder: "Example: {'columnName': 'value', ...}",
},
{
type: ComponentInputType.Query,
@@ -94,6 +97,7 @@ export default class BaseModelComponent {
description: `Select on ${model.singularName}`,
required: true,
id: 'select',
placeholder: "Example: {'columnName': true, ...}",
},
{
type: ComponentInputType.Number,
@@ -189,6 +193,7 @@ export default class BaseModelComponent {
description: 'Please fill out this query',
required: true,
id: 'query',
placeholder: "Example: {'columnName': 'value', ...}",
},
],
returnValues: [],
@@ -231,6 +236,7 @@ export default class BaseModelComponent {
description: 'Please fill out this query',
required: true,
id: 'query',
placeholder: "Example: {'columnName': 'value', ...}",
},
{
type: ComponentInputType.Number,
@@ -314,6 +320,7 @@ export default class BaseModelComponent {
arguments: [
{
id: 'json',
placeholder: "Example: {'columnName': 'value', ...}",
name: 'JSON Object',
description: `${model.singularName} represented as JSON`,
type: ComponentInputType.JSON,
@@ -365,6 +372,8 @@ export default class BaseModelComponent {
{
id: 'json-array',
name: 'JSON Array',
placeholder:
"Example: [{'columnName': 'value', ...}, {...}]",
description: 'List of models represented as JSON array',
type: ComponentInputType.JSONArray,
required: true,
@@ -448,9 +457,11 @@ export default class BaseModelComponent {
description: 'Please fill out this query',
required: true,
id: 'query',
placeholder: "Example: {'columnName': 'value', ...}",
},
{
id: 'data',
placeholder: "Example: {'columnName': 'value', ...}",
name: 'Data (JSON Object)',
description: `${model.singularName} represented as JSON`,
type: ComponentInputType.JSON,
@@ -497,10 +508,12 @@ export default class BaseModelComponent {
description: 'Please fill out this query',
required: true,
id: 'query',
placeholder: "Example: {'columnName': 'value', ...}",
},
{
id: 'data',
name: 'Data (JSON Object)',
placeholder: "Example: {'columnName': 'value', ...}",
description: `${model.singularName} represented as JSON`,
type: ComponentInputType.JSON,
required: true,

View File

@@ -1,4 +1,5 @@
import IconProp from '../../Icon/IconProp';
import ComponentID from '../ComponentID';
import ComponentMetadata, {
ComponentInputType,
ComponentType,
@@ -6,87 +7,7 @@ import ComponentMetadata, {
const components: Array<ComponentMetadata> = [
{
id: 'if-true',
title: 'If True',
category: 'Conditions',
description: 'If the inputs are true then proceed',
iconProp: IconProp.Check,
componentType: ComponentType.Component,
arguments: [
{
type: ComponentInputType.Text,
name: 'Expression 1',
description: 'Expression 1',
required: true,
id: 'expression-1',
},
{
type: ComponentInputType.Text,
name: 'Expression 2',
description: 'Expression 2',
required: true,
id: 'expression-2',
},
],
returnValues: [],
inPorts: [
{
title: 'In',
description:
'Please connect components to this port for this component to work.',
id: 'in',
},
],
outPorts: [
{
title: 'Yes',
description: 'If, yes then this port will be executed',
id: 'yes',
},
],
},
{
id: 'if-false',
title: 'If False',
category: 'Conditions',
description: 'If the inputs are false then proceed',
iconProp: IconProp.Close,
componentType: ComponentType.Component,
arguments: [
{
type: ComponentInputType.Text,
name: 'Expression 1',
description: 'Expression 1',
required: true,
id: 'expression-1',
},
{
type: ComponentInputType.Text,
name: 'Expression 2',
description: 'Expression 2',
required: true,
id: 'expression-2',
},
],
returnValues: [],
inPorts: [
{
title: 'In',
description:
'Please connect components to this port for this component to work.',
id: 'in',
},
],
outPorts: [
{
title: 'Yes',
description: 'If, yes then this port will be executed',
id: 'yes',
},
],
},
{
id: 'if-else',
id: ComponentID.IfElse,
title: 'If / Else',
category: 'Conditions',
description: 'Branch based on Inputs',
@@ -95,17 +16,11 @@ const components: Array<ComponentMetadata> = [
arguments: [
{
type: ComponentInputType.Text,
name: 'Expression 1',
description: 'Expression 1',
name: 'Expression',
description: 'Expression',
placeholder: 'x === y',
required: true,
id: 'expression-1',
},
{
type: ComponentInputType.Text,
name: 'Expression 2',
description: 'Expression 2',
required: true,
id: 'expression-2',
id: 'expression',
},
],
returnValues: [],

View File

@@ -1,4 +1,5 @@
import IconProp from '../../Icon/IconProp';
import ComponentID from '../ComponentID';
import ComponentMetadata, {
ComponentInputType,
ComponentType,
@@ -6,7 +7,7 @@ import ComponentMetadata, {
const components: Array<ComponentMetadata> = [
{
id: 'send-email',
id: ComponentID.SendEmail,
title: 'Send Email',
category: 'Email',
description: 'Send email from your workflows',
@@ -15,10 +16,19 @@ const components: Array<ComponentMetadata> = [
arguments: [
{
type: ComponentInputType.Text,
name: 'Email',
description: 'Email to send to',
name: 'From Email',
description: 'Email to send from',
placeholder: 'Name <email@company.com>',
required: true,
id: 'email',
id: 'from',
},
{
type: ComponentInputType.Text,
name: 'To Email',
description: 'Email to send to',
placeholder: 'email@company.com; email2@company.com; ...',
required: true,
id: 'to',
},
{
type: ComponentInputType.Text,
@@ -28,7 +38,7 @@ const components: Array<ComponentMetadata> = [
id: 'subject',
},
{
type: ComponentInputType.LongText,
type: ComponentInputType.HTML,
name: 'Email Body',
description: 'Email to send to',
required: false,
@@ -39,28 +49,28 @@ const components: Array<ComponentMetadata> = [
name: 'SMTP HOST',
description: 'SMTP Host to send emails from',
required: true,
id: 'smtp_host',
id: 'smtp-host',
},
{
type: ComponentInputType.Text,
name: 'SMTP Username',
description: 'SMTP Username to send emails from',
required: true,
id: 'smtp_username',
id: 'smtp-username',
},
{
type: ComponentInputType.Password,
name: 'SMTP Password',
description: 'SMTP Password to send emails from',
required: true,
id: 'smtp_password',
id: 'smtp-password',
},
{
type: ComponentInputType.Number,
name: 'SMTP Port',
description: 'SMTP Port to send emails from',
required: true,
id: 'smtp_port',
id: 'smtp-port',
},
{
type: ComponentInputType.Boolean,
@@ -82,10 +92,15 @@ const components: Array<ComponentMetadata> = [
],
outPorts: [
{
title: 'Email Sent',
title: 'Success',
description:
'Connect to this port if you want other componets to execute after the email is sent.',
id: 'out',
'This is executed when the message is successfully posted',
id: 'success',
},
{
title: 'Error',
description: 'This is executed when there is an error',
id: 'error',
},
],
},

View File

@@ -1,4 +1,5 @@
import IconProp from '../../Icon/IconProp';
import ComponentID from '../ComponentID';
import ComponentMetadata, {
ComponentInputType,
ComponentType,
@@ -6,7 +7,7 @@ import ComponentMetadata, {
const components: Array<ComponentMetadata> = [
{
id: 'json-to-text',
id: ComponentID.JsonToText,
title: 'JSON to Text',
category: 'JSON',
description: 'Converts JSON Object to Text',
@@ -54,7 +55,7 @@ const components: Array<ComponentMetadata> = [
],
},
{
id: 'text-to-json',
id: ComponentID.TextToJson,
title: 'Text to JSON',
category: 'JSON',
description: 'Converts Text to JSON Object',
@@ -102,7 +103,7 @@ const components: Array<ComponentMetadata> = [
],
},
{
id: 'json-merge',
id: ComponentID.MergeJson,
title: 'Merge JSON',
category: 'JSON',
description: 'Merge two JSON Objects into one',
@@ -148,6 +149,12 @@ const components: Array<ComponentMetadata> = [
'This is executed when the JSON is successfully merged',
id: 'success',
},
{
title: 'Error',
description:
'This is executed when the JSON is not successfully merged',
id: 'error',
},
],
},
];

View File

@@ -23,6 +23,10 @@ export default class Queue {
queueName: QueueName,
jobId: string
): Promise<void> {
if (!jobId) {
return;
}
const job: Job | undefined = await this.getQueue(queueName).getJob(
jobId
);

View File

@@ -0,0 +1,73 @@
import BadDataException from 'Common/Types/Exception/BadDataException';
import { JSONObject } from 'Common/Types/JSON';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import ComponentID from 'Common/Types/Workflow/ComponentID';
import APIComponents from 'Common/Types/Workflow/Components/API';
import API from 'Common/Utils/API';
import ComponentCode, { RunOptions, RunReturnType } from '../../ComponentCode';
import { ApiComponentUtils } from './Utils';
import URL from 'Common/Types/API/URL';
import Dictionary from 'Common/Types/Dictionary';
import HTTPResponse from 'Common/Types/API/HTTPResponse';
import HTTPErrorResponse from 'Common/Types/API/HTTPErrorResponse';
import APIException from 'Common/Types/Exception/ApiException';
export default class ApiDelete extends ComponentCode {
public constructor() {
super();
const Component: ComponentMetadata | undefined = APIComponents.find(
(i: ComponentMetadata) => {
return i.id === ComponentID.ApiDelete;
}
);
if (!Component) {
throw new BadDataException('Component not found.');
}
this.setMetadata(Component);
}
public override async run(
args: JSONObject,
options: RunOptions
): Promise<RunReturnType> {
const result: { args: JSONObject; successPort: Port; errorPort: Port } =
ApiComponentUtils.sanitizeArgs(this.getMetadata(), args, options);
let apiResult: HTTPResponse<JSONObject> | HTTPErrorResponse | null =
null;
try {
apiResult = await API.delete(
args['url'] as URL,
args['request-body'] as JSONObject,
args['request-headers'] as Dictionary<string>
);
return Promise.resolve({
returnValues: ApiComponentUtils.getReturnValues(apiResult),
executePort: result.successPort,
});
} catch (err) {
if (err instanceof HTTPErrorResponse) {
return Promise.resolve({
returnValues: ApiComponentUtils.getReturnValues(err),
executePort: result.successPort,
});
}
if (apiResult) {
return Promise.resolve({
returnValues: ApiComponentUtils.getReturnValues(apiResult),
executePort: result.successPort,
});
}
throw options.onError(
new APIException('Something wrong happened.')
);
}
}
}

View File

@@ -0,0 +1,73 @@
import BadDataException from 'Common/Types/Exception/BadDataException';
import { JSONObject } from 'Common/Types/JSON';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import ComponentID from 'Common/Types/Workflow/ComponentID';
import APIComponents from 'Common/Types/Workflow/Components/API';
import API from 'Common/Utils/API';
import ComponentCode, { RunOptions, RunReturnType } from '../../ComponentCode';
import { ApiComponentUtils } from './Utils';
import URL from 'Common/Types/API/URL';
import Dictionary from 'Common/Types/Dictionary';
import HTTPResponse from 'Common/Types/API/HTTPResponse';
import HTTPErrorResponse from 'Common/Types/API/HTTPErrorResponse';
import APIException from 'Common/Types/Exception/ApiException';
export default class ApiGet extends ComponentCode {
public constructor() {
super();
const Component: ComponentMetadata | undefined = APIComponents.find(
(i: ComponentMetadata) => {
return i.id === ComponentID.ApiGet;
}
);
if (!Component) {
throw new BadDataException('Component not found.');
}
this.setMetadata(Component);
}
public override async run(
args: JSONObject,
options: RunOptions
): Promise<RunReturnType> {
const result: { args: JSONObject; successPort: Port; errorPort: Port } =
ApiComponentUtils.sanitizeArgs(this.getMetadata(), args, options);
let apiResult: HTTPResponse<JSONObject> | HTTPErrorResponse | null =
null;
try {
apiResult = await API.get(
args['url'] as URL,
args['request-body'] as JSONObject,
args['request-headers'] as Dictionary<string>
);
return Promise.resolve({
returnValues: ApiComponentUtils.getReturnValues(apiResult),
executePort: result.successPort,
});
} catch (err) {
if (err instanceof HTTPErrorResponse) {
return Promise.resolve({
returnValues: ApiComponentUtils.getReturnValues(err),
executePort: result.successPort,
});
}
if (apiResult) {
return Promise.resolve({
returnValues: ApiComponentUtils.getReturnValues(apiResult),
executePort: result.successPort,
});
}
throw options.onError(
new APIException('Something wrong happened.')
);
}
}
}

View File

@@ -0,0 +1,73 @@
import BadDataException from 'Common/Types/Exception/BadDataException';
import { JSONObject } from 'Common/Types/JSON';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import ComponentID from 'Common/Types/Workflow/ComponentID';
import APIComponents from 'Common/Types/Workflow/Components/API';
import API from 'Common/Utils/API';
import ComponentCode, { RunOptions, RunReturnType } from '../../ComponentCode';
import { ApiComponentUtils } from './Utils';
import URL from 'Common/Types/API/URL';
import Dictionary from 'Common/Types/Dictionary';
import HTTPResponse from 'Common/Types/API/HTTPResponse';
import HTTPErrorResponse from 'Common/Types/API/HTTPErrorResponse';
import APIException from 'Common/Types/Exception/ApiException';
export default class ApiPost extends ComponentCode {
public constructor() {
super();
const Component: ComponentMetadata | undefined = APIComponents.find(
(i: ComponentMetadata) => {
return i.id === ComponentID.ApiPost;
}
);
if (!Component) {
throw new BadDataException('Component not found.');
}
this.setMetadata(Component);
}
public override async run(
args: JSONObject,
options: RunOptions
): Promise<RunReturnType> {
const result: { args: JSONObject; successPort: Port; errorPort: Port } =
ApiComponentUtils.sanitizeArgs(this.getMetadata(), args, options);
let apiResult: HTTPResponse<JSONObject> | HTTPErrorResponse | null =
null;
try {
apiResult = await API.post(
args['url'] as URL,
args['request-body'] as JSONObject,
args['request-headers'] as Dictionary<string>
);
return Promise.resolve({
returnValues: ApiComponentUtils.getReturnValues(apiResult),
executePort: result.successPort,
});
} catch (err) {
if (err instanceof HTTPErrorResponse) {
return Promise.resolve({
returnValues: ApiComponentUtils.getReturnValues(err),
executePort: result.successPort,
});
}
if (apiResult) {
return Promise.resolve({
returnValues: ApiComponentUtils.getReturnValues(apiResult),
executePort: result.successPort,
});
}
throw options.onError(
new APIException('Something wrong happened.')
);
}
}
}

View File

@@ -0,0 +1,73 @@
import BadDataException from 'Common/Types/Exception/BadDataException';
import { JSONObject } from 'Common/Types/JSON';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import ComponentID from 'Common/Types/Workflow/ComponentID';
import APIComponents from 'Common/Types/Workflow/Components/API';
import API from 'Common/Utils/API';
import ComponentCode, { RunOptions, RunReturnType } from '../../ComponentCode';
import { ApiComponentUtils } from './Utils';
import URL from 'Common/Types/API/URL';
import Dictionary from 'Common/Types/Dictionary';
import HTTPResponse from 'Common/Types/API/HTTPResponse';
import HTTPErrorResponse from 'Common/Types/API/HTTPErrorResponse';
import APIException from 'Common/Types/Exception/ApiException';
export default class ApiPut extends ComponentCode {
public constructor() {
super();
const Component: ComponentMetadata | undefined = APIComponents.find(
(i: ComponentMetadata) => {
return i.id === ComponentID.ApiPut;
}
);
if (!Component) {
throw new BadDataException('Component not found.');
}
this.setMetadata(Component);
}
public override async run(
args: JSONObject,
options: RunOptions
): Promise<RunReturnType> {
const result: { args: JSONObject; successPort: Port; errorPort: Port } =
ApiComponentUtils.sanitizeArgs(this.getMetadata(), args, options);
let apiResult: HTTPResponse<JSONObject> | HTTPErrorResponse | null =
null;
try {
apiResult = await API.put(
args['url'] as URL,
args['request-body'] as JSONObject,
args['request-headers'] as Dictionary<string>
);
return Promise.resolve({
returnValues: ApiComponentUtils.getReturnValues(apiResult),
executePort: result.successPort,
});
} catch (err) {
if (err instanceof HTTPErrorResponse) {
return Promise.resolve({
returnValues: ApiComponentUtils.getReturnValues(err),
executePort: result.successPort,
});
}
if (apiResult) {
return Promise.resolve({
returnValues: ApiComponentUtils.getReturnValues(apiResult),
executePort: result.successPort,
});
}
throw options.onError(
new APIException('Something wrong happened.')
);
}
}
}

View File

@@ -0,0 +1,84 @@
import BadDataException from 'Common/Types/Exception/BadDataException';
import { JSONObject } from 'Common/Types/JSON';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import { RunOptions } from '../../ComponentCode';
import URL from 'Common/Types/API/URL';
import HTTPResponse from 'Common/Types/API/HTTPResponse';
import HTTPErrorResponse from 'Common/Types/API/HTTPErrorResponse';
export class ApiComponentUtils {
public static getReturnValues(
response: HTTPResponse<JSONObject> | HTTPErrorResponse
): JSONObject {
if (response instanceof HTTPErrorResponse) {
return {
'response-status': response.statusCode,
'response-body': response.jsonData,
'response-headers': response.headers,
error: response.message || 'Server Error.',
};
}
return {
'response-status': response.statusCode,
'response-body': response.jsonData,
'response-headers': response.headers,
error: null,
};
}
public static sanitizeArgs(
metadata: ComponentMetadata,
args: JSONObject,
options: RunOptions
): { args: JSONObject; successPort: Port; errorPort: Port } {
const successPort: Port | undefined = metadata.outPorts.find(
(p: Port) => {
return p.id === 'success';
}
);
if (!successPort) {
throw options.onError(
new BadDataException('Success port not found')
);
}
const errorPort: Port | undefined = metadata.outPorts.find(
(p: Port) => {
return p.id === 'error';
}
);
if (!errorPort) {
throw options.onError(new BadDataException('Error port not found'));
}
if (args['request-body'] && typeof args['request-body'] === 'string') {
args['request-body'] = JSON.parse(args['request-body'] as string);
}
if (
args['request-headers'] &&
typeof args['request-headers'] === 'string'
) {
args['request-headers'] = JSON.parse(
args['request-headers'] as string
);
}
if (!args['url']) {
throw options.onError(new BadDataException('URL not found'));
}
if (args['url'] && typeof args['url'] !== 'string') {
throw options.onError(
new BadDataException('URL is not type of string')
);
}
args['url'] = URL.fromString(args['url'] as string);
return { args, successPort, errorPort };
}
}

View File

@@ -1,8 +1,8 @@
import BaseModel from 'Common/Models/BaseModel';
import BadDataException from 'Common/Types/Exception/BadDataException';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import DatabaseService from '../../../Services/DatabaseService';
import ComponentCode, { RunOptions, RunReturnType } from '../ComponentCode';
import DatabaseService from '../../../../Services/DatabaseService';
import ComponentCode, { RunOptions, RunReturnType } from '../../ComponentCode';
import BaseModelComponents from 'Common/Types/Workflow/Components/BaseModel';
import Text from 'Common/Types/Text';
import { JSONObject } from 'Common/Types/JSON';

View File

@@ -1,8 +1,8 @@
import BaseModel from 'Common/Models/BaseModel';
import BadDataException from 'Common/Types/Exception/BadDataException';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import DatabaseService from '../../../Services/DatabaseService';
import ComponentCode, { RunOptions, RunReturnType } from '../ComponentCode';
import DatabaseService from '../../../../Services/DatabaseService';
import ComponentCode, { RunOptions, RunReturnType } from '../../ComponentCode';
import BaseModelComponents from 'Common/Types/Workflow/Components/BaseModel';
import Text from 'Common/Types/Text';
import { JSONObject } from 'Common/Types/JSON';

View File

@@ -1,12 +1,12 @@
import BaseModel from 'Common/Models/BaseModel';
import BadDataException from 'Common/Types/Exception/BadDataException';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import DatabaseService from '../../../Services/DatabaseService';
import ComponentCode, { RunOptions, RunReturnType } from '../ComponentCode';
import DatabaseService from '../../../../Services/DatabaseService';
import ComponentCode, { RunOptions, RunReturnType } from '../../ComponentCode';
import BaseModelComponents from 'Common/Types/Workflow/Components/BaseModel';
import Text from 'Common/Types/Text';
import { JSONObject } from 'Common/Types/JSON';
import Query from '../../Database/Query';
import Query from '../../../Database/Query';
import { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
import PositiveNumber from 'Common/Types/PositiveNumber';

View File

@@ -1,12 +1,12 @@
import BaseModel from 'Common/Models/BaseModel';
import BadDataException from 'Common/Types/Exception/BadDataException';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import DatabaseService from '../../../Services/DatabaseService';
import ComponentCode, { RunOptions, RunReturnType } from '../ComponentCode';
import DatabaseService from '../../../../Services/DatabaseService';
import ComponentCode, { RunOptions, RunReturnType } from '../../ComponentCode';
import BaseModelComponents from 'Common/Types/Workflow/Components/BaseModel';
import Text from 'Common/Types/Text';
import { JSONObject } from 'Common/Types/JSON';
import Query from '../../Database/Query';
import Query from '../../../Database/Query';
export default class DeleteOneBaseModel<
TBaseModel extends BaseModel

View File

@@ -1,14 +1,14 @@
import BaseModel from 'Common/Models/BaseModel';
import BadDataException from 'Common/Types/Exception/BadDataException';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import DatabaseService from '../../../Services/DatabaseService';
import ComponentCode, { RunOptions, RunReturnType } from '../ComponentCode';
import DatabaseService from '../../../../Services/DatabaseService';
import ComponentCode, { RunOptions, RunReturnType } from '../../ComponentCode';
import BaseModelComponents from 'Common/Types/Workflow/Components/BaseModel';
import Text from 'Common/Types/Text';
import { JSONObject } from 'Common/Types/JSON';
import Query from '../../Database/Query';
import Query from '../../../Database/Query';
import JSONFunctions from 'Common/Types/JSONFunctions';
import Select from '../../Database/Select';
import Select from '../../../Database/Select';
import { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
import PositiveNumber from 'Common/Types/PositiveNumber';

View File

@@ -1,14 +1,14 @@
import BaseModel from 'Common/Models/BaseModel';
import BadDataException from 'Common/Types/Exception/BadDataException';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import DatabaseService from '../../../Services/DatabaseService';
import ComponentCode, { RunOptions, RunReturnType } from '../ComponentCode';
import DatabaseService from '../../../../Services/DatabaseService';
import ComponentCode, { RunOptions, RunReturnType } from '../../ComponentCode';
import BaseModelComponents from 'Common/Types/Workflow/Components/BaseModel';
import Text from 'Common/Types/Text';
import { JSONObject } from 'Common/Types/JSON';
import Query from '../../Database/Query';
import Query from '../../../Database/Query';
import JSONFunctions from 'Common/Types/JSONFunctions';
import Select from '../../Database/Select';
import Select from '../../../Database/Select';
export default class FindOneBaseModel<
TBaseModel extends BaseModel

View File

@@ -1,5 +1,5 @@
import BaseModel from 'Common/Models/BaseModel';
import DatabaseService from '../../../Services/DatabaseService';
import DatabaseService from '../../../../Services/DatabaseService';
import OnTriggerBaseModel from './OnTriggerBaseModel';
export default class OnCreateBaseModel<

View File

@@ -1,5 +1,5 @@
import BaseModel from 'Common/Models/BaseModel';
import DatabaseService from '../../../Services/DatabaseService';
import DatabaseService from '../../../../Services/DatabaseService';
import OnTriggerBaseModel from './OnTriggerBaseModel';
export default class OnDeleteBaseModel<

View File

@@ -2,16 +2,16 @@ import BaseModel from 'Common/Models/BaseModel';
import BadDataException from 'Common/Types/Exception/BadDataException';
import ObjectID from 'Common/Types/ObjectID';
import ComponentMetadata from 'Common/Types/Workflow/Component';
import DatabaseService from '../../../Services/DatabaseService';
import { ExpressRequest, ExpressResponse } from '../../../Utils/Express';
import Response from '../../../Utils/Response';
import TriggerCode, { ExecuteWorkflowType, InitProps } from '../TriggerCode';
import DatabaseService from '../../../../Services/DatabaseService';
import { ExpressRequest, ExpressResponse } from '../../../../Utils/Express';
import Response from '../../../../Utils/Response';
import TriggerCode, { ExecuteWorkflowType, InitProps } from '../../TriggerCode';
import BaseModelComponents from 'Common/Types/Workflow/Components/BaseModel';
import Text from 'Common/Types/Text';
import WorkflowService from '../../../Services/WorkflowService';
import WorkflowService from '../../../../Services/WorkflowService';
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
import Workflow from 'Model/Models/Workflow';
import ClusterKeyAuthorization from '../../../Middleware/ClusterKeyAuthorization';
import ClusterKeyAuthorization from '../../../../Middleware/ClusterKeyAuthorization';
export default class OnTriggerBaseModel<
TBaseModel extends BaseModel

View File

@@ -1,5 +1,5 @@
import BaseModel from 'Common/Models/BaseModel';
import DatabaseService from '../../../Services/DatabaseService';
import DatabaseService from '../../../../Services/DatabaseService';
import OnTriggerBaseModel from './OnTriggerBaseModel';
export default class OnUpdateBaseModel<

View File

@@ -1,12 +1,12 @@
import BaseModel from 'Common/Models/BaseModel';
import BadDataException from 'Common/Types/Exception/BadDataException';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import DatabaseService from '../../../Services/DatabaseService';
import ComponentCode, { RunOptions, RunReturnType } from '../ComponentCode';
import DatabaseService from '../../../../Services/DatabaseService';
import ComponentCode, { RunOptions, RunReturnType } from '../../ComponentCode';
import BaseModelComponents from 'Common/Types/Workflow/Components/BaseModel';
import Text from 'Common/Types/Text';
import { JSONObject } from 'Common/Types/JSON';
import Query from '../../Database/Query';
import Query from '../../../Database/Query';
import QueryDeepPartialEntity from 'Common/Types/Database/PartialEntity';
import { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
import PositiveNumber from 'Common/Types/PositiveNumber';

View File

@@ -1,12 +1,12 @@
import BaseModel from 'Common/Models/BaseModel';
import BadDataException from 'Common/Types/Exception/BadDataException';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import DatabaseService from '../../../Services/DatabaseService';
import ComponentCode, { RunOptions, RunReturnType } from '../ComponentCode';
import DatabaseService from '../../../../Services/DatabaseService';
import ComponentCode, { RunOptions, RunReturnType } from '../../ComponentCode';
import BaseModelComponents from 'Common/Types/Workflow/Components/BaseModel';
import Text from 'Common/Types/Text';
import { JSONObject } from 'Common/Types/JSON';
import Query from '../../Database/Query';
import Query from '../../../Database/Query';
import QueryDeepPartialEntity from 'Common/Types/Database/PartialEntity';
export default class UpdateOneBaseModel<

View File

@@ -0,0 +1,98 @@
import BadDataException from 'Common/Types/Exception/BadDataException';
import { JSONObject, JSONValue } from 'Common/Types/JSON';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import ComponentID from 'Common/Types/Workflow/ComponentID';
import Components from 'Common/Types/Workflow/Components/Condition';
import ComponentCode, { RunOptions, RunReturnType } from '../../ComponentCode';
import VM, { VMScript } from 'vm2';
export default class IfElse extends ComponentCode {
public constructor() {
super();
const Component: ComponentMetadata | undefined = Components.find(
(i: ComponentMetadata) => {
return i.id === ComponentID.IfElse;
}
);
if (!Component) {
throw new BadDataException(
'Custom JavaScirpt Component not found.'
);
}
this.setMetadata(Component);
}
public override async run(
args: JSONObject,
options: RunOptions
): Promise<RunReturnType> {
const yesPort: Port | undefined = this.getMetadata().outPorts.find(
(p: Port) => {
return p.id === 'yes';
}
);
if (!yesPort) {
throw options.onError(new BadDataException('Yes port not found'));
}
const noPort: Port | undefined = this.getMetadata().outPorts.find(
(p: Port) => {
return p.id === 'no';
}
);
if (!noPort) {
throw options.onError(new BadDataException('No port not found'));
}
try {
// Set timeout
// Inject args
// Inject dependencies
const vm: VM.NodeVM = new VM.NodeVM({
timeout: 5000,
allowAsync: true,
sandbox: {
args: args['arguments'],
console: {
log: (logValue: JSONValue) => {
options.log(logValue);
},
},
},
});
const script: VMScript = new VMScript(
`module.exports = async function() { return ${
(args['expression'] as string) || ''
} }`
).compile();
const functionToRun: any = vm.run(script);
const returnVal: any = await functionToRun();
if (returnVal) {
return {
returnValues: {},
executePort: yesPort,
};
}
return {
returnValues: {},
executePort: noPort,
};
} catch (err: any) {
options.log('Error running script');
options.log(
err.message ? err.message : JSON.stringify(err, null, 2)
);
throw options.onError(err);
}
}
}

View File

@@ -0,0 +1,154 @@
import BadDataException from 'Common/Types/Exception/BadDataException';
import { JSONObject } from 'Common/Types/JSON';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import ComponentID from 'Common/Types/Workflow/ComponentID';
import Components from 'Common/Types/Workflow/Components/Email';
import ComponentCode, { RunOptions, RunReturnType } from '../ComponentCode';
import nodemailer, { Transporter } from 'nodemailer';
export default class Email extends ComponentCode {
public constructor() {
super();
const Component: ComponentMetadata | undefined = Components.find(
(i: ComponentMetadata) => {
return i.id === ComponentID.SendEmail;
}
);
if (!Component) {
throw new BadDataException('Component not found.');
}
this.setMetadata(Component);
}
public override async run(
args: JSONObject,
options: RunOptions
): Promise<RunReturnType> {
const successPort: Port | undefined = this.getMetadata().outPorts.find(
(p: Port) => {
return p.id === 'success';
}
);
if (!successPort) {
throw options.onError(
new BadDataException('Success port not found')
);
}
const errorPort: Port | undefined = this.getMetadata().outPorts.find(
(p: Port) => {
return p.id === 'error';
}
);
if (!errorPort) {
throw options.onError(new BadDataException('Error port not found'));
}
if (!args['to']) {
throw options.onError(new BadDataException('to not found'));
}
if (args['to'] && typeof args['to'] !== 'string') {
throw options.onError(
new BadDataException('to is not type of string')
);
}
if (!args['from']) {
throw options.onError(new BadDataException('from not found'));
}
if (args['from'] && typeof args['from'] !== 'string') {
throw options.onError(
new BadDataException('from is not type of string')
);
}
if (!args['smtp-username']) {
throw options.onError(new BadDataException('email not found'));
}
if (
args['smtp-username'] &&
typeof args['smtp-username'] !== 'string'
) {
throw options.onError(
new BadDataException('smtp-username is not type of string')
);
}
if (!args['smtp-password']) {
throw options.onError(new BadDataException('email not found'));
}
if (
args['smtp-password'] &&
typeof args['smtp-password'] !== 'string'
) {
throw options.onError(
new BadDataException('smtp-username is not type of string')
);
}
if (!args['smtp-host']) {
throw options.onError(new BadDataException('email not found'));
}
if (args['smtp-host'] && typeof args['smtp-host'] !== 'string') {
throw options.onError(
new BadDataException('smtp-host is not type of string')
);
}
if (!args['smtp-port']) {
throw options.onError(new BadDataException('email not found'));
}
if (args['smtp-port'] && typeof args['smtp-port'] === 'string') {
args['smtp-port'] = parseInt(args['smtp-port']);
}
if (args['smtp-port'] && typeof args['smtp-port'] !== 'number') {
throw options.onError(
new BadDataException('smtp-host is not type of number')
);
}
try {
const mailer: Transporter = nodemailer.createTransport({
host: args['smtp-host']?.toString(),
port: args['smtp-port'] as number,
secure: Boolean(args['secure']),
auth: {
user: args['smtp-username'] as string,
pass: args['smtp-password'] as string,
},
});
await mailer.sendMail({
from: args['from'].toString(),
to: args['to'].toString(),
subject: args['subject']?.toString() || '',
html: args['email-body']?.toString() || '',
});
options.log('Email sent.');
return Promise.resolve({
returnValues: {},
executePort: successPort,
});
} catch (err) {
options.log(err);
return Promise.resolve({
returnValues: {},
executePort: successPort,
});
}
}
}

View File

@@ -8,18 +8,27 @@ import JavaScirptCode from './JavaScript';
import BaseModelServices from '../../../Services/Index';
import BaseModel from 'Common/Models/BaseModel';
import Text from 'Common/Types/Text';
import OnCreateBaseModel from './OnCreateBaseModel';
import CreateOneBaseModel from './CreateOneBaseModel';
import CreateManyBaseModel from './CreateManyBaseModel';
import FindOneBaseModel from './FindOneBaseModel';
import FindManyBaseModel from './FindManyBaseModel';
import OnUpdateBaseModel from './OnUpdateBaseModel';
import UpdateOneBaseModel from './UpdateOneBaseModel';
import UpdateManyBaseModel from './UpdateManyBaseModel';
import OnDeleteBaseModel from './OnDeleteBaseModel';
import DeleteOneBaseModel from './DeleteOneBaseModel';
import DeleteManyBaseModel from './DeleteManyBaseMoidel';
import OnCreateBaseModel from './BaseModel/OnCreateBaseModel';
import CreateOneBaseModel from './BaseModel/CreateOneBaseModel';
import CreateManyBaseModel from './BaseModel/CreateManyBaseModel';
import FindOneBaseModel from './BaseModel/FindOneBaseModel';
import FindManyBaseModel from './BaseModel/FindManyBaseModel';
import OnUpdateBaseModel from './BaseModel/OnUpdateBaseModel';
import UpdateOneBaseModel from './BaseModel/UpdateOneBaseModel';
import UpdateManyBaseModel from './BaseModel/UpdateManyBaseModel';
import OnDeleteBaseModel from './BaseModel/OnDeleteBaseModel';
import DeleteOneBaseModel from './BaseModel/DeleteOneBaseModel';
import DeleteManyBaseModel from './BaseModel/DeleteManyBaseMoidel';
import ManualTrigger from './Manual';
import JsonToText from './JSON/JsonToText';
import MergeJSON from './JSON/MergeJson';
import TextToJSON from './JSON/TextToJson';
import ApiGet from './API/Get';
import ApiDelete from './API/Delete';
import ApiPost from './API/Post';
import ApiPut from './API/Put';
import Email from './Email';
import IfElse from './Conditions/IfElse';
const Components: Dictionary<ComponentCode> = {
[ComponentID.Webhook]: new WebhookTrigger(),
@@ -27,6 +36,15 @@ const Components: Dictionary<ComponentCode> = {
[ComponentID.Schedule]: new Schedule(),
[ComponentID.JavaScriptCode]: new JavaScirptCode(),
[ComponentID.Manual]: new ManualTrigger(),
[ComponentID.JsonToText]: new JsonToText(),
[ComponentID.MergeJson]: new MergeJSON(),
[ComponentID.TextToJson]: new TextToJSON(),
[ComponentID.ApiGet]: new ApiGet(),
[ComponentID.ApiPost]: new ApiPost(),
[ComponentID.ApiDelete]: new ApiDelete(),
[ComponentID.ApiPut]: new ApiPut(),
[ComponentID.SendEmail]: new Email(),
[ComponentID.IfElse]: new IfElse(),
};
for (const baseModelService of BaseModelServices) {

View File

@@ -0,0 +1,83 @@
import BadDataException from 'Common/Types/Exception/BadDataException';
import { JSONObject } from 'Common/Types/JSON';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import ComponentID from 'Common/Types/Workflow/ComponentID';
import JSONComponents from 'Common/Types/Workflow/Components/JSON';
import ComponentCode, { RunOptions, RunReturnType } from '../../ComponentCode';
export default class JsonToText extends ComponentCode {
public constructor() {
super();
const Component: ComponentMetadata | undefined = JSONComponents.find(
(i: ComponentMetadata) => {
return i.id === ComponentID.JsonToText;
}
);
if (!Component) {
throw new BadDataException('Component not found.');
}
this.setMetadata(Component);
}
public override async run(
args: JSONObject,
options: RunOptions
): Promise<RunReturnType> {
const successPort: Port | undefined = this.getMetadata().outPorts.find(
(p: Port) => {
return p.id === 'success';
}
);
if (!successPort) {
throw options.onError(
new BadDataException('Success port not found')
);
}
const errorPort: Port | undefined = this.getMetadata().outPorts.find(
(p: Port) => {
return p.id === 'error';
}
);
if (!errorPort) {
throw options.onError(new BadDataException('Error port not found'));
}
if (!args['json']) {
throw options.onError(new BadDataException('JSON is undefined.'));
}
if (typeof args['json'] === 'string') {
args['json'] = JSON.parse(args['json'] as string);
}
if (typeof args['json'] !== 'object') {
throw options.onError(
new BadDataException('JSON is should be of type object.')
);
}
try {
const returnValue: string = JSON.stringify(
args['json'] as JSONObject
);
return Promise.resolve({
returnValues: {
text: returnValue,
},
executePort: successPort,
});
} catch (err) {
options.log('JSON is not in the correct format.');
return Promise.resolve({
returnValues: {},
executePort: errorPort,
});
}
}
}

View File

@@ -0,0 +1,89 @@
import BadDataException from 'Common/Types/Exception/BadDataException';
import { JSONObject } from 'Common/Types/JSON';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import ComponentID from 'Common/Types/Workflow/ComponentID';
import JSONComponents from 'Common/Types/Workflow/Components/JSON';
import ComponentCode, { RunOptions, RunReturnType } from '../../ComponentCode';
export default class MergeJSON extends ComponentCode {
public constructor() {
super();
const Component: ComponentMetadata | undefined = JSONComponents.find(
(i: ComponentMetadata) => {
return i.id === ComponentID.MergeJson;
}
);
if (!Component) {
throw new BadDataException('Component not found.');
}
this.setMetadata(Component);
}
public override async run(
args: JSONObject,
options: RunOptions
): Promise<RunReturnType> {
const successPort: Port | undefined = this.getMetadata().outPorts.find(
(p: Port) => {
return p.id === 'success';
}
);
if (!successPort) {
throw options.onError(
new BadDataException('Success port not found')
);
}
const errorPort: Port | undefined = this.getMetadata().outPorts.find(
(p: Port) => {
return p.id === 'error';
}
);
if (!errorPort) {
throw options.onError(new BadDataException('Error port not found'));
}
if (!args['json1']) {
throw options.onError(new BadDataException('JSON1 is undefined.'));
}
if (typeof args['json1'] === 'string') {
args['json1'] = JSON.parse(args['json1'] as string);
}
if (typeof args['json1'] !== 'object') {
throw options.onError(
new BadDataException('JSON1 is should be of type object.')
);
}
if (!args['json2']) {
throw options.onError(new BadDataException('JSON2 is undefined.'));
}
if (typeof args['json2'] === 'string') {
args['json2'] = JSON.parse(args['json2'] as string);
}
if (typeof args['json2'] !== 'object') {
throw options.onError(
new BadDataException('JSON2 is should be of type object.')
);
}
return Promise.resolve({
returnValues: {
json: {
...(args['json1'] as JSONObject),
...(args['json2'] as JSONObject),
},
},
executePort: successPort,
});
}
}

View File

@@ -0,0 +1,77 @@
import BadDataException from 'Common/Types/Exception/BadDataException';
import { JSONObject } from 'Common/Types/JSON';
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
import ComponentID from 'Common/Types/Workflow/ComponentID';
import JSONComponents from 'Common/Types/Workflow/Components/JSON';
import ComponentCode, { RunOptions, RunReturnType } from '../../ComponentCode';
export default class TextToJSON extends ComponentCode {
public constructor() {
super();
const Component: ComponentMetadata | undefined = JSONComponents.find(
(i: ComponentMetadata) => {
return i.id === ComponentID.TextToJson;
}
);
if (!Component) {
throw new BadDataException('Component not found.');
}
this.setMetadata(Component);
}
public override async run(
args: JSONObject,
options: RunOptions
): Promise<RunReturnType> {
const successPort: Port | undefined = this.getMetadata().outPorts.find(
(p: Port) => {
return p.id === 'success';
}
);
if (!successPort) {
throw options.onError(
new BadDataException('Success port not found')
);
}
const errorPort: Port | undefined = this.getMetadata().outPorts.find(
(p: Port) => {
return p.id === 'error';
}
);
if (!errorPort) {
throw options.onError(new BadDataException('Error port not found'));
}
if (!args['text']) {
throw options.onError(new BadDataException('text is undefined.'));
}
if (typeof args['text'] !== 'string') {
throw options.onError(
new BadDataException('text is should be of type string.')
);
}
try {
const returnValue: JSONObject = JSON.parse(args['text'] as string);
return Promise.resolve({
returnValues: {
json: returnValue,
},
executePort: successPort,
});
} catch (err) {
options.log('text is not in the correct format.');
return Promise.resolve({
returnValues: {},
executePort: errorPort,
});
}
}
}

View File

@@ -16,6 +16,7 @@
"@types/ejs": "^3.1.1",
"@types/gridfs-stream": "^0.5.35",
"@types/json2csv": "^5.0.3",
"@types/nodemailer": "^6.4.7",
"airtable": "^0.11.3",
"axios": "^1.3.3",
"bullmq": "^3.6.6",
@@ -3872,6 +3873,14 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="
},
"node_modules/@types/nodemailer": {
"version": "6.4.7",
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.7.tgz",
"integrity": "sha512-f5qCBGAn/f0qtRcd4SEn88c8Fp3Swct1731X4ryPKqS61/A3LmmzN8zaEz7hneJvpjFbUUgY7lru/B/7ODTazg==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/pg": {
"version": "8.6.1",
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz",
@@ -13102,6 +13111,14 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="
},
"@types/nodemailer": {
"version": "6.4.7",
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.7.tgz",
"integrity": "sha512-f5qCBGAn/f0qtRcd4SEn88c8Fp3Swct1731X4ryPKqS61/A3LmmzN8zaEz7hneJvpjFbUUgY7lru/B/7ODTazg==",
"requires": {
"@types/node": "*"
}
},
"@types/pg": {
"version": "8.6.1",
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz",

View File

@@ -18,6 +18,7 @@
"@types/ejs": "^3.1.1",
"@types/gridfs-stream": "^0.5.35",
"@types/json2csv": "^5.0.3",
"@types/nodemailer": "^6.4.7",
"airtable": "^0.11.3",
"axios": "^1.3.3",
"bullmq": "^3.6.6",

View File

@@ -31,6 +31,7 @@ const CodeEditor: FunctionComponent<ComponentProps> = (
let className: string = '';
const [placeholder, setPlaceholder] = useState<string>('');
const [helpText, setHelpText] = useState<string>('');
useEffect(() => {
if (props.type === CodeType.Markdown) {
@@ -50,7 +51,7 @@ const CodeEditor: FunctionComponent<ComponentProps> = (
}
if (props.type === CodeType.JSON) {
setPlaceholder(`// ${props.placeholder}. This is in JSON.`);
setHelpText(`${props.placeholder}`);
}
if (props.type === CodeType.CSS) {
@@ -106,6 +107,13 @@ const CodeEditor: FunctionComponent<ComponentProps> = (
props.onFocus && props.onFocus();
}}
>
{helpText && (
<p className="bg-gray-50 text-gray-500 p-3 mt-2 mb-2 rounded text-base text-sm">
{' '}
{helpText}{' '}
</p>
)}
<Editor
defaultLanguage={props.type}
height="25vh"

View File

@@ -60,6 +60,13 @@ export const componentInputTypeToFormFieldType: Function = (
};
}
if (componentInputType === ComponentInputType.HTML) {
return {
fieldType: FormFieldSchemaType.HTML,
dropdownOptions: [],
};
}
if (componentInputType === ComponentInputType.CronTab) {
return {
fieldType: FormFieldSchemaType.Dropdown,

View File

@@ -387,16 +387,32 @@ export default class RunWorkflow {
argumentContent.toString().includes('{{') &&
argumentContent.toString().includes('}}')
) {
// this is dynamic content. Pick from storageMap.
argumentContent = argumentContent
.toString()
.replace('{{', '')
.replace('}}', '');
let argumentContentCopy: string = argumentContent.toString();
const variablesInArgument: Array<string> = [];
argumentContent = deepFind(
storageMap as any,
argumentContent as any
);
const regex: RegExp = /{{(.*?)}}/g; // Find all matches of the regular expression and capture the word between the braces {{x}} => x
let match: RegExpExecArray | null = null;
while ((match = regex.exec(argumentContentCopy)) !== null) {
if (match[1]) {
variablesInArgument.push(match[1]);
}
}
for (const variable of variablesInArgument) {
const value: string = deepFind(
storageMap as any,
variable as any
);
argumentContentCopy = argumentContentCopy.replace(
'{{' + variable + '}}',
value
);
}
argumentContent = argumentContentCopy;
}
argumentObj[argument.id] = argumentContent;