Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add workflow query hooks #6876

Merged
merged 4 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { CustomException } from 'src/utils/custom-exception';

export class WorkflowQueryHookException extends CustomException {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we want to call it QueryHookException? Maybe WorkflowVersionValidationException? Up to you!

constructor(message: string, code: WorkflowQueryHookExceptionCode) {
super(message, code);
}
}

export enum WorkflowQueryHookExceptionCode {
FORBIDDEN = 'FORBIDDEN',
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,34 @@
import { Module } from '@nestjs/common';

import { WorkflowVersionUpdateOnePreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow-version/workflow-version-update-one.pre-query-hook';
import { WorkflowVersionValidationWorkspaceService } from 'src/modules/workflow/common/query-hooks/workflow-version/workflow-version-validation.workspace-service';
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workflow-common.workspace-service';
import { WorkflowRunCreateManyPreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow-run/workflow-run-create-many.pre-query.hook';
import { WorkflowRunCreateOnePreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow-run/workflow-run-create-one.pre-query.hook';
import { WorkflowVersionValidationWorkspaceService } from 'src/modules/workflow/common/query-hooks/workflow-version/services/workflow-version-validation.workspace-service';
import { WorkflowVersionCreateManyPreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow-version/workflow-version-create-many.pre-query.hook';
import { WorkflowVersionCreateOnePreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow-version/workflow-version-create-one.pre-query.hook';
import { WorkflowVersionDeleteManyPreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow-version/workflow-version-delete-many.pre-query.hook';
import { WorkflowVersionDeleteOnePreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow-version/workflow-version-delete-one.pre-query.hook';
import { WorkflowVersionUpdateManyPreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow-version/workflow-version-update-many.pre-query.hook';
import { WorkflowVersionUpdateOnePreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow-version/workflow-version-update-one.pre-query.hook';
import { WorkflowCreateManyPreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow/workflow-create-many.pre-query.hook';
import { WorkflowCreateOnePreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow/workflow-create-one.pre-query.hook';
import { WorkflowUpdateManyPreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow/workflow-update-many.pre-query.hook';
import { WorkflowUpdateOnePreQueryHook } from 'src/modules/workflow/common/query-hooks/workflow/workflow-update-one.pre-query.hook';
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/services/workflow-common.workspace-service';

@Module({
providers: [
WorkflowCreateOnePreQueryHook,
WorkflowCreateManyPreQueryHook,
WorkflowUpdateOnePreQueryHook,
WorkflowUpdateManyPreQueryHook,
WorkflowRunCreateOnePreQueryHook,
WorkflowRunCreateManyPreQueryHook,
WorkflowVersionCreateOnePreQueryHook,
WorkflowVersionCreateManyPreQueryHook,
WorkflowVersionUpdateOnePreQueryHook,
WorkflowVersionUpdateManyPreQueryHook,
WorkflowVersionDeleteOnePreQueryHook,
WorkflowVersionDeleteManyPreQueryHook,
WorkflowVersionValidationWorkspaceService,
WorkflowCommonWorkspaceService,
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
import { CreateManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';

import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
import {
WorkflowQueryHookException,
WorkflowQueryHookExceptionCode,
} from 'src/modules/workflow/common/query-hooks/workflow-query-hook.exception';
import { WorkflowRunWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity';

@WorkspaceQueryHook(`workflowRun.createMany`)
export class WorkflowRunCreateManyPreQueryHook
implements WorkspaceQueryHookInstance
{
async execute(): Promise<CreateManyResolverArgs<WorkflowRunWorkspaceEntity>> {
throw new WorkflowQueryHookException(
'Method not allowed.',
WorkflowQueryHookExceptionCode.FORBIDDEN,
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider adding a more specific error message explaining why creating multiple workflow runs is not allowed

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
import { CreateOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';

import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
import {
WorkflowQueryHookException,
WorkflowQueryHookExceptionCode,
} from 'src/modules/workflow/common/query-hooks/workflow-query-hook.exception';
import { WorkflowRunWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity';

@WorkspaceQueryHook(`workflowRun.createOne`)
export class WorkflowRunCreateOnePreQueryHook
implements WorkspaceQueryHookInstance
{
async execute(): Promise<CreateOneResolverArgs<WorkflowRunWorkspaceEntity>> {
throw new WorkflowQueryHookException(
'Method not allowed.',
WorkflowQueryHookExceptionCode.FORBIDDEN,
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
import { DeleteManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';

import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
import {
WorkflowQueryHookException,
WorkflowQueryHookExceptionCode,
} from 'src/modules/workflow/common/query-hooks/workflow-query-hook.exception';
import { WorkflowRunWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: WorkflowRunWorkspaceEntity is imported but not directly used. Consider removing if unnecessary.


@WorkspaceQueryHook(`workflowRun.deleteMany`)
export class WorkflowRunDeleteManyPreQueryHook
implements WorkspaceQueryHookInstance
{
async execute(): Promise<DeleteManyResolverArgs<WorkflowRunWorkspaceEntity>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: execute method doesn't use its parameters (userId, workspaceId, payload). Consider removing them if not needed.

throw new WorkflowQueryHookException(
'Method not allowed.',
WorkflowQueryHookExceptionCode.FORBIDDEN,
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
import { DeleteOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';

import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
import {
WorkflowQueryHookException,
WorkflowQueryHookExceptionCode,
} from 'src/modules/workflow/common/query-hooks/workflow-query-hook.exception';

@WorkspaceQueryHook(`workflowRun.deleteOne`)
export class WorkflowRunDeleteOnePreQueryHook
implements WorkspaceQueryHookInstance
{
async execute(): Promise<DeleteOneResolverArgs> {
throw new WorkflowQueryHookException(
'Method not allowed.',
WorkflowQueryHookExceptionCode.FORBIDDEN,
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider adding parameters to execute() method for consistency with WorkspaceQueryHookInstance interface

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
import { UpdateManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';

import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
import {
WorkflowQueryHookException,
WorkflowQueryHookExceptionCode,
} from 'src/modules/workflow/common/query-hooks/workflow-query-hook.exception';
import { WorkflowRunWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity';

@WorkspaceQueryHook(`workflowRun.updateMany`)
export class WorkflowRunUpdateManyPreQueryHook
implements WorkspaceQueryHookInstance
{
async execute(): Promise<UpdateManyResolverArgs<WorkflowRunWorkspaceEntity>> {
throw new WorkflowQueryHookException(
'Method not allowed.',
WorkflowQueryHookExceptionCode.FORBIDDEN,
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider adding a more specific error message explaining why this operation is not allowed

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
import { UpdateOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';

import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
import {
WorkflowQueryHookException,
WorkflowQueryHookExceptionCode,
} from 'src/modules/workflow/common/query-hooks/workflow-query-hook.exception';
import { WorkflowRunWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity';

@WorkspaceQueryHook(`workflowRun.updateOne`)
export class WorkflowRunUpdateOnePreQueryHook
implements WorkspaceQueryHookInstance
{
async execute(): Promise<UpdateOneResolverArgs<WorkflowRunWorkspaceEntity>> {
throw new WorkflowQueryHookException(
'Method not allowed.',
WorkflowQueryHookExceptionCode.FORBIDDEN,
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider adding a more descriptive error message explaining why updates are not allowed

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

import {
CreateOneResolverArgs,
DeleteOneResolverArgs,
UpdateOneResolverArgs,
} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';

import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
import {
WorkflowQueryHookException,
WorkflowQueryHookExceptionCode,
} from 'src/modules/workflow/common/query-hooks/workflow-query-hook.exception';
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/services/workflow-common.workspace-service';
import {
WorkflowVersionStatus,
WorkflowVersionWorkspaceEntity,
} from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
import { WorkflowTrigger } from 'src/modules/workflow/common/types/workflow-trigger.type';

@Injectable()
export class WorkflowVersionValidationWorkspaceService {
constructor(
private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService,
private readonly twentyORMManager: TwentyORMManager,
) {}

async validateWorkflowVersionForCreateOne(
payload: CreateOneResolverArgs<WorkflowVersionWorkspaceEntity>,
) {
if (
payload.data.status &&
payload.data.status !== WorkflowVersionStatus.DRAFT
) {
Comment on lines +31 to +34
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider using optional chaining (?.) for payload.data.status to handle potential undefined values

throw new WorkflowQueryHookException(
'Cannot create workflow version with status other than draft',
WorkflowQueryHookExceptionCode.FORBIDDEN,
);
}

const workflowVersionRepository =
await this.twentyORMManager.getRepository<WorkflowVersionWorkspaceEntity>(
'workflowVersion',
);

const workflowAlreadyHasDraftVersion =
await workflowVersionRepository.exists({
where: {
workflowId: payload.data.workflowId,
status: WorkflowVersionStatus.DRAFT,
},
});

if (workflowAlreadyHasDraftVersion) {
throw new WorkflowQueryHookException(
'Cannot create multiple draft versions for the same workflow',
WorkflowQueryHookExceptionCode.FORBIDDEN,
);
}
}

async validateWorkflowVersionForUpdateOne(
payload: UpdateOneResolverArgs<WorkflowVersionWorkspaceEntity>,
) {
const workflowVersion =
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail(
payload.id,
);

this.validateWorkflowVersionIsDraft(workflowVersion);

if (payload.data.status !== workflowVersion.status) {
throw new WorkflowQueryHookException(
'Cannot update workflow version status manually',
WorkflowQueryHookExceptionCode.FORBIDDEN,
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: This check prevents any status updates. Ensure this is the intended behavior

}

async validateWorkflowVersionForDeleteOne(payload: DeleteOneResolverArgs) {
const workflowVersion =
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail(
payload.id,
);

this.validateWorkflowVersionIsDraft(workflowVersion);
}

private validateWorkflowVersionIsDraft(
workflowVersion: Omit<WorkflowVersionWorkspaceEntity, 'trigger'> & {
trigger: WorkflowTrigger;
},
) {
if (workflowVersion.status !== WorkflowVersionStatus.DRAFT) {
throw new WorkflowQueryHookException(
'Workflow version is not in draft status',
WorkflowQueryHookExceptionCode.FORBIDDEN,
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
import {
CreateManyResolverArgs,
CreateOneResolverArgs,
} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';

import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
import { WorkflowVersionValidationWorkspaceService } from 'src/modules/workflow/common/query-hooks/workflow-version/services/workflow-version-validation.workspace-service';
import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';

@WorkspaceQueryHook(`workflowVersion.createMany`)
export class WorkflowVersionCreateManyPreQueryHook
implements WorkspaceQueryHookInstance
{
constructor(
private readonly workflowVersionValidationWorkspaceService: WorkflowVersionValidationWorkspaceService,
) {}

async execute(
_authContext: AuthContext,
_objectName: string,
payload: CreateManyResolverArgs<WorkflowVersionWorkspaceEntity>,
): Promise<CreateManyResolverArgs<WorkflowVersionWorkspaceEntity>> {
await Promise.all(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be careful with Promise.all, it could have an impact on the server performances so if the list is big it would be better to run those promises in batches

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be ok, we should only do createOne. I did not block because createOne calls createMany in the query runner

payload.data.map(async (workflowVersion) => {
await this.workflowVersionValidationWorkspaceService.validateWorkflowVersionForCreateOne(
{
data: workflowVersion,
} satisfies CreateOneResolverArgs<WorkflowVersionWorkspaceEntity>,
);
}),
);
Comment on lines +25 to +33
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: This Promise.all could potentially throw if any validation fails. Consider wrapping it in a try-catch block to handle errors gracefully and provide meaningful feedback.


return payload;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
import { CreateOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';

import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
import { WorkflowVersionValidationWorkspaceService } from 'src/modules/workflow/common/query-hooks/workflow-version/services/workflow-version-validation.workspace-service';
import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';

@WorkspaceQueryHook(`workflowVersion.createOne`)
export class WorkflowVersionCreateOnePreQueryHook
implements WorkspaceQueryHookInstance
{
constructor(
private readonly workflowVersionValidationWorkspaceService: WorkflowVersionValidationWorkspaceService,
) {}

async execute(
_authContext: AuthContext,
_objectName: string,
payload: CreateOneResolverArgs<WorkflowVersionWorkspaceEntity>,
): Promise<CreateOneResolverArgs<WorkflowVersionWorkspaceEntity>> {
await this.workflowVersionValidationWorkspaceService.validateWorkflowVersionForCreateOne(
payload,
);

return payload;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
import { DeleteManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';

import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
import {
WorkflowQueryHookException,
WorkflowQueryHookExceptionCode,
} from 'src/modules/workflow/common/query-hooks/workflow-query-hook.exception';
import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';

@WorkspaceQueryHook(`workflowVersion.deleteMany`)
export class WorkflowVersionDeleteManyPreQueryHook
implements WorkspaceQueryHookInstance
{
async execute(): Promise<
DeleteManyResolverArgs<WorkflowVersionWorkspaceEntity>
> {
throw new WorkflowQueryHookException(
'Method not allowed.',
WorkflowQueryHookExceptionCode.FORBIDDEN,
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: The error message could be more specific, e.g., 'Bulk deletion of workflow versions is not allowed.'

}
}
Loading
Loading