manual and workflow commit.

This commit is contained in:
Simon Larsen
2023-02-28 14:09:44 +00:00
parent 1a8044ddbd
commit 08e9477a9c
10 changed files with 191 additions and 1 deletions

View File

@@ -3,6 +3,7 @@ enum ComponentID {
Log = 'log',
Schedule = 'schedule',
JavaScriptCode = 'javascript',
Manual = 'manual'
}
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: 'manual',
id: ComponentID.Manual,
title: 'Manual',
category: 'Utils',
description: 'Run this workflow manually',

View File

@@ -8,6 +8,13 @@ import {
NodeDataProp,
NodeType,
} from 'Common/Types/Workflow/Component';
import API from 'Common/Utils/API';
import EmptyResponseData from 'Common/Types/API/EmptyResponse';
import URL from 'Common/Types/API/URL';
import Protocol from 'Common/Types/API/Protocol';
import { WorkflowHostname } from '../Config';
import Route from 'Common/Types/API/Route';
import ClusterKeyAuthorization from '../Middleware/ClusterKeyAuthorization';
export class Service extends DatabaseService<Model> {
public constructor(postgresDatabase?: PostgresDatabase) {
@@ -56,6 +63,14 @@ export class Service extends DatabaseService<Model> {
},
});
await API.post<EmptyResponseData>(
new URL(Protocol.HTTP, WorkflowHostname, new Route('/workflow/update/'+onUpdate.updateBy.query._id!)),
{},
{
...ClusterKeyAuthorization.getClusterKeyHeaders(),
}
);
return onUpdate;
}
}

View File

