feat: Dynamic hook registration for WorkspaceQueryHooks (#6008)
#### Overview
This PR introduces a new API for dynamically registering and executing
pre and post query hooks in the Workspace Query Hook system using the
`@WorkspaceQueryHook` decorator. This approach eliminates the need for
manual provider registration, and fix the issue of `undefined` or `null`
repository using `@InjectWorkspaceRepository`.
#### New API
**Define a Hook**
Use the `@WorkspaceQueryHook` decorator to define pre or post hooks:
```typescript
@WorkspaceQueryHook({
key: `calendarEvent.findMany`,
scope: Scope.REQUEST,
})
export class CalendarEventFindManyPreQueryHook implements WorkspaceQueryHookInstance {
async execute(userId: string, workspaceId: string, payload: FindManyResolverArgs): Promise<void> {
if (!payload?.filter?.id?.eq) {
throw new BadRequestException('id filter is required');
}
// Implement hook logic here
}
}
```
This API simplifies the registration and execution of query hooks,
providing a more flexible and maintainable approach.
---------
Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
@ -1,16 +1,20 @@
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { BadRequestException, NotFoundException, Scope } from '@nestjs/common';
|
||||
|
||||
import { WorkspacePreQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-pre-query-hook/interfaces/workspace-pre-query-hook.interface';
|
||||
import { FindManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
|
||||
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-channel-event-association.workspace-entity';
|
||||
import { CanAccessCalendarEventService } from 'src/modules/calendar/query-hooks/calendar-event/services/can-access-calendar-event.service';
|
||||
import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceQueryHook({
|
||||
key: `calendarEvent.findMany`,
|
||||
scope: Scope.REQUEST,
|
||||
})
|
||||
export class CalendarEventFindManyPreQueryHook
|
||||
implements WorkspacePreQueryHook
|
||||
implements WorkspaceQueryHookInstance
|
||||
{
|
||||
constructor(
|
||||
@InjectWorkspaceRepository(CalendarChannelEventAssociationWorkspaceEntity)
|
||||
@ -27,25 +31,24 @@ export class CalendarEventFindManyPreQueryHook
|
||||
throw new BadRequestException('id filter is required');
|
||||
}
|
||||
|
||||
// TODO: Re-implement this using twenty ORM
|
||||
// const calendarChannelCalendarEventAssociations =
|
||||
// await this.calendarChannelEventAssociationRepository.find({
|
||||
// where: {
|
||||
// calendarEvent: {
|
||||
// id: payload?.filter?.id?.eq,
|
||||
// },
|
||||
// },
|
||||
// relations: ['calendarChannel.connectedAccount'],
|
||||
// });
|
||||
const calendarChannelCalendarEventAssociations =
|
||||
await this.calendarChannelEventAssociationRepository.find({
|
||||
where: {
|
||||
calendarEvent: {
|
||||
id: payload?.filter?.id?.eq,
|
||||
},
|
||||
},
|
||||
relations: ['calendarChannel.connectedAccount'],
|
||||
});
|
||||
|
||||
// if (calendarChannelCalendarEventAssociations.length === 0) {
|
||||
// throw new NotFoundException();
|
||||
// }
|
||||
if (calendarChannelCalendarEventAssociations.length === 0) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
// await this.canAccessCalendarEventService.canAccessCalendarEvent(
|
||||
// userId,
|
||||
// workspaceId,
|
||||
// calendarChannelCalendarEventAssociations,
|
||||
// );
|
||||
await this.canAccessCalendarEventService.canAccessCalendarEvent(
|
||||
userId,
|
||||
workspaceId,
|
||||
calendarChannelCalendarEventAssociations,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,21 @@
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { BadRequestException, NotFoundException, Scope } from '@nestjs/common';
|
||||
|
||||
import { WorkspacePreQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-pre-query-hook/interfaces/workspace-pre-query-hook.interface';
|
||||
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
import { FindOneResolverArgs } 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 { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { CanAccessCalendarEventService } from 'src/modules/calendar/query-hooks/calendar-event/services/can-access-calendar-event.service';
|
||||
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-channel-event-association.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class CalendarEventFindOnePreQueryHook implements WorkspacePreQueryHook {
|
||||
@WorkspaceQueryHook({
|
||||
key: `calendarEvent.findOne`,
|
||||
scope: Scope.REQUEST,
|
||||
})
|
||||
export class CalendarEventFindOnePreQueryHook
|
||||
implements WorkspaceQueryHookInstance
|
||||
{
|
||||
constructor(
|
||||
@InjectWorkspaceRepository(CalendarChannelEventAssociationWorkspaceEntity)
|
||||
private readonly calendarChannelEventAssociationRepository: WorkspaceRepository<CalendarChannelEventAssociationWorkspaceEntity>,
|
||||
@ -26,23 +32,24 @@ export class CalendarEventFindOnePreQueryHook implements WorkspacePreQueryHook {
|
||||
}
|
||||
|
||||
// TODO: Re-implement this using twenty ORM
|
||||
// const calendarChannelCalendarEventAssociations =
|
||||
// await this.calendarChannelEventAssociationRepository.find({
|
||||
// where: {
|
||||
// calendarEvent: {
|
||||
// id: payload?.filter?.id?.eq,
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
const calendarChannelCalendarEventAssociations =
|
||||
await this.calendarChannelEventAssociationRepository.find({
|
||||
where: {
|
||||
calendarEvent: {
|
||||
id: payload?.filter?.id?.eq,
|
||||
},
|
||||
},
|
||||
relations: ['calendarChannel.connectedAccount'],
|
||||
});
|
||||
|
||||
// if (calendarChannelCalendarEventAssociations.length === 0) {
|
||||
// throw new NotFoundException();
|
||||
// }
|
||||
if (calendarChannelCalendarEventAssociations.length === 0) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
// await this.canAccessCalendarEventService.canAccessCalendarEvent(
|
||||
// userId,
|
||||
// workspaceId,
|
||||
// calendarChannelCalendarEventAssociations,
|
||||
// );
|
||||
await this.canAccessCalendarEventService.canAccessCalendarEvent(
|
||||
userId,
|
||||
workspaceId,
|
||||
calendarChannelCalendarEventAssociations,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ export class CanAccessCalendarEventService {
|
||||
|
||||
const calendarChannelsConnectedAccounts =
|
||||
await this.connectedAccountRepository.getByIds(
|
||||
calendarChannels.map((channel) => channel.connectedAccount.id),
|
||||
calendarChannels.map((channel) => channel.connectedAccountId),
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
|
||||
@ -23,14 +23,8 @@ import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
],
|
||||
providers: [
|
||||
CanAccessCalendarEventService,
|
||||
{
|
||||
provide: CalendarEventFindOnePreQueryHook.name,
|
||||
useClass: CalendarEventFindOnePreQueryHook,
|
||||
},
|
||||
{
|
||||
provide: CalendarEventFindManyPreQueryHook.name,
|
||||
useClass: CalendarEventFindManyPreQueryHook,
|
||||
},
|
||||
CalendarEventFindOnePreQueryHook,
|
||||
CalendarEventFindManyPreQueryHook,
|
||||
],
|
||||
})
|
||||
export class CalendarQueryHookModule {}
|
||||
|
||||
@ -237,6 +237,8 @@ export class CalendarChannelWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
})
|
||||
connectedAccount: Relation<ConnectedAccountWorkspaceEntity>;
|
||||
|
||||
connectedAccountId: string;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId:
|
||||
CALENDAR_CHANNEL_STANDARD_FIELD_IDS.calendarChannelEventAssociations,
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspacePreQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-pre-query-hook/interfaces/workspace-pre-query-hook.interface';
|
||||
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 {
|
||||
BlocklistItem,
|
||||
BlocklistValidationService,
|
||||
} from 'src/modules/connected-account/services/blocklist/blocklist-validation.service';
|
||||
|
||||
@Injectable()
|
||||
export class BlocklistCreateManyPreQueryHook implements WorkspacePreQueryHook {
|
||||
@WorkspaceQueryHook(`blocklist.createMany`)
|
||||
export class BlocklistCreateManyPreQueryHook
|
||||
implements WorkspaceQueryHookInstance
|
||||
{
|
||||
constructor(
|
||||
private readonly blocklistValidationService: BlocklistValidationService,
|
||||
) {}
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
import { Injectable, MethodNotAllowedException } from '@nestjs/common';
|
||||
import { MethodNotAllowedException } from '@nestjs/common';
|
||||
|
||||
import { WorkspacePreQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-pre-query-hook/interfaces/workspace-pre-query-hook.interface';
|
||||
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
|
||||
@Injectable()
|
||||
export class BlocklistUpdateManyPreQueryHook implements WorkspacePreQueryHook {
|
||||
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
|
||||
@WorkspaceQueryHook(`blocklist.updateMany`)
|
||||
export class BlocklistUpdateManyPreQueryHook
|
||||
implements WorkspaceQueryHookInstance
|
||||
{
|
||||
constructor() {}
|
||||
|
||||
async execute(): Promise<void> {
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspacePreQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-pre-query-hook/interfaces/workspace-pre-query-hook.interface';
|
||||
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 {
|
||||
BlocklistItem,
|
||||
BlocklistValidationService,
|
||||
} from 'src/modules/connected-account/services/blocklist/blocklist-validation.service';
|
||||
|
||||
@Injectable()
|
||||
export class BlocklistUpdateOnePreQueryHook implements WorkspacePreQueryHook {
|
||||
@WorkspaceQueryHook(`blocklist.updateOne`)
|
||||
export class BlocklistUpdateOnePreQueryHook
|
||||
implements WorkspaceQueryHookInstance
|
||||
{
|
||||
constructor(
|
||||
private readonly blocklistValidationService: BlocklistValidationService,
|
||||
) {}
|
||||
|
||||
@ -8,18 +8,9 @@ import { BlocklistValidationModule } from 'src/modules/connected-account/service
|
||||
@Module({
|
||||
imports: [BlocklistValidationModule],
|
||||
providers: [
|
||||
{
|
||||
provide: BlocklistCreateManyPreQueryHook.name,
|
||||
useClass: BlocklistCreateManyPreQueryHook,
|
||||
},
|
||||
{
|
||||
provide: BlocklistUpdateManyPreQueryHook.name,
|
||||
useClass: BlocklistUpdateManyPreQueryHook,
|
||||
},
|
||||
{
|
||||
provide: BlocklistUpdateOnePreQueryHook.name,
|
||||
useClass: BlocklistUpdateOnePreQueryHook,
|
||||
},
|
||||
BlocklistCreateManyPreQueryHook,
|
||||
BlocklistUpdateManyPreQueryHook,
|
||||
BlocklistUpdateOnePreQueryHook,
|
||||
],
|
||||
})
|
||||
export class ConnectedAccountQueryHookModule {}
|
||||
|
||||
@ -1,19 +1,16 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
||||
|
||||
import { WorkspacePreQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-pre-query-hook/interfaces/workspace-pre-query-hook.interface';
|
||||
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
import { FindManyResolverArgs } 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 { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||
import { CanAccessMessageThreadService } from 'src/modules/messaging/common/query-hooks/message/can-access-message-thread.service';
|
||||
import { MessageChannelMessageAssociationRepository } from 'src/modules/messaging/common/repositories/message-channel-message-association.repository';
|
||||
import { MessageChannelMessageAssociationWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class MessageFindManyPreQueryHook implements WorkspacePreQueryHook {
|
||||
@WorkspaceQueryHook(`message.findMany`)
|
||||
export class MessageFindManyPreQueryHook implements WorkspaceQueryHookInstance {
|
||||
constructor(
|
||||
@InjectObjectMetadataRepository(
|
||||
MessageChannelMessageAssociationWorkspaceEntity,
|
||||
|
||||
@ -1,16 +1,17 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { NotFoundException } from '@nestjs/common';
|
||||
|
||||
import { WorkspacePreQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-pre-query-hook/interfaces/workspace-pre-query-hook.interface';
|
||||
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
import { FindOneResolverArgs } 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 { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||
import { CanAccessMessageThreadService } from 'src/modules/messaging/common/query-hooks/message/can-access-message-thread.service';
|
||||
import { MessageChannelMessageAssociationRepository } from 'src/modules/messaging/common/repositories/message-channel-message-association.repository';
|
||||
import { MessageChannelMessageAssociationWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class MessageFindOnePreQueryHook implements WorkspacePreQueryHook {
|
||||
@WorkspaceQueryHook(`message.findOne`)
|
||||
export class MessageFindOnePreQueryHook implements WorkspaceQueryHookInstance {
|
||||
constructor(
|
||||
@InjectObjectMetadataRepository(
|
||||
MessageChannelMessageAssociationWorkspaceEntity,
|
||||
|
||||
@ -20,14 +20,8 @@ import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/stan
|
||||
],
|
||||
providers: [
|
||||
CanAccessMessageThreadService,
|
||||
{
|
||||
provide: MessageFindOnePreQueryHook.name,
|
||||
useClass: MessageFindOnePreQueryHook,
|
||||
},
|
||||
{
|
||||
provide: MessageFindManyPreQueryHook.name,
|
||||
useClass: MessageFindManyPreQueryHook,
|
||||
},
|
||||
MessageFindOnePreQueryHook,
|
||||
MessageFindManyPreQueryHook,
|
||||
],
|
||||
})
|
||||
export class MessagingQueryHookModule {}
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { Injectable, MethodNotAllowedException } from '@nestjs/common';
|
||||
import { MethodNotAllowedException } from '@nestjs/common';
|
||||
|
||||
import { WorkspacePreQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-pre-query-hook/interfaces/workspace-pre-query-hook.interface';
|
||||
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
|
||||
@Injectable()
|
||||
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
|
||||
@WorkspaceQueryHook(`workspaceMember.deleteMany`)
|
||||
export class WorkspaceMemberDeleteManyPreQueryHook
|
||||
implements WorkspacePreQueryHook
|
||||
implements WorkspaceQueryHookInstance
|
||||
{
|
||||
constructor() {}
|
||||
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspacePreQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-pre-query-hook/interfaces/workspace-pre-query-hook.interface';
|
||||
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 { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { CommentWorkspaceEntity } from 'src/modules/activity/standard-objects/comment.workspace-entity';
|
||||
import { AttachmentWorkspaceEntity } from 'src/modules/attachment/standard-objects/attachment.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceQueryHook(`workspaceMember.deleteOne`)
|
||||
export class WorkspaceMemberDeleteOnePreQueryHook
|
||||
implements WorkspacePreQueryHook
|
||||
implements WorkspaceQueryHookInstance
|
||||
{
|
||||
constructor(
|
||||
@InjectWorkspaceRepository(AttachmentWorkspaceEntity)
|
||||
|
||||
@ -14,14 +14,8 @@ import { WorkspaceMemberDeleteOnePreQueryHook } from 'src/modules/workspace-memb
|
||||
]),
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: WorkspaceMemberDeleteOnePreQueryHook.name,
|
||||
useClass: WorkspaceMemberDeleteOnePreQueryHook,
|
||||
},
|
||||
{
|
||||
provide: WorkspaceMemberDeleteManyPreQueryHook.name,
|
||||
useClass: WorkspaceMemberDeleteManyPreQueryHook,
|
||||
},
|
||||
WorkspaceMemberDeleteOnePreQueryHook,
|
||||
WorkspaceMemberDeleteManyPreQueryHook,
|
||||
],
|
||||
})
|
||||
export class WorkspaceMemberQueryHookModule {}
|
||||
|
||||
Reference in New Issue
Block a user