Remove serverless functions on soft delete (#9438)

Delete workflow versions serverless functions when soft delete workflow
This commit is contained in:
martmull
2025-01-07 16:51:34 +01:00
committed by GitHub
parent 46f51577a3
commit 0c75b244ba
7 changed files with 81 additions and 31 deletions

View File

@ -122,10 +122,10 @@ export class ServerlessFunctionResolver {
try { try {
await this.checkFeatureFlag(workspaceId); await this.checkFeatureFlag(workspaceId);
return await this.serverlessFunctionService.deleteOneServerlessFunction( return await this.serverlessFunctionService.deleteOneServerlessFunction({
input.id, id: input.id,
workspaceId, workspaceId,
); });
} catch (error) { } catch (error) {
serverlessFunctionGraphQLApiExceptionHandler(error); serverlessFunctionGraphQLApiExceptionHandler(error);
} }

View File

@ -205,7 +205,15 @@ export class ServerlessFunctionService {
}); });
} }
async deleteOneServerlessFunction(id: string, workspaceId: string) { async deleteOneServerlessFunction({
id,
workspaceId,
isHardDeletion = true,
}: {
id: string;
workspaceId: string;
isHardDeletion?: boolean;
}) {
const existingServerlessFunction = const existingServerlessFunction =
await this.serverlessFunctionRepository.findOneBy({ await this.serverlessFunctionRepository.findOneBy({
id, id,
@ -219,16 +227,17 @@ export class ServerlessFunctionService {
); );
} }
await this.serverlessFunctionRepository.delete(id); if (isHardDeletion) {
await this.serverlessFunctionRepository.delete(id);
await this.fileStorageService.delete({
folderPath: getServerlessFolder({
serverlessFunction: existingServerlessFunction,
}),
});
}
await this.serverlessService.delete(existingServerlessFunction); await this.serverlessService.delete(existingServerlessFunction);
await this.fileStorageService.delete({
folderPath: getServerlessFolder({
serverlessFunction: existingServerlessFunction,
}),
});
return existingServerlessFunction; return existingServerlessFunction;
} }

View File

@ -18,12 +18,13 @@ export class WorkflowDeleteManyPostQueryHook
) {} ) {}
async execute( async execute(
_authContext: AuthContext, authContext: AuthContext,
_objectName: string, _objectName: string,
payload: WorkflowWorkspaceEntity[], payload: WorkflowWorkspaceEntity[],
): Promise<void> { ): Promise<void> {
this.workflowCommonWorkspaceService.cleanWorkflowsSubEntities( this.workflowCommonWorkspaceService.cleanWorkflowsSubEntities(
payload.map((workflow) => workflow.id), payload.map((workflow) => workflow.id),
authContext.workspace.id,
); );
} }
} }

View File

@ -18,12 +18,13 @@ export class WorkflowDeleteOnePostQueryHook
) {} ) {}
async execute( async execute(
_authContext: AuthContext, authContext: AuthContext,
_objectName: string, _objectName: string,
payload: WorkflowWorkspaceEntity[], payload: WorkflowWorkspaceEntity[],
): Promise<void> { ): Promise<void> {
this.workflowCommonWorkspaceService.cleanWorkflowsSubEntities( this.workflowCommonWorkspaceService.cleanWorkflowsSubEntities(
payload.map((workflow) => workflow.id), payload.map((workflow) => workflow.id),
authContext.workspace.id,
); );
} }
} }

View File

@ -25,10 +25,12 @@ import { WorkflowVersionUpdateManyPreQueryHook } from 'src/modules/workflow/comm
import { WorkflowVersionUpdateOnePreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow-version-update-one.pre-query.hook'; import { WorkflowVersionUpdateOnePreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow-version-update-one.pre-query.hook';
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
import { WorkflowVersionValidationWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-version-validation.workspace-service'; import { WorkflowVersionValidationWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-version-validation.workspace-service';
import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless-function/serverless-function.module';
@Module({ @Module({
imports: [ imports: [
NestjsQueryTypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'), NestjsQueryTypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'),
ServerlessFunctionModule,
], ],
providers: [ providers: [
WorkflowCreateOnePreQueryHook, WorkflowCreateOnePreQueryHook,

View File

@ -8,10 +8,16 @@ import {
WorkflowTriggerException, WorkflowTriggerException,
WorkflowTriggerExceptionCode, WorkflowTriggerExceptionCode,
} from 'src/modules/workflow/workflow-trigger/exceptions/workflow-trigger.exception'; } from 'src/modules/workflow/workflow-trigger/exceptions/workflow-trigger.exception';
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
@Injectable() @Injectable()
export class WorkflowCommonWorkspaceService { export class WorkflowCommonWorkspaceService {
constructor(private readonly twentyORMManager: TwentyORMManager) {} constructor(
private readonly twentyORMManager: TwentyORMManager,
private readonly serverlessFunctionService: ServerlessFunctionService,
) {}
async getWorkflowVersionOrFail( async getWorkflowVersionOrFail(
workflowVersionId: string, workflowVersionId: string,
@ -58,7 +64,10 @@ export class WorkflowCommonWorkspaceService {
return { ...workflowVersion, trigger: workflowVersion.trigger }; return { ...workflowVersion, trigger: workflowVersion.trigger };
} }
async cleanWorkflowsSubEntities(workflowIds: string[]): Promise<void> { async cleanWorkflowsSubEntities(
workflowIds: string[],
workspaceId: string,
): Promise<void> {
const workflowVersionRepository = const workflowVersionRepository =
await this.twentyORMManager.getRepository<WorkflowVersionWorkspaceEntity>( await this.twentyORMManager.getRepository<WorkflowVersionWorkspaceEntity>(
'workflowVersion', 'workflowVersion',
@ -74,20 +83,48 @@ export class WorkflowCommonWorkspaceService {
'workflowEventListener', 'workflowEventListener',
); );
Promise.all( workflowIds.forEach((workflowId) => {
workflowIds.map((workflowId) => { workflowEventListenerRepository.softDelete({
workflowEventListenerRepository.softDelete({ workflowId,
workflowId, });
});
workflowRunRepository.softDelete({ workflowRunRepository.softDelete({
workflowId, workflowId,
}); });
workflowVersionRepository.softDelete({ workflowVersionRepository.softDelete({
workflowId, workflowId,
}); });
}),
); this.deleteServerlessFunctions(
workflowVersionRepository,
workflowId,
workspaceId,
);
});
}
private async deleteServerlessFunctions(
workflowVersionRepository: WorkspaceRepository<WorkflowVersionWorkspaceEntity>,
workflowId: string,
workspaceId: string,
) {
const workflowVersions = await workflowVersionRepository.find({
where: {
workflowId,
},
});
workflowVersions.forEach((workflowVersion) => {
workflowVersion.steps?.forEach(async (step) => {
if (step.type === WorkflowActionType.CODE) {
await this.serverlessFunctionService.deleteOneServerlessFunction({
id: step.settings.input.serverlessFunctionId,
workspaceId,
isHardDeletion: false,
});
}
});
});
} }
} }

View File

@ -511,10 +511,10 @@ export class WorkflowVersionStepWorkspaceService {
}) { }) {
switch (step.type) { switch (step.type) {
case WorkflowActionType.CODE: { case WorkflowActionType.CODE: {
await this.serverlessFunctionService.deleteOneServerlessFunction( await this.serverlessFunctionService.deleteOneServerlessFunction({
step.settings.input.serverlessFunctionId, id: step.settings.input.serverlessFunctionId,
workspaceId, workspaceId,
); });
break; break;
} }