mirror of
https://github.com/OneUptime/oneuptime
synced 2024-11-23 07:42:10 +00:00
Merge branch 'master' into on-call
This commit is contained in:
commit
ad451fd9c9
@ -18,6 +18,7 @@ export enum ComponentInputType {
|
||||
Email = 'Email',
|
||||
CronTab = 'CronTab',
|
||||
Query = 'Database Query',
|
||||
Select = 'Database Select',
|
||||
BaseModel = 'Database Record',
|
||||
BaseModelArray = 'Database Records',
|
||||
JSONArray = 'List of JSON',
|
||||
|
@ -33,7 +33,7 @@ export default class BaseModelComponent {
|
||||
placeholder: 'Example: {"columnName": "value", ...}',
|
||||
},
|
||||
{
|
||||
type: ComponentInputType.Query,
|
||||
type: ComponentInputType.Select,
|
||||
name: 'Select Fields',
|
||||
description: `Select on ${model.singularName}`,
|
||||
required: true,
|
||||
@ -92,7 +92,7 @@ export default class BaseModelComponent {
|
||||
placeholder: 'Example: {"columnName": "value", ...}',
|
||||
},
|
||||
{
|
||||
type: ComponentInputType.Query,
|
||||
type: ComponentInputType.Select,
|
||||
name: 'Select Fields',
|
||||
description: `Select on ${model.singularName}`,
|
||||
required: true,
|
||||
@ -288,7 +288,16 @@ export default class BaseModelComponent {
|
||||
iconProp: IconProp.Bolt,
|
||||
tableName: model.tableName!,
|
||||
componentType: ComponentType.Trigger,
|
||||
arguments: [],
|
||||
arguments: [
|
||||
{
|
||||
type: ComponentInputType.Select,
|
||||
name: 'Select Fields',
|
||||
description: `Select on ${model.singularName}`,
|
||||
required: true,
|
||||
id: 'select',
|
||||
placeholder: 'Example: {"columnName": true, ...}',
|
||||
},
|
||||
],
|
||||
returnValues: [
|
||||
{
|
||||
id: 'data',
|
||||
@ -422,7 +431,16 @@ export default class BaseModelComponent {
|
||||
iconProp: IconProp.Bolt,
|
||||
tableName: model.tableName!,
|
||||
componentType: ComponentType.Trigger,
|
||||
arguments: [],
|
||||
arguments: [
|
||||
{
|
||||
type: ComponentInputType.Select,
|
||||
name: 'Select Fields',
|
||||
description: `Select on ${model.singularName}`,
|
||||
required: true,
|
||||
id: 'select',
|
||||
placeholder: 'Example: {"columnName": true, ...}',
|
||||
},
|
||||
],
|
||||
returnValues: [
|
||||
{
|
||||
id: 'data',
|
||||
|
@ -47,7 +47,6 @@ import API from 'Common/Utils/API';
|
||||
import Protocol from 'Common/Types/API/Protocol';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import ClusterKeyAuthorization from '../Middleware/ClusterKeyAuthorization';
|
||||
import Text from 'Common/Types/Text';
|
||||
|
||||
@ -518,7 +517,7 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
}
|
||||
|
||||
public async onTrigger(
|
||||
model: TBaseModel,
|
||||
id: ObjectID,
|
||||
projectId: ObjectID,
|
||||
triggerType: DatabaseTriggerType
|
||||
): Promise<void> {
|
||||
@ -533,7 +532,9 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
)
|
||||
),
|
||||
{
|
||||
data: JSONFunctions.toJSON(model, this.entityType),
|
||||
data: {
|
||||
_id: id.toString(),
|
||||
},
|
||||
},
|
||||
{
|
||||
...ClusterKeyAuthorization.getClusterKeyHeaders(),
|
||||
@ -618,7 +619,7 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
)))
|
||||
) {
|
||||
await this.onTrigger(
|
||||
createBy.data,
|
||||
createBy.data.id!,
|
||||
createBy.props.tenantId ||
|
||||
createBy.data.getValue<ObjectID>(
|
||||
this.getModel().getTenantColumn()!
|
||||
@ -954,7 +955,7 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
deleteBy.props.tenantId
|
||||
) {
|
||||
await this.onTrigger(
|
||||
item,
|
||||
item.id!,
|
||||
deleteBy.props.tenantId ||
|
||||
item.getValue<ObjectID>(
|
||||
this.getModel().getTenantColumn()!
|
||||
@ -992,15 +993,23 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
withDeleted?: boolean | undefined
|
||||
): Promise<Array<TBaseModel>> {
|
||||
try {
|
||||
let automaticallyAddedCreatedAtInSelect: boolean = false;
|
||||
|
||||
if (!findBy.sort || Object.keys(findBy.sort).length === 0) {
|
||||
findBy.sort = {
|
||||
createdAt: SortOrder.Descending,
|
||||
};
|
||||
|
||||
if (!(findBy.select as any)['createdAt']) {
|
||||
(findBy.select as any)['createdAt'] = true;
|
||||
automaticallyAddedCreatedAtInSelect = true;
|
||||
}
|
||||
}
|
||||
|
||||
const onFind: OnFind<TBaseModel> = findBy.props.ignoreHooks
|
||||
? { findBy, carryForward: [] }
|
||||
: await this.onBeforeFind(findBy);
|
||||
const onBeforeFind: FindBy<TBaseModel> = onFind.findBy;
|
||||
const onBeforeFind: FindBy<TBaseModel> = { ...onFind.findBy };
|
||||
const carryForward: any = onFind.carryForward;
|
||||
|
||||
if (
|
||||
@ -1014,10 +1023,6 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
(onBeforeFind.select as any)['_id'] = true;
|
||||
}
|
||||
|
||||
if (!(onBeforeFind.select as any)['createdAt']) {
|
||||
(onBeforeFind.select as any)['createdAt'] = true;
|
||||
}
|
||||
|
||||
const result: {
|
||||
query: Query<TBaseModel>;
|
||||
select: Select<TBaseModel> | null;
|
||||
@ -1060,6 +1065,13 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
decryptedItems,
|
||||
onBeforeFind
|
||||
);
|
||||
|
||||
for (const item of decryptedItems) {
|
||||
if (automaticallyAddedCreatedAtInSelect) {
|
||||
delete (item as any).createdAt;
|
||||
}
|
||||
}
|
||||
|
||||
if (!findBy.props.ignoreHooks) {
|
||||
decryptedItems = await (
|
||||
await this.onFindSuccess(
|
||||
@ -1230,7 +1242,7 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
))
|
||||
) {
|
||||
await this.onTrigger(
|
||||
item,
|
||||
item.id!,
|
||||
updateBy.props.tenantId ||
|
||||
item.getValue<ObjectID>(
|
||||
this.getModel().getTenantColumn()!
|
||||
|
@ -1,7 +1,7 @@
|
||||
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 ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
|
||||
import DatabaseService from '../../../../Services/DatabaseService';
|
||||
import { ExpressRequest, ExpressResponse } from '../../../../Utils/Express';
|
||||
import Response from '../../../../Utils/Response';
|
||||
@ -12,6 +12,10 @@ 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 { JSONObject } from 'Common/Types/JSON';
|
||||
import { RunOptions, RunReturnType } from '../../ComponentCode';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import Select from '../../../Database/Select';
|
||||
|
||||
export default class OnTriggerBaseModel<
|
||||
TBaseModel extends BaseModel
|
||||
@ -19,12 +23,14 @@ export default class OnTriggerBaseModel<
|
||||
public modelId: string = '';
|
||||
public type: string = '';
|
||||
|
||||
public service: DatabaseService<TBaseModel> | null = null;
|
||||
|
||||
public constructor(
|
||||
modelService: DatabaseService<TBaseModel>,
|
||||
type: string
|
||||
) {
|
||||
super();
|
||||
|
||||
this.service = modelService;
|
||||
this.modelId = `${Text.pascalCaseToDashes(
|
||||
modelService.getModel().tableName!
|
||||
)}`;
|
||||
@ -66,6 +72,80 @@ export default class OnTriggerBaseModel<
|
||||
);
|
||||
}
|
||||
|
||||
public override async run(
|
||||
args: JSONObject,
|
||||
options: RunOptions
|
||||
): Promise<RunReturnType> {
|
||||
const data: JSONObject = args['data'] as JSONObject;
|
||||
|
||||
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')
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!data['_id'] ||
|
||||
!args['select'] ||
|
||||
Object.keys(args['select']).length === 0
|
||||
) {
|
||||
return {
|
||||
returnValues: {
|
||||
model: data
|
||||
? JSONFunctions.toJSON(
|
||||
data as any,
|
||||
this.service!.entityType
|
||||
)
|
||||
: null,
|
||||
},
|
||||
executePort: successPort,
|
||||
};
|
||||
}
|
||||
|
||||
let select: Select<TBaseModel> = args['select'] as Select<TBaseModel>;
|
||||
|
||||
if (select) {
|
||||
select = JSONFunctions.deserialize(
|
||||
args['select'] as JSONObject
|
||||
) as Select<TBaseModel>;
|
||||
}
|
||||
|
||||
const model: TBaseModel | null = await this.service!.findOneById({
|
||||
id: new ObjectID(args['_id'] as string),
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
...select,
|
||||
},
|
||||
});
|
||||
|
||||
if (!model) {
|
||||
throw new BadDataException(
|
||||
('Model not found with id ' + args['_id']) as string
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
returnValues: {
|
||||
model: data
|
||||
? JSONFunctions.toJSON(
|
||||
model as any,
|
||||
this.service!.entityType
|
||||
)
|
||||
: null,
|
||||
},
|
||||
executePort: successPort,
|
||||
};
|
||||
}
|
||||
|
||||
public async initTrigger(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
@ -95,6 +175,8 @@ export default class OnTriggerBaseModel<
|
||||
for (const workflow of workflows) {
|
||||
/// Run Graph.
|
||||
|
||||
/// Find the object and send data.
|
||||
|
||||
const executeWorkflow: ExecuteWorkflowType = {
|
||||
workflowId: workflow.id!,
|
||||
returnValues: {
|
||||
|
@ -4,7 +4,9 @@
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import { ExpressRouter } from '../../Utils/Express';
|
||||
import ComponentCode from './ComponentCode';
|
||||
import ComponentCode, { RunOptions, RunReturnType } from './ComponentCode';
|
||||
import { Port } from 'Common/Types/Workflow/Component';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
|
||||
export interface ExecuteWorkflowType {
|
||||
workflowId: ObjectID;
|
||||
@ -44,6 +46,30 @@ export default class TrigegrCode extends ComponentCode {
|
||||
super();
|
||||
}
|
||||
|
||||
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')
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
returnValues: {
|
||||
...args,
|
||||
},
|
||||
executePort: successPort,
|
||||
};
|
||||
}
|
||||
|
||||
public async setupComponent(props: InitProps): Promise<void> {
|
||||
this.executeWorkflow = props.executeWorkflow;
|
||||
this.scheduleWorkflow = props.scheduleWorkflow;
|
||||
|
@ -74,6 +74,8 @@ const RunModal: FunctionComponent<ComponentProps> = (
|
||||
ComponentInputType.BaseModelArray ||
|
||||
args.type ===
|
||||
ComponentInputType.Query ||
|
||||
args.type ===
|
||||
ComponentInputType.Select ||
|
||||
args.type ===
|
||||
ComponentInputType.StringDictionary) &&
|
||||
component.returnValues[args.id] &&
|
||||
|
@ -80,6 +80,12 @@ export const componentInputTypeToFormFieldType: Function = (
|
||||
};
|
||||
}
|
||||
|
||||
if (componentInputType === ComponentInputType.Select) {
|
||||
return {
|
||||
fieldType: FormFieldSchemaType.JSON,
|
||||
};
|
||||
}
|
||||
|
||||
if (componentInputType === ComponentInputType.StringDictionary) {
|
||||
return {
|
||||
fieldType: FormFieldSchemaType.JSON,
|
||||
|
@ -196,99 +196,70 @@ export default class RunWorkflow {
|
||||
);
|
||||
}
|
||||
|
||||
// execute this stack.
|
||||
// now actually run this component.
|
||||
|
||||
let args: JSONObject = this.getComponentArguments(
|
||||
storageMap,
|
||||
stackItem.node
|
||||
);
|
||||
|
||||
if (stackItem.node.componentType === ComponentType.Trigger) {
|
||||
// this is already executed. So, place its arguments inside of storage map.
|
||||
storageMap.local.components[stackItem.node.id] = {
|
||||
returnValues: runProps.arguments,
|
||||
// If this is the trigger. Then pass workflow argument to this component as args to execute.
|
||||
args = {
|
||||
...args,
|
||||
...runProps.arguments,
|
||||
};
|
||||
}
|
||||
|
||||
this.log('Trigger args:');
|
||||
this.log(runProps.arguments);
|
||||
this.log('Component Args:');
|
||||
this.log(args);
|
||||
this.log('Component Logs: ' + executeComponentId);
|
||||
|
||||
// need port to be executed.
|
||||
const nodesToBeExecuted: Array<string> | undefined =
|
||||
Object.keys(stackItem.outPorts)
|
||||
.map((outport: string) => {
|
||||
return stackItem.outPorts[outport] || [];
|
||||
})
|
||||
.flat();
|
||||
const result: RunReturnType = await this.runComponent(
|
||||
args,
|
||||
stackItem.node,
|
||||
setDidErrorOut
|
||||
);
|
||||
|
||||
if (nodesToBeExecuted && nodesToBeExecuted.length > 0) {
|
||||
nodesToBeExecuted.forEach((item: string) => {
|
||||
// if its not in the stack, then add it to execution stack.
|
||||
if (
|
||||
!fifoStackOfComponentsPendingExecution.includes(
|
||||
item
|
||||
)
|
||||
) {
|
||||
fifoStackOfComponentsPendingExecution.push(
|
||||
item
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// now actually run this component.
|
||||
|
||||
const args: JSONObject = this.getComponentArguments(
|
||||
storageMap,
|
||||
stackItem.node
|
||||
if (didWorkflowErrorOut) {
|
||||
throw new BadDataException(
|
||||
'Workflow stopped because of an error'
|
||||
);
|
||||
}
|
||||
|
||||
this.log('Component Args:');
|
||||
this.log(args);
|
||||
this.log('Component Logs: ' + executeComponentId);
|
||||
const result: RunReturnType = await this.runComponent(
|
||||
args,
|
||||
stackItem.node,
|
||||
setDidErrorOut
|
||||
);
|
||||
this.log(
|
||||
'Completed Execution Component: ' + executeComponentId
|
||||
);
|
||||
this.log('Data Returned');
|
||||
this.log(result.returnValues);
|
||||
this.log(
|
||||
'Executing Port: ' + result.executePort?.title || '<None>'
|
||||
);
|
||||
|
||||
if (didWorkflowErrorOut) {
|
||||
throw new BadDataException(
|
||||
'Workflow stopped because of an error'
|
||||
);
|
||||
}
|
||||
storageMap.local.components[stackItem.node.id] = {
|
||||
returnValues: result.returnValues,
|
||||
};
|
||||
|
||||
this.log(
|
||||
'Completed Execution Component: ' + executeComponentId
|
||||
);
|
||||
this.log('Data Returned');
|
||||
this.log(result.returnValues);
|
||||
this.log(
|
||||
'Executing Port: ' + result.executePort?.title ||
|
||||
'<None>'
|
||||
);
|
||||
const portToBeExecuted: Port | undefined = result.executePort;
|
||||
|
||||
storageMap.local.components[stackItem.node.id] = {
|
||||
returnValues: result.returnValues,
|
||||
};
|
||||
if (!portToBeExecuted) {
|
||||
break; // stop the workflow, the process has ended.
|
||||
}
|
||||
|
||||
const portToBeExecuted: Port | undefined =
|
||||
result.executePort;
|
||||
const nodesToBeExecuted: Array<string> | undefined =
|
||||
stackItem.outPorts[portToBeExecuted.id];
|
||||
|
||||
if (!portToBeExecuted) {
|
||||
break; // stop the workflow, the process has ended.
|
||||
}
|
||||
|
||||
const nodesToBeExecuted: Array<string> | undefined =
|
||||
stackItem.outPorts[portToBeExecuted.id];
|
||||
|
||||
if (nodesToBeExecuted && nodesToBeExecuted.length > 0) {
|
||||
nodesToBeExecuted.forEach((item: string) => {
|
||||
// if its not in the stack, then add it to execution stack.
|
||||
if (
|
||||
!fifoStackOfComponentsPendingExecution.includes(
|
||||
item
|
||||
)
|
||||
) {
|
||||
fifoStackOfComponentsPendingExecution.push(
|
||||
item
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (nodesToBeExecuted && nodesToBeExecuted.length > 0) {
|
||||
nodesToBeExecuted.forEach((item: string) => {
|
||||
// if its not in the stack, then add it to execution stack.
|
||||
if (
|
||||
!fifoStackOfComponentsPendingExecution.includes(
|
||||
item
|
||||
)
|
||||
) {
|
||||
fifoStackOfComponentsPendingExecution.push(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,7 +279,7 @@ export default class RunWorkflow {
|
||||
});
|
||||
} catch (err: any) {
|
||||
logger.error(err);
|
||||
this.log(err.toString());
|
||||
this.log(err.message || err.toString());
|
||||
|
||||
if (!runProps.workflowLogId) {
|
||||
return;
|
||||
@ -473,6 +444,26 @@ export default class RunWorkflow {
|
||||
argumentContent = argumentContentCopy;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof argumentContent === 'string' &&
|
||||
(argument.type === ComponentInputType.JSON ||
|
||||
argument.type === ComponentInputType.Query ||
|
||||
argument.type === ComponentInputType.Select)
|
||||
) {
|
||||
try {
|
||||
argumentContent = JSON.parse(argumentContent);
|
||||
} catch (err: any) {
|
||||
throw new BadDataException(
|
||||
'Invalid JSON provided for argument ' +
|
||||
argument.id +
|
||||
'. JSON parse error: ' +
|
||||
err.message +
|
||||
'. JSON: ' +
|
||||
argumentContent
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
argumentObj[argument.id] = argumentContent;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user