101 featch available variables from previous steps (#8062)

- add outputSchema in workflow step settings
- use outputSchemas to compute step available variables


https://github.com/user-attachments/assets/6b851d8e-625c-49ff-b29c-074cd86cbfee
This commit is contained in:
martmull
2024-10-28 12:25:29 +01:00
committed by GitHub
parent 3ae987be92
commit 1aa961dedf
49 changed files with 706 additions and 83 deletions

View File

@ -0,0 +1,18 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { WorkflowBuilderService } from 'src/modules/workflow/workflow-builder/workflow-builder.service';
import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless-function/serverless-function.module';
import { CodeIntrospectionModule } from 'src/modules/code-introspection/code-introspection.module';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
@Module({
imports: [
TypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'),
ServerlessFunctionModule,
CodeIntrospectionModule,
],
providers: [WorkflowBuilderService],
exports: [WorkflowBuilderService],
})
export class WorkflowBuilderModule {}

View File

@ -0,0 +1,111 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Injectable } from '@nestjs/common';
import { join } from 'path';
import { Repository } from 'typeorm';
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
import { CodeIntrospectionService } from 'src/modules/code-introspection/code-introspection.service';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import {
WorkflowTrigger,
WorkflowTriggerType,
} from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
import {
WorkflowActionType,
WorkflowStep,
} from 'src/modules/workflow/workflow-executor/types/workflow-action.type';
import { isDefined } from 'src/utils/is-defined';
import { generateFakeObjectRecordEvent } from 'src/engine/core-modules/event-emitter/utils/generate-fake-object-record-event';
import { INDEX_FILE_NAME } from 'src/engine/core-modules/serverless/drivers/constants/index-file-name';
@Injectable()
export class WorkflowBuilderService {
constructor(
private readonly serverlessFunctionService: ServerlessFunctionService,
private readonly codeIntrospectionService: CodeIntrospectionService,
@InjectRepository(ObjectMetadataEntity, 'metadata')
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
) {}
async computeStepOutputSchema({
step,
workspaceId,
}: {
step: WorkflowTrigger | WorkflowStep;
workspaceId: string;
}) {
const stepType = step.type;
switch (stepType) {
case WorkflowTriggerType.DATABASE_EVENT: {
const [nameSingular, action] = step.settings.eventName.split('.');
if (!['created', 'updated', 'deleted', 'destroyed'].includes(action)) {
return {};
}
const objectMetadata =
await this.objectMetadataRepository.findOneOrFail({
where: {
nameSingular,
workspaceId,
},
relations: ['fields'],
});
if (!isDefined(objectMetadata)) {
return {};
}
return generateFakeObjectRecordEvent(
objectMetadata,
action as 'created' | 'updated' | 'deleted' | 'destroyed',
);
}
case WorkflowActionType.SEND_EMAIL: {
return { success: true };
}
case WorkflowActionType.CODE: {
const { serverlessFunctionId, serverlessFunctionVersion } =
step.settings.input;
if (serverlessFunctionId === '') {
return {};
}
const sourceCode = (
await this.serverlessFunctionService.getServerlessFunctionSourceCode(
workspaceId,
serverlessFunctionId,
serverlessFunctionVersion,
)
)?.[join('src', INDEX_FILE_NAME)];
if (!isDefined(sourceCode)) {
return {};
}
const fakeFunctionInput =
this.codeIntrospectionService.generateInputData(sourceCode);
// handle the case when event parameter is destructured:
// (event: {param1: string; param2: number}) VS ({param1, param2}: {param1: string; param2: number})
const formattedInput = Object.values(fakeFunctionInput)[0];
const resultFromFakeInput =
await this.serverlessFunctionService.executeOneServerlessFunction(
serverlessFunctionId,
workspaceId,
formattedInput,
serverlessFunctionVersion,
);
return resultFromFakeInput.data ?? {};
}
default:
throw new Error(`Unknown type ${stepType}`);
}
}
}

View File

@ -1,6 +1,7 @@
import {
WorkflowCodeStepSettings,
WorkflowSendEmailStepSettings,
WorkflowStepSettings,
} from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type';
export enum WorkflowActionType {
@ -11,6 +12,8 @@ export enum WorkflowActionType {
type BaseWorkflowStep = {
id: string;
name: string;
type: WorkflowActionType;
settings: WorkflowStepSettings;
valid: boolean;
};

View File

@ -1,4 +1,8 @@
export type OutputSchema = object;
type BaseWorkflowStepSettings = {
input: object;
outputSchema: OutputSchema;
errorHandlingOptions: {
retryOnFailure: {
value: boolean;
@ -11,6 +15,8 @@ type BaseWorkflowStepSettings = {
export type WorkflowCodeStepInput = {
serverlessFunctionId: string;
serverlessFunctionVersion: string;
payloadInput: object;
};
export type WorkflowCodeStepSettings = BaseWorkflowStepSettings & {
@ -27,3 +33,7 @@ export type WorkflowSendEmailStepInput = {
export type WorkflowSendEmailStepSettings = BaseWorkflowStepSettings & {
input: WorkflowSendEmailStepInput;
};
export type WorkflowStepSettings =
| WorkflowSendEmailStepSettings
| WorkflowCodeStepSettings;

View File

@ -1,12 +1,19 @@
import { OutputSchema } from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type';
export enum WorkflowTriggerType {
DATABASE_EVENT = 'DATABASE_EVENT',
MANUAL = 'MANUAL',
}
type BaseWorkflowTriggerSettings = {
input?: object;
outputSchema: OutputSchema;
};
type BaseTrigger = {
name: string;
type: WorkflowTriggerType;
input?: object;
settings: BaseWorkflowTriggerSettings;
};
export type WorkflowDatabaseEventTrigger = BaseTrigger & {