Split record crud actions (#8930)

Having a global record crud action adds complex logic.
We decided to split those actions. I only kept a common folder / module
in backend.

⚠️ this may break existing workflows if these were using previous
actions!
This commit is contained in:
Thomas Trompette
2024-12-06 16:58:57 +01:00
committed by GitHub
parent 229a93e41a
commit e1a0259154
26 changed files with 341 additions and 590 deletions
@@ -8,7 +8,10 @@ import {
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
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 { CreateRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action';
import { DeleteRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/delete-record.workflow-action';
import { FindRecordsWorflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action';
import { UpdateRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/update-record.workflow-action';
import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
@Injectable()
@@ -16,7 +19,10 @@ export class WorkflowActionFactory {
constructor(
private readonly codeWorkflowAction: CodeWorkflowAction,
private readonly sendEmailWorkflowAction: SendEmailWorkflowAction,
private readonly recordCRUDWorkflowAction: RecordCRUDWorkflowAction,
private readonly createRecordWorkflowAction: CreateRecordWorkflowAction,
private readonly updateRecordWorkflowAction: UpdateRecordWorkflowAction,
private readonly deleteRecordWorkflowAction: DeleteRecordWorkflowAction,
private readonly findRecordsWorflowAction: FindRecordsWorflowAction,
) {}
get(stepType: WorkflowActionType): WorkflowAction {
@@ -25,8 +31,14 @@ export class WorkflowActionFactory {
return this.codeWorkflowAction;
case WorkflowActionType.SEND_EMAIL:
return this.sendEmailWorkflowAction;
case WorkflowActionType.RECORD_CRUD:
return this.recordCRUDWorkflowAction;
case WorkflowActionType.CREATE_RECORD:
return this.createRecordWorkflowAction;
case WorkflowActionType.UPDATE_RECORD:
return this.updateRecordWorkflowAction;
case WorkflowActionType.DELETE_RECORD:
return this.deleteRecordWorkflowAction;
case WorkflowActionType.FIND_RECORDS:
return this.findRecordsWorflowAction;
default:
throw new WorkflowStepExecutorException(
`Workflow step executor not found for step type '${stepType}'`,
@@ -0,0 +1,30 @@
import { Injectable } from '@nestjs/common';
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
import { WorkflowCreateRecordActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-result.type';
@Injectable()
export class CreateRecordWorkflowAction implements WorkflowAction {
constructor(private readonly twentyORMManager: TwentyORMManager) {}
async execute(
workflowActionInput: WorkflowCreateRecordActionInput,
): Promise<WorkflowActionResult> {
const repository = await this.twentyORMManager.getRepository(
workflowActionInput.objectName,
);
const objectRecord = await repository.create(
workflowActionInput.objectRecord,
);
await repository.save(objectRecord);
return {
result: objectRecord,
};
}
}
@@ -0,0 +1,45 @@
import { Injectable } from '@nestjs/common';
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
import {
RecordCRUDActionException,
RecordCRUDActionExceptionCode,
} from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/exceptions/record-crud-action.exception';
import { WorkflowDeleteRecordActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-result.type';
@Injectable()
export class DeleteRecordWorkflowAction implements WorkflowAction {
constructor(private readonly twentyORMManager: TwentyORMManager) {}
async execute(
workflowActionInput: WorkflowDeleteRecordActionInput,
): Promise<WorkflowActionResult> {
const repository = await this.twentyORMManager.getRepository(
workflowActionInput.objectName,
);
const objectRecord = await repository.findOne({
where: {
id: workflowActionInput.objectRecordId,
},
});
if (!objectRecord) {
throw new RecordCRUDActionException(
`Failed to delete: Record ${workflowActionInput.objectName} with id ${workflowActionInput.objectRecordId} not found`,
RecordCRUDActionExceptionCode.RECORD_NOT_FOUND,
);
}
await repository.update(workflowActionInput.objectRecordId, {
deletedAt: new Date(),
});
return {
result: objectRecord,
};
}
}
@@ -24,18 +24,11 @@ import {
RecordCRUDActionException,
RecordCRUDActionExceptionCode,
} from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/exceptions/record-crud-action.exception';
import {
WorkflowCreateRecordActionInput,
WorkflowDeleteRecordActionInput,
WorkflowReadRecordActionInput,
WorkflowRecordCRUDActionInput,
WorkflowRecordCRUDType,
WorkflowUpdateRecordActionInput,
} from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
import { WorkflowFindRecordsActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-result.type';
@Injectable()
export class RecordCRUDWorkflowAction implements WorkflowAction {
export class FindRecordsWorflowAction implements WorkflowAction {
constructor(
private readonly twentyORMManager: TwentyORMManager,
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
@@ -43,106 +36,7 @@ export class RecordCRUDWorkflowAction implements WorkflowAction {
) {}
async execute(
workflowActionInput: WorkflowRecordCRUDActionInput,
): Promise<WorkflowActionResult> {
switch (workflowActionInput.type) {
case WorkflowRecordCRUDType.CREATE:
return this.createRecord(workflowActionInput);
case WorkflowRecordCRUDType.DELETE:
return this.deleteRecord(workflowActionInput);
case WorkflowRecordCRUDType.UPDATE:
return this.updateRecord(workflowActionInput);
case WorkflowRecordCRUDType.READ:
return this.findRecords(workflowActionInput);
default:
throw new RecordCRUDActionException(
`Unknown record operation type`,
RecordCRUDActionExceptionCode.INVALID_REQUEST,
);
}
}
private async createRecord(
workflowActionInput: WorkflowCreateRecordActionInput,
): Promise<WorkflowActionResult> {
const repository = await this.twentyORMManager.getRepository(
workflowActionInput.objectName,
);
const objectRecord = await repository.create(
workflowActionInput.objectRecord,
);
await repository.save(objectRecord);
return {
result: objectRecord,
};
}
private async updateRecord(
workflowActionInput: WorkflowUpdateRecordActionInput,
): Promise<WorkflowActionResult> {
const repository = await this.twentyORMManager.getRepository(
workflowActionInput.objectName,
);
const objectRecord = await repository.findOne({
where: {
id: workflowActionInput.objectRecordId,
},
});
if (!objectRecord) {
throw new RecordCRUDActionException(
`Failed to update: Record ${workflowActionInput.objectName} with id ${workflowActionInput.objectRecordId} not found`,
RecordCRUDActionExceptionCode.RECORD_NOT_FOUND,
);
}
await repository.update(workflowActionInput.objectRecordId, {
...workflowActionInput.objectRecord,
});
return {
result: {
...objectRecord,
...workflowActionInput.objectRecord,
},
};
}
private async deleteRecord(
workflowActionInput: WorkflowDeleteRecordActionInput,
): Promise<WorkflowActionResult> {
const repository = await this.twentyORMManager.getRepository(
workflowActionInput.objectName,
);
const objectRecord = await repository.findOne({
where: {
id: workflowActionInput.objectRecordId,
},
});
if (!objectRecord) {
throw new RecordCRUDActionException(
`Failed to delete: Record ${workflowActionInput.objectName} with id ${workflowActionInput.objectRecordId} not found`,
RecordCRUDActionExceptionCode.RECORD_NOT_FOUND,
);
}
await repository.update(workflowActionInput.objectRecordId, {
deletedAt: new Date(),
});
return {
result: objectRecord,
};
}
private async findRecords(
workflowActionInput: WorkflowReadRecordActionInput,
workflowActionInput: WorkflowFindRecordsActionInput,
): Promise<WorkflowActionResult> {
const repository = await this.twentyORMManager.getRepository(
workflowActionInput.objectName,
@@ -221,7 +115,7 @@ export class RecordCRUDWorkflowAction implements WorkflowAction {
}
private async getObjectRecords<T extends ObjectLiteral>(
workflowActionInput: WorkflowReadRecordActionInput,
workflowActionInput: WorkflowFindRecordsActionInput,
objectMetadataItemWithFieldsMaps: ObjectMetadataItemWithFieldMaps,
objectMetadataMaps: ObjectMetadataMaps,
repository: WorkspaceRepository<T>,
@@ -261,7 +155,7 @@ export class RecordCRUDWorkflowAction implements WorkflowAction {
}
private async getTotalCount(
workflowActionInput: WorkflowReadRecordActionInput,
workflowActionInput: WorkflowFindRecordsActionInput,
repository: WorkspaceRepository<Entity>,
graphqlQueryParser: GraphqlQueryParser,
) {
@@ -2,11 +2,25 @@ import { Module } from '@nestjs/common';
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
import { RecordCRUDWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/record-crud.workflow-action';
import { CreateRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action';
import { DeleteRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/delete-record.workflow-action';
import { FindRecordsWorflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action';
import { UpdateRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/update-record.workflow-action';
@Module({
imports: [WorkspaceCacheStorageModule],
providers: [RecordCRUDWorkflowAction, ScopedWorkspaceContextFactory],
exports: [RecordCRUDWorkflowAction],
providers: [
ScopedWorkspaceContextFactory,
CreateRecordWorkflowAction,
UpdateRecordWorkflowAction,
DeleteRecordWorkflowAction,
FindRecordsWorflowAction,
],
exports: [
CreateRecordWorkflowAction,
UpdateRecordWorkflowAction,
DeleteRecordWorkflowAction,
FindRecordsWorflowAction,
],
})
export class RecordCRUDActionModule {}
@@ -5,42 +5,25 @@ import {
type ObjectRecord = Record<string, any>;
export enum WorkflowRecordCRUDType {
CREATE = 'CREATE',
UPDATE = 'UPDATE',
DELETE = 'DELETE',
READ = 'READ',
}
export type WorkflowCreateRecordActionInput = {
type: WorkflowRecordCRUDType.CREATE;
objectName: string;
objectRecord: ObjectRecord;
};
export type WorkflowUpdateRecordActionInput = {
type: WorkflowRecordCRUDType.UPDATE;
objectName: string;
objectRecord: ObjectRecord;
objectRecordId: string;
};
export type WorkflowDeleteRecordActionInput = {
type: WorkflowRecordCRUDType.DELETE;
objectName: string;
objectRecordId: string;
};
export type WorkflowReadRecordActionInput = {
type: WorkflowRecordCRUDType.READ;
export type WorkflowFindRecordsActionInput = {
objectName: string;
filter?: Partial<ObjectRecordFilter>;
orderBy?: Partial<ObjectRecordOrderBy>;
limit?: number;
};
export type WorkflowRecordCRUDActionInput =
| WorkflowCreateRecordActionInput
| WorkflowUpdateRecordActionInput
| WorkflowDeleteRecordActionInput
| WorkflowReadRecordActionInput;
@@ -1,6 +1,23 @@
import { WorkflowRecordCRUDActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
import {
WorkflowCreateRecordActionInput,
WorkflowDeleteRecordActionInput,
WorkflowFindRecordsActionInput,
WorkflowUpdateRecordActionInput,
} from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
import { BaseWorkflowActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';
export type WorkflowRecordCRUDActionSettings = BaseWorkflowActionSettings & {
input: WorkflowRecordCRUDActionInput;
export type WorkflowCreateRecordActionSettings = BaseWorkflowActionSettings & {
input: WorkflowCreateRecordActionInput;
};
export type WorkflowUpdateRecordActionSettings = BaseWorkflowActionSettings & {
input: WorkflowUpdateRecordActionInput;
};
export type WorkflowDeleteRecordActionSettings = BaseWorkflowActionSettings & {
input: WorkflowDeleteRecordActionInput;
};
export type WorkflowFindRecordsActionSettings = BaseWorkflowActionSettings & {
input: WorkflowFindRecordsActionInput;
};
@@ -0,0 +1,48 @@
import { Injectable } from '@nestjs/common';
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
import {
RecordCRUDActionException,
RecordCRUDActionExceptionCode,
} from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/exceptions/record-crud-action.exception';
import { WorkflowUpdateRecordActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-result.type';
@Injectable()
export class UpdateRecordWorkflowAction implements WorkflowAction {
constructor(private readonly twentyORMManager: TwentyORMManager) {}
async execute(
workflowActionInput: WorkflowUpdateRecordActionInput,
): Promise<WorkflowActionResult> {
const repository = await this.twentyORMManager.getRepository(
workflowActionInput.objectName,
);
const objectRecord = await repository.findOne({
where: {
id: workflowActionInput.objectRecordId,
},
});
if (!objectRecord) {
throw new RecordCRUDActionException(
`Failed to update: Record ${workflowActionInput.objectName} with id ${workflowActionInput.objectRecordId} not found`,
RecordCRUDActionExceptionCode.RECORD_NOT_FOUND,
);
}
await repository.update(workflowActionInput.objectRecordId, {
...workflowActionInput.objectRecord,
});
return {
result: {
...objectRecord,
...workflowActionInput.objectRecord,
},
};
}
}
@@ -1,7 +1,12 @@
import { OutputSchema } from 'src/modules/workflow/workflow-builder/types/output-schema.type';
import { WorkflowCodeActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/code/types/workflow-code-action-settings.type';
import { WorkflowSendEmailActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/types/workflow-send-email-action-settings.type';
import { WorkflowRecordCRUDActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-settings.type';
import { OutputSchema } from 'src/modules/workflow/workflow-builder/types/output-schema.type';
import {
WorkflowCreateRecordActionSettings,
WorkflowDeleteRecordActionSettings,
WorkflowFindRecordsActionSettings,
WorkflowUpdateRecordActionSettings,
} from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-settings.type';
export type BaseWorkflowActionSettings = {
input: object;
@@ -19,4 +24,7 @@ export type BaseWorkflowActionSettings = {
export type WorkflowActionSettings =
| WorkflowSendEmailActionSettings
| WorkflowCodeActionSettings
| WorkflowRecordCRUDActionSettings;
| WorkflowCreateRecordActionSettings
| WorkflowUpdateRecordActionSettings
| WorkflowDeleteRecordActionSettings
| WorkflowFindRecordsActionSettings;
@@ -1,12 +1,20 @@
import { WorkflowCodeActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/code/types/workflow-code-action-settings.type';
import { WorkflowSendEmailActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/types/workflow-send-email-action-settings.type';
import { WorkflowRecordCRUDActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-settings.type';
import {
WorkflowCreateRecordActionSettings,
WorkflowDeleteRecordActionSettings,
WorkflowFindRecordsActionSettings,
WorkflowUpdateRecordActionSettings,
} from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-settings.type';
import { WorkflowActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';
export enum WorkflowActionType {
CODE = 'CODE',
SEND_EMAIL = 'SEND_EMAIL',
RECORD_CRUD = 'RECORD_CRUD',
CREATE_RECORD = 'CREATE_RECORD',
UPDATE_RECORD = 'UPDATE_RECORD',
DELETE_RECORD = 'DELETE_RECORD',
FIND_RECORDS = 'FIND_RECORDS',
}
type BaseWorkflowAction = {
@@ -27,12 +35,30 @@ export type WorkflowSendEmailAction = BaseWorkflowAction & {
settings: WorkflowSendEmailActionSettings;
};
export type WorkflowRecordCRUDAction = BaseWorkflowAction & {
type: WorkflowActionType.RECORD_CRUD;
settings: WorkflowRecordCRUDActionSettings;
export type WorkflowCreateRecordAction = BaseWorkflowAction & {
type: WorkflowActionType.CREATE_RECORD;
settings: WorkflowCreateRecordActionSettings;
};
export type WorkflowUpdateRecordAction = BaseWorkflowAction & {
type: WorkflowActionType.UPDATE_RECORD;
settings: WorkflowUpdateRecordActionSettings;
};
export type WorkflowDeleteRecordAction = BaseWorkflowAction & {
type: WorkflowActionType.DELETE_RECORD;
settings: WorkflowDeleteRecordActionSettings;
};
export type WorkflowFindRecordsAction = BaseWorkflowAction & {
type: WorkflowActionType.FIND_RECORDS;
settings: WorkflowFindRecordsActionSettings;
};
export type WorkflowAction =
| WorkflowCodeAction
| WorkflowSendEmailAction
| WorkflowRecordCRUDAction;
| WorkflowCreateRecordAction
| WorkflowUpdateRecordAction
| WorkflowDeleteRecordAction
| WorkflowFindRecordsAction;