Skip to content

Commit

Permalink
Create record action (#8460)
Browse files Browse the repository at this point in the history
- Add create record action
- Migrate all step name => action in a common folder
- Separate types
  • Loading branch information
thomtrp authored Nov 13, 2024
1 parent ba79a1d commit faeea2b
Show file tree
Hide file tree
Showing 27 changed files with 268 additions and 128 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import { Field, InputType } from '@nestjs/graphql';

import graphqlTypeJson from 'graphql-type-json';

import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
import { WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
import { WorkflowStep } from 'src/modules/workflow/workflow-executor/types/workflow-action.type';

@InputType()
export class ComputeStepOutputSchemaInput {
@Field(() => graphqlTypeJson, {
description: 'Step JSON format',
nullable: false,
})
step: WorkflowTrigger | WorkflowStep;
step: WorkflowTrigger | WorkflowAction;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorat
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { WorkflowBuilderWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-builder.workspace-service';
import { OutputSchema } from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type';
import { OutputSchema } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';

@Resolver()
@UseGuards(WorkspaceAuthGuard, UserAuthGuard)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { FavoriteWorkspaceEntity } from 'src/modules/favorite/standard-objects/f
import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity';
import { WorkflowRunWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity';
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity';
import { WorkflowStep } from 'src/modules/workflow/workflow-executor/types/workflow-action.type';
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
import { WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';

export enum WorkflowVersionStatus {
Expand Down Expand Up @@ -98,7 +98,7 @@ export class WorkflowVersionWorkspaceEntity extends BaseWorkspaceEntity {
icon: 'IconSettingsAutomation',
})
@WorkspaceIsNullable()
steps: WorkflowStep[] | null;
steps: WorkflowAction[] | null;

@WorkspaceField({
standardId: WORKFLOW_VERSION_STANDARD_FIELD_IDS.status,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,24 @@ import { join } from 'path';

import { Repository } from 'typeorm';

import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
import { checkStringIsDatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/utils/check-string-is-database-event-action';
import { INDEX_FILE_NAME } from 'src/engine/core-modules/serverless/drivers/constants/index-file-name';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
import { CodeIntrospectionService } from 'src/modules/code-introspection/code-introspection.service';
import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/utils/generate-fake-object-record';
import { generateFakeObjectRecordEvent } from 'src/modules/workflow/workflow-builder/utils/generate-fake-object-record-event';
import { WorkflowSendEmailStepOutputSchema } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/send-email.workflow-action';
import {
WorkflowAction,
WorkflowActionType,
WorkflowStep,
} from 'src/modules/workflow/workflow-executor/types/workflow-action.type';
import { WorkflowSendEmailStepOutputSchema } from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type';
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
import {
WorkflowTrigger,
WorkflowTriggerType,
} from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
import { isDefined } from 'src/utils/is-defined';
import { checkStringIsDatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/utils/check-string-is-database-event-action';
import { generateFakeObjectRecordEvent } from 'src/modules/workflow/workflow-builder/utils/generate-fake-object-record-event';
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';

@Injectable()
export class WorkflowBuilderWorkspaceService {
Expand All @@ -37,7 +37,7 @@ export class WorkflowBuilderWorkspaceService {
step,
workspaceId,
}: {
step: WorkflowTrigger | WorkflowStep;
step: WorkflowTrigger | WorkflowAction;
workspaceId: string;
}): Promise<object> {
const stepType = step.type;
Expand All @@ -57,7 +57,7 @@ export class WorkflowBuilderWorkspaceService {
return {};
}

return await this.computeManualTriggerOutputSchema({
return await this.computeRecordOutputSchema({
objectType,
workspaceId,
objectMetadataRepository: this.objectMetadataRepository,
Expand All @@ -78,6 +78,12 @@ export class WorkflowBuilderWorkspaceService {
codeIntrospectionService: this.codeIntrospectionService,
});
}
case WorkflowActionType.RECORD_CRUD:
return await this.computeRecordOutputSchema({
objectType: step.settings.input.objectName,
workspaceId,
objectMetadataRepository: this.objectMetadataRepository,
});
default:
return {};
}
Expand Down Expand Up @@ -116,7 +122,7 @@ export class WorkflowBuilderWorkspaceService {
);
}

private async computeManualTriggerOutputSchema<Entity>({
private async computeRecordOutputSchema<Entity>({
objectType,
workspaceId,
objectMetadataRepository,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ import { Injectable } from '@nestjs/common';

import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';

import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/types/workflow-action.type';
import {
WorkflowStepExecutorException,
WorkflowStepExecutorExceptionCode,
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
import { CodeWorkflowAction } from 'src/modules/serverless/workflow-actions/code.workflow-action';
import { SendEmailWorkflowAction } from 'src/modules/mail-sender/workflow-actions/send-email.workflow-action';
import { CodeWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/code/code.workflow-action';
import { SendEmailWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/send-email.workflow-action';
import { RecordCRUDWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/record-crud.workflow-action';
import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';

@Injectable()
export class WorkflowActionFactory {
constructor(
private readonly codeWorkflowAction: CodeWorkflowAction,
private readonly sendEmailWorkflowAction: SendEmailWorkflowAction,
private readonly recordCRUDWorkflowAction: RecordCRUDWorkflowAction,
) {}

get(stepType: WorkflowActionType): WorkflowAction {
Expand All @@ -23,6 +25,8 @@ export class WorkflowActionFactory {
return this.codeWorkflowAction;
case WorkflowActionType.SEND_EMAIL:
return this.sendEmailWorkflowAction;
case WorkflowActionType.RECORD_CRUD:
return this.recordCRUDWorkflowAction;
default:
throw new WorkflowStepExecutorException(
`Workflow step executor not found for step type '${stepType}'`,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/types/workflow-action-result.type';
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-result.type';

export interface WorkflowAction {
execute(workflowStepInput: unknown): Promise<WorkflowActionResult>;
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';

import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless-function/serverless-function.module';
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
import { CodeWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/code/code.workflow-action';

@Module({
imports: [ServerlessFunctionModule],
providers: [ScopedWorkspaceContextFactory, CodeWorkflowAction],
exports: [CodeWorkflowAction],
})
export class CodeActionModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {
WorkflowStepExecutorException,
WorkflowStepExecutorExceptionCode,
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/types/workflow-action-result.type';
import { WorkflowCodeStepInput } from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type';
import { WorkflowCodeActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/code/types/workflow-code-action-input.type';
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-result.type';

@Injectable()
export class CodeWorkflowAction implements WorkflowAction {
Expand All @@ -19,7 +19,7 @@ export class CodeWorkflowAction implements WorkflowAction {
) {}

async execute(
workflowStepInput: WorkflowCodeStepInput,
workflowActionInput: WorkflowCodeActionInput,
): Promise<WorkflowActionResult> {
try {
const { workspaceId } = this.scopedWorkspaceContextFactory.create();
Expand All @@ -33,9 +33,9 @@ export class CodeWorkflowAction implements WorkflowAction {

const result =
await this.serverlessFunctionService.executeOneServerlessFunction(
workflowStepInput.serverlessFunctionId,
workflowActionInput.serverlessFunctionId,
workspaceId,
workflowStepInput.serverlessFunctionInput,
workflowActionInput.serverlessFunctionInput,
);

if (result.error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export type WorkflowCodeActionInput = {
serverlessFunctionId: string;
serverlessFunctionVersion: string;
serverlessFunctionInput: {
[key: string]: any;
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { WorkflowCodeActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/code/types/workflow-code-action-input.type';
import { BaseWorkflowActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';

export type WorkflowCodeActionSettings = BaseWorkflowActionSettings & {
input: WorkflowCodeActionInput;
};
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { CustomException } from 'src/utils/custom-exception';

export class MailSenderException extends CustomException {
code: MailSenderExceptionCode;
constructor(message: string, code: MailSenderExceptionCode) {
export class SendEmailActionException extends CustomException {
code: SendEmailActionExceptionCode;
constructor(message: string, code: SendEmailActionExceptionCode) {
super(message, code);
}
}

export enum MailSenderExceptionCode {
export enum SendEmailActionExceptionCode {
PROVIDER_NOT_SUPPORTED = 'PROVIDER_NOT_SUPPORTED',
CONNECTED_ACCOUNT_NOT_FOUND = 'CONNECTED_ACCOUNT_NOT_FOUND',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Module } from '@nestjs/common';

import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
import { OAuth2ClientManagerModule } from 'src/modules/connected-account/oauth2-client-manager/oauth2-client-manager.module';
import { GmailClientProvider } from 'src/modules/messaging/message-import-manager/drivers/gmail/providers/gmail-client.provider';
import { SendEmailWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/send-email.workflow-action';

@Module({
imports: [OAuth2ClientManagerModule],
providers: [
GmailClientProvider,
ScopedWorkspaceContextFactory,
SendEmailWorkflowAction,
],
exports: [SendEmailWorkflowAction],
})
export class SendEmailActionModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,23 @@ import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interface
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import {
MailSenderException,
MailSenderExceptionCode,
} from 'src/modules/mail-sender/exceptions/mail-sender.exception';
import { GmailClientProvider } from 'src/modules/messaging/message-import-manager/drivers/gmail/providers/gmail-client.provider';
import {
WorkflowStepExecutorException,
WorkflowStepExecutorExceptionCode,
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/types/workflow-action-result.type';
import {
WorkflowSendEmailStepInput,
WorkflowSendEmailStepOutputSchema,
} from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type';
SendEmailActionException,
SendEmailActionExceptionCode,
} from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/exceptions/send-email-action.exception';
import { WorkflowSendEmailActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/types/workflow-send-email-action-input.type';
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-result.type';
import { isDefined } from 'src/utils/is-defined';

export type WorkflowSendEmailStepOutputSchema = {
success: boolean;
};

@Injectable()
export class SendEmailWorkflowAction implements WorkflowAction {
private readonly logger = new Logger(SendEmailWorkflowAction.name);
Expand Down Expand Up @@ -54,30 +55,30 @@ export class SendEmailWorkflowAction implements WorkflowAction {
});

if (!isDefined(connectedAccount)) {
throw new MailSenderException(
throw new SendEmailActionException(
`Connected Account '${connectedAccountId}' not found`,
MailSenderExceptionCode.CONNECTED_ACCOUNT_NOT_FOUND,
SendEmailActionExceptionCode.CONNECTED_ACCOUNT_NOT_FOUND,
);
}

switch (connectedAccount.provider) {
case 'google':
return await this.gmailClientProvider.getGmailClient(connectedAccount);
default:
throw new MailSenderException(
throw new SendEmailActionException(
`Provider ${connectedAccount.provider} is not supported`,
MailSenderExceptionCode.PROVIDER_NOT_SUPPORTED,
SendEmailActionExceptionCode.PROVIDER_NOT_SUPPORTED,
);
}
}

async execute(
workflowStepInput: WorkflowSendEmailStepInput,
workflowActionInput: WorkflowSendEmailActionInput,
): Promise<WorkflowActionResult> {
const emailProvider = await this.getEmailClient(
workflowStepInput.connectedAccountId,
workflowActionInput.connectedAccountId,
);
const { email, body, subject } = workflowStepInput;
const { email, body, subject } = workflowActionInput;

try {
const emailSchema = z.string().trim().email('Invalid email');
Expand Down
Loading

0 comments on commit faeea2b

Please sign in to comment.