@@ -42,9 +42,20 @@ export interface InitProps {
) => Promise<void>;
}
export interface UpdateProps {
workflowId: ObjectID;
}
export default class ComponentCode {
private metadata: ComponentMetadata | null = null;
public executeWorkflow: ((executeWorkflow: ExecuteWorkflowType) => Promise<void>) | null = null;
public scheduleWorkflow: ((
executeWorkflow: ExecuteWorkflowType,
scheduleAt: string
) => Promise<void>) | null = null;
public constructor() {}
public setMetadata(metadata: ComponentMetadata): void {
@@ -59,10 +70,22 @@ export default class ComponentCode {
return this.metadata;
}
public async setupComponent(props: InitProps): Promise<void> {
this.executeWorkflow = props.executeWorkflow;
this.scheduleWorkflow = props.scheduleWorkflow;
return await this.init(props);
}
public async init(_props: InitProps): Promise<void> {
return await Promise.resolve();
}
public async update(_props: UpdateProps): Promise<void> {
return await Promise.resolve();
}
public async run(
_args: JSONObject,
_options: RunOptions

View File

@@ -19,12 +19,14 @@ import UpdateManyBaseModel from './UpdateManyBaseModel';
import OnDeleteBaseModel from './OnDeleteBaseModel';
import DeleteOneBaseModel from './DeleteOneBaseModel';
import DeleteManyBaseModel from './DeleteManyBaseMoidel';
import ManualTrigger from './Manual';
const Components: Dictionary<ComponentCode> = {
[ComponentID.Webhook]: new WebhookTrigger(),
[ComponentID.Log]: new Log(),
[ComponentID.Schedule]: new Schedule(),
[ComponentID.JavaScriptCode]: new JavaScirptCode(),
[ComponentID.Manual]: new ManualTrigger()
};
for (const baseModelService of BaseModelServices) {

View File

@@ -0,0 +1,26 @@
import BadDataException from 'Common/Types/Exception/BadDataException';
import ComponentMetadata from 'Common/Types/Workflow/Component';
import ComponentID from 'Common/Types/Workflow/ComponentID';
import ManualComponents from 'Common/Types/Workflow/Components/Manual';
import ComponentCode, {
InitProps,
} from '../ComponentCode';
export default class ManualTrigger extends ComponentCode {
public constructor() {
super();
const Component: ComponentMetadata | undefined =
ManualComponents.find((i: ComponentMetadata) => {
return i.id === ComponentID.Manual;
});
if (!Component) {
throw new BadDataException('Component not found.');
}
this.setMetadata(Component);
}
public override async init(_props: InitProps): Promise<void> {
// do nothing because its a manual component.
}
}

View File

@@ -10,6 +10,7 @@ import QueryHelper from '../../Database/QueryHelper';
import ComponentCode, {
ExecuteWorkflowType,
InitProps,
UpdateProps,
} from '../ComponentCode';
export default class WebhookTrigger extends ComponentCode {
@@ -61,4 +62,44 @@ export default class WebhookTrigger extends ComponentCode {
}
}
}
public override async update(props: UpdateProps): Promise<void> {
const workflow: Workflow | null = await WorkflowService.findOneBy({
query: {
triggerId: ComponentID.Schedule,
_id: props.workflowId.toString(),
triggerArguments: QueryHelper.notNull(),
},
select: {
_id: true,
triggerArguments: true,
},
props: {
isRoot: true,
}
});
if(!workflow){
return;
}
if(!this.scheduleWorkflow){
return;
}
const executeWorkflow: ExecuteWorkflowType = {
workflowId: new ObjectID(workflow._id!),
returnValues: {},
};
if (
workflow.triggerArguments &&
workflow.triggerArguments['schedule']
) {
await this.scheduleWorkflow(
executeWorkflow,
workflow.triggerArguments['schedule'] as string
);
}
}
}

View File

@@ -64,6 +64,10 @@ export const componentInputTypeToFormFieldType: Function = (
return {
fieldType: FormFieldSchemaType.Dropdown,
dropdownOptions: [
{
label: 'Every Minute',
value: '* * * * *',
},
{
label: 'Every 30 minutes',
value: '*/30 * * * *',

74
Workflow/API/Workflow.ts Normal file
View File

@@ -0,0 +1,74 @@
import Express, {
ExpressRequest,
ExpressResponse,
ExpressRouter,
} from 'CommonServer/Utils/Express';
import Response from 'CommonServer/Utils/Response';
import ObjectID from 'Common/Types/ObjectID';
import BadDataException from 'Common/Types/Exception/BadDataException';
import WorkflowService from 'CommonServer/Services/WorkflowService';
import Workflow from 'Model/Models/Workflow';
import ClusterKeyAuthorization from 'CommonServer/Middleware/ClusterKeyAuthorization';
import ComponentCode from 'CommonServer/Types/Workflow/ComponentCode';
import Components from 'CommonServer/Types/Workflow/Components/Index';
export default class WorkflowAPI {
public router!: ExpressRouter;
public constructor() {
this.router = Express.getRouter();
this.router.get(`/update/:workflowId`, ClusterKeyAuthorization.isAuthorizedServiceMiddleware, this.updateWorkflow);
this.router.post(`/update/:workflowId`, ClusterKeyAuthorization.isAuthorizedServiceMiddleware, this.updateWorkflow);
}
public async updateWorkflow(
req: ExpressRequest,
res: ExpressResponse
): Promise<void> {
// add this workflow to the run queue and return the 200 response.
if (!req.params['workflowId']) {
return Response.sendErrorResponse(
req,
res,
new BadDataException('workflowId not found in URL')
);
}
const workflow: Workflow | null = await WorkflowService.findOneById({
id: new ObjectID(req.params['workflowId']),
select: {
_id: true,
triggerId: true,
},
props: {
isRoot: true,
}
});
if(!workflow){
return Response.sendJsonObjectResponse(req, res, {
status: 'Workflow not found',
});
}
const componentCode: ComponentCode | undefined = Components[workflow.triggerId as string];
if(!componentCode){
return Response.sendJsonObjectResponse(req, res, {
status: 'Component not found',
});
}
await componentCode.update({
workflowId: workflow.id!
});
return Response.sendJsonObjectResponse(req, res, {
status: 'Updated',
});
}
}

View File

@@ -14,6 +14,7 @@ import QueueWorker from 'CommonServer/Infrastructure/QueueWorker';
import RunWorkflow from './Services/RunWorkflow';
import { JSONObject } from 'Common/Types/JSON';
import ObjectID from 'Common/Types/ObjectID';
import WorkflowAPI from './API/Workflow';
const APP_NAME: string = 'workflow';
@@ -21,6 +22,8 @@ const app: ExpressApplication = Express.getExpressApp();
app.use(`/${APP_NAME}/manual`, new ManualAPI().router);
app.use(`/${APP_NAME}`, new WorkflowAPI().router);
app.get(
`/${APP_NAME}/docs/:componentName`,
(req: ExpressRequest, res: ExpressResponse) => {