fix(16819): Add NoteDeleteOnePostQueryHook for soft removing note targets (#16826)
Fixes [#16819](https://github.com/twentyhq/twenty/issues/16819) Added a post-query hook (NoteDeleteOnePostQueryHook) that automatically soft-deletes all associated noteTarget records when a note is deleted. ## Key changes: Created `note-delete-one.post-query.hook.ts` Uses `GlobalWorkspaceOrmManager` to access workspace entities correctly Executes in the proper workspace context to ensure data isolation Soft-deletes noteTarget records to maintain referential consistency ## Testing 1. Company notes Create a Company Create and attach a Note to the Company Navigate to Company → Notes page (should display the note) Delete the Note Navigate to Company → Notes page (should no longer crash) Restore the Note (should appear again) Delete and destroy the Note permanently Navigate to Company → Notes page (should still work, just empty) 2. Opportunity notes Create an Opportunity Create and attach a Note to the Opportunity Navigate to Opportunity → Notes page (should display the note) Delete the Note Navigate to Opportunity → Notes page (should no longer crash) Restore the Note (should appear again) Delete and destroy the Note permanently Navigate to Opportunity → Notes page (should still work, just empty) ### Expected behavior: Company Notes page remains functional at all stages No console errors Deleted notes properly excluded from the view Other notes on the same Company remain accessible Additional Notes This fix ensures data consistency by maintaining the relationship between notes and their targets throughout the deletion lifecycle. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
This commit is contained in:
+4
@@ -10,6 +10,8 @@ import { CalendarQueryHookModule } from 'src/modules/calendar/common/query-hooks
|
||||
import { ConnectedAccountQueryHookModule } from 'src/modules/connected-account/query-hooks/connected-account-query-hook.module';
|
||||
import { DashboardQueryHookModule } from 'src/modules/dashboard/query-hooks/dashboard-query-hook.module';
|
||||
import { MessagingQueryHookModule } from 'src/modules/messaging/common/query-hooks/messaging-query-hook.module';
|
||||
import { NoteQueryHookModule } from 'src/modules/note/query-hooks/note-query-hook.module';
|
||||
import { TaskQueryHookModule } from 'src/modules/task/query-hooks/task-query-hook.module';
|
||||
import { WorkspaceMemberQueryHookModule } from 'src/modules/workspace-member/query-hooks/workspace-member-query-hook.module';
|
||||
|
||||
@Module({
|
||||
@@ -20,6 +22,8 @@ import { WorkspaceMemberQueryHookModule } from 'src/modules/workspace-member/que
|
||||
DashboardQueryHookModule,
|
||||
BlocklistQueryHookModule,
|
||||
WorkspaceMemberQueryHookModule,
|
||||
NoteQueryHookModule,
|
||||
TaskQueryHookModule,
|
||||
DiscoveryModule,
|
||||
],
|
||||
providers: [
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type WorkspacePostQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
|
||||
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { NotePostQueryHookService } from 'src/modules/note/query-hooks/note-post-query-hook.service';
|
||||
import { NoteWorkspaceEntity } from 'src/modules/note/standard-objects/note.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceQueryHook({
|
||||
key: `note.deleteMany`,
|
||||
type: WorkspaceQueryHookType.POST_HOOK,
|
||||
})
|
||||
export class NoteDeleteManyPostQueryHook
|
||||
implements WorkspacePostQueryHookInstance
|
||||
{
|
||||
constructor(
|
||||
private readonly notePostQueryHookService: NotePostQueryHookService,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
authContext: AuthContext,
|
||||
_objectName: string,
|
||||
payload: NoteWorkspaceEntity[],
|
||||
): Promise<void> {
|
||||
await this.notePostQueryHookService.handleNoteTargetsDelete(
|
||||
authContext,
|
||||
payload,
|
||||
);
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type WorkspacePostQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
|
||||
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { NotePostQueryHookService } from 'src/modules/note/query-hooks/note-post-query-hook.service';
|
||||
import { NoteWorkspaceEntity } from 'src/modules/note/standard-objects/note.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceQueryHook({
|
||||
key: `note.deleteOne`,
|
||||
type: WorkspaceQueryHookType.POST_HOOK,
|
||||
})
|
||||
export class NoteDeleteOnePostQueryHook
|
||||
implements WorkspacePostQueryHookInstance
|
||||
{
|
||||
constructor(
|
||||
private readonly notePostQueryHookService: NotePostQueryHookService,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
authContext: AuthContext,
|
||||
_objectName: string,
|
||||
payload: NoteWorkspaceEntity[],
|
||||
): Promise<void> {
|
||||
await this.notePostQueryHookService.handleNoteTargetsDelete(
|
||||
authContext,
|
||||
payload,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { assertIsDefinedOrThrow } from 'twenty-shared/utils';
|
||||
import { In } from 'typeorm';
|
||||
|
||||
import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface';
|
||||
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { WorkspaceNotFoundDefaultError } from 'src/engine/core-modules/workspace/workspace.exception';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { NoteTargetWorkspaceEntity } from 'src/modules/note/standard-objects/note-target.workspace-entity';
|
||||
import { NoteWorkspaceEntity } from 'src/modules/note/standard-objects/note.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class NotePostQueryHookService {
|
||||
constructor(
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
) {}
|
||||
|
||||
async handleNoteTargetsDelete(
|
||||
authContext: AuthContext,
|
||||
payload: NoteWorkspaceEntity[],
|
||||
): Promise<void> {
|
||||
if (!payload || payload?.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workspace = authContext.workspace;
|
||||
|
||||
assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError);
|
||||
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext as WorkspaceAuthContext,
|
||||
async () => {
|
||||
const noteTargetRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<NoteTargetWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'noteTarget',
|
||||
);
|
||||
|
||||
await noteTargetRepository.softDelete({
|
||||
noteId: In(payload.map((note) => note.id)),
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async handleNoteTargetsRestore(
|
||||
authContext: AuthContext,
|
||||
payload: NoteWorkspaceEntity[],
|
||||
): Promise<void> {
|
||||
if (!payload || payload?.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workspace = authContext.workspace;
|
||||
|
||||
assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError);
|
||||
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext as WorkspaceAuthContext,
|
||||
async () => {
|
||||
const noteTargetRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<NoteTargetWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'noteTarget',
|
||||
);
|
||||
|
||||
await noteTargetRepository.restore({
|
||||
noteId: In(payload.map((note) => note.id)),
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { NoteDeleteManyPostQueryHook } from 'src/modules/note/query-hooks/note-delete-many.post-query.hook';
|
||||
import { NoteDeleteOnePostQueryHook } from 'src/modules/note/query-hooks/note-delete-one.post-query.hook';
|
||||
import { NotePostQueryHookService } from 'src/modules/note/query-hooks/note-post-query-hook.service';
|
||||
import { NoteRestoreManyPostQueryHook } from 'src/modules/note/query-hooks/note-restore-many.post-query.hook';
|
||||
import { NoteRestoreOnePostQueryHook } from 'src/modules/note/query-hooks/note-restore-one.post-query.hook';
|
||||
|
||||
@Module({
|
||||
providers: [
|
||||
NotePostQueryHookService,
|
||||
NoteDeleteManyPostQueryHook,
|
||||
NoteDeleteOnePostQueryHook,
|
||||
NoteRestoreManyPostQueryHook,
|
||||
NoteRestoreOnePostQueryHook,
|
||||
],
|
||||
})
|
||||
export class NoteQueryHookModule {}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type WorkspacePostQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
|
||||
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { NotePostQueryHookService } from 'src/modules/note/query-hooks/note-post-query-hook.service';
|
||||
import { NoteWorkspaceEntity } from 'src/modules/note/standard-objects/note.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceQueryHook({
|
||||
key: `note.restoreMany`,
|
||||
type: WorkspaceQueryHookType.POST_HOOK,
|
||||
})
|
||||
export class NoteRestoreManyPostQueryHook
|
||||
implements WorkspacePostQueryHookInstance
|
||||
{
|
||||
constructor(
|
||||
private readonly notePostQueryHookService: NotePostQueryHookService,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
authContext: AuthContext,
|
||||
_objectName: string,
|
||||
payload: NoteWorkspaceEntity[],
|
||||
): Promise<void> {
|
||||
await this.notePostQueryHookService.handleNoteTargetsRestore(
|
||||
authContext,
|
||||
payload,
|
||||
);
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type WorkspacePostQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
|
||||
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { NotePostQueryHookService } from 'src/modules/note/query-hooks/note-post-query-hook.service';
|
||||
import { NoteWorkspaceEntity } from 'src/modules/note/standard-objects/note.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceQueryHook({
|
||||
key: `note.restoreOne`,
|
||||
type: WorkspaceQueryHookType.POST_HOOK,
|
||||
})
|
||||
export class NoteRestoreOnePostQueryHook
|
||||
implements WorkspacePostQueryHookInstance
|
||||
{
|
||||
constructor(
|
||||
private readonly notePostQueryHookService: NotePostQueryHookService,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
authContext: AuthContext,
|
||||
_objectName: string,
|
||||
payload: NoteWorkspaceEntity[],
|
||||
): Promise<void> {
|
||||
await this.notePostQueryHookService.handleNoteTargetsRestore(
|
||||
authContext,
|
||||
payload,
|
||||
);
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type WorkspacePostQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
|
||||
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { TaskPostQueryHookService } from 'src/modules/task/query-hooks/task-post-query-hook.service';
|
||||
import { TaskWorkspaceEntity } from 'src/modules/task/standard-objects/task.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceQueryHook({
|
||||
key: `task.deleteMany`,
|
||||
type: WorkspaceQueryHookType.POST_HOOK,
|
||||
})
|
||||
export class TaskDeleteManyPostQueryHook
|
||||
implements WorkspacePostQueryHookInstance
|
||||
{
|
||||
constructor(
|
||||
private readonly taskPostQueryHookService: TaskPostQueryHookService,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
authContext: AuthContext,
|
||||
_objectName: string,
|
||||
payload: TaskWorkspaceEntity[],
|
||||
): Promise<void> {
|
||||
await this.taskPostQueryHookService.handleTaskTargetsDelete(
|
||||
authContext,
|
||||
payload,
|
||||
);
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type WorkspacePostQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
|
||||
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { TaskPostQueryHookService } from 'src/modules/task/query-hooks/task-post-query-hook.service';
|
||||
import { TaskWorkspaceEntity } from 'src/modules/task/standard-objects/task.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceQueryHook({
|
||||
key: `task.deleteOne`,
|
||||
type: WorkspaceQueryHookType.POST_HOOK,
|
||||
})
|
||||
export class TaskDeleteOnePostQueryHook
|
||||
implements WorkspacePostQueryHookInstance
|
||||
{
|
||||
constructor(
|
||||
private readonly taskPostQueryHookService: TaskPostQueryHookService,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
authContext: AuthContext,
|
||||
_objectName: string,
|
||||
payload: TaskWorkspaceEntity[],
|
||||
): Promise<void> {
|
||||
await this.taskPostQueryHookService.handleTaskTargetsDelete(
|
||||
authContext,
|
||||
payload,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { assertIsDefinedOrThrow } from 'twenty-shared/utils';
|
||||
import { In } from 'typeorm';
|
||||
|
||||
import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface';
|
||||
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { WorkspaceNotFoundDefaultError } from 'src/engine/core-modules/workspace/workspace.exception';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { TaskTargetWorkspaceEntity } from 'src/modules/task/standard-objects/task-target.workspace-entity';
|
||||
import { TaskWorkspaceEntity } from 'src/modules/task/standard-objects/task.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class TaskPostQueryHookService {
|
||||
constructor(
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
) {}
|
||||
|
||||
async handleTaskTargetsDelete(
|
||||
authContext: AuthContext,
|
||||
payload: TaskWorkspaceEntity[],
|
||||
): Promise<void> {
|
||||
if (!payload || payload?.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workspace = authContext.workspace;
|
||||
|
||||
assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError);
|
||||
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext as WorkspaceAuthContext,
|
||||
async () => {
|
||||
const taskTargetRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<TaskTargetWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'taskTarget',
|
||||
);
|
||||
|
||||
await taskTargetRepository.softDelete({
|
||||
taskId: In(payload.map((task) => task.id)),
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async handleTaskTargetsRestore(
|
||||
authContext: AuthContext,
|
||||
payload: TaskWorkspaceEntity[],
|
||||
): Promise<void> {
|
||||
if (!payload || payload?.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workspace = authContext.workspace;
|
||||
|
||||
assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError);
|
||||
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext as WorkspaceAuthContext,
|
||||
async () => {
|
||||
const taskTargetRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<TaskTargetWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'taskTarget',
|
||||
);
|
||||
|
||||
await taskTargetRepository.restore({
|
||||
taskId: In(payload.map((task) => task.id)),
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { TaskDeleteManyPostQueryHook } from 'src/modules/task/query-hooks/task-delete-many.post-query.hook';
|
||||
import { TaskDeleteOnePostQueryHook } from 'src/modules/task/query-hooks/task-delete-one.post-query.hook';
|
||||
import { TaskPostQueryHookService } from 'src/modules/task/query-hooks/task-post-query-hook.service';
|
||||
import { TaskRestoreManyPostQueryHook } from 'src/modules/task/query-hooks/task-restore-many.post-query.hook';
|
||||
import { TaskRestoreOnePostQueryHook } from 'src/modules/task/query-hooks/task-restore-one.post-query.hook';
|
||||
|
||||
@Module({
|
||||
providers: [
|
||||
TaskPostQueryHookService,
|
||||
TaskDeleteManyPostQueryHook,
|
||||
TaskDeleteOnePostQueryHook,
|
||||
TaskRestoreManyPostQueryHook,
|
||||
TaskRestoreOnePostQueryHook,
|
||||
],
|
||||
})
|
||||
export class TaskQueryHookModule {}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type WorkspacePostQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
|
||||
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { TaskPostQueryHookService } from 'src/modules/task/query-hooks/task-post-query-hook.service';
|
||||
import { TaskWorkspaceEntity } from 'src/modules/task/standard-objects/task.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceQueryHook({
|
||||
key: `task.restoreMany`,
|
||||
type: WorkspaceQueryHookType.POST_HOOK,
|
||||
})
|
||||
export class TaskRestoreManyPostQueryHook
|
||||
implements WorkspacePostQueryHookInstance
|
||||
{
|
||||
constructor(
|
||||
private readonly taskPostQueryHookService: TaskPostQueryHookService,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
authContext: AuthContext,
|
||||
_objectName: string,
|
||||
payload: TaskWorkspaceEntity[],
|
||||
): Promise<void> {
|
||||
await this.taskPostQueryHookService.handleTaskTargetsRestore(
|
||||
authContext,
|
||||
payload,
|
||||
);
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type WorkspacePostQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
|
||||
|
||||
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
|
||||
import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { TaskPostQueryHookService } from 'src/modules/task/query-hooks/task-post-query-hook.service';
|
||||
import { TaskWorkspaceEntity } from 'src/modules/task/standard-objects/task.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceQueryHook({
|
||||
key: `task.restoreOne`,
|
||||
type: WorkspaceQueryHookType.POST_HOOK,
|
||||
})
|
||||
export class TaskRestoreOnePostQueryHook
|
||||
implements WorkspacePostQueryHookInstance
|
||||
{
|
||||
constructor(
|
||||
private readonly taskPostQueryHookService: TaskPostQueryHookService,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
authContext: AuthContext,
|
||||
_objectName: string,
|
||||
payload: TaskWorkspaceEntity[],
|
||||
): Promise<void> {
|
||||
await this.taskPostQueryHookService.handleTaskTargetsRestore(
|
||||
authContext,
|
||||
payload,
|
||||
);
|
||||
}
|
||||
}
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
import { randomUUID } from 'crypto';
|
||||
|
||||
import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util';
|
||||
import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util';
|
||||
import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util';
|
||||
import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util';
|
||||
import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util';
|
||||
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
|
||||
import { restoreManyOperationFactory } from 'test/integration/graphql/utils/restore-many-operation-factory.util';
|
||||
import { restoreOneOperationFactory } from 'test/integration/graphql/utils/restore-one-operation-factory.util';
|
||||
|
||||
const NOTE_GQL_FIELDS = `
|
||||
id
|
||||
title
|
||||
deletedAt
|
||||
`;
|
||||
|
||||
const NOTE_TARGET_GQL_FIELDS = `
|
||||
id
|
||||
noteId
|
||||
deletedAt
|
||||
`;
|
||||
|
||||
describe('Note post-query hooks', () => {
|
||||
const noteIds: string[] = [];
|
||||
const noteTargetIds: string[] = [];
|
||||
|
||||
afterAll(async () => {
|
||||
if (noteIds.length > 0) {
|
||||
const destroyNotesOperation = destroyManyOperationFactory({
|
||||
objectMetadataSingularName: 'note',
|
||||
objectMetadataPluralName: 'notes',
|
||||
gqlFields: 'id',
|
||||
filter: { id: { in: noteIds } },
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(destroyNotesOperation);
|
||||
}
|
||||
|
||||
if (noteTargetIds.length > 0) {
|
||||
const destroyNoteTargetsOperation = destroyManyOperationFactory({
|
||||
objectMetadataSingularName: 'noteTarget',
|
||||
objectMetadataPluralName: 'noteTargets',
|
||||
gqlFields: 'id',
|
||||
filter: { id: { in: noteTargetIds } },
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(destroyNoteTargetsOperation);
|
||||
}
|
||||
});
|
||||
|
||||
it('deleteOne should soft delete related noteTargets', async () => {
|
||||
const noteId = randomUUID();
|
||||
const noteTargetId = randomUUID();
|
||||
|
||||
noteIds.push(noteId);
|
||||
noteTargetIds.push(noteTargetId);
|
||||
|
||||
const createNoteOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'note',
|
||||
gqlFields: NOTE_GQL_FIELDS,
|
||||
data: { id: noteId, title: 'Test Note for DeleteOne' },
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(createNoteOperation);
|
||||
|
||||
const createNoteTargetOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'noteTarget',
|
||||
gqlFields: NOTE_TARGET_GQL_FIELDS,
|
||||
data: { id: noteTargetId, noteId },
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(createNoteTargetOperation);
|
||||
|
||||
const deleteNoteOperation = deleteOneOperationFactory({
|
||||
objectMetadataSingularName: 'note',
|
||||
gqlFields: NOTE_GQL_FIELDS,
|
||||
recordId: noteId,
|
||||
});
|
||||
|
||||
const deleteResponse = await makeGraphqlAPIRequest(deleteNoteOperation);
|
||||
|
||||
expect(deleteResponse.body.data.deleteNote).toBeDefined();
|
||||
expect(deleteResponse.body.data.deleteNote.deletedAt).not.toBeNull();
|
||||
|
||||
const findNoteTargetsOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'noteTarget',
|
||||
objectMetadataPluralName: 'noteTargets',
|
||||
gqlFields: NOTE_TARGET_GQL_FIELDS,
|
||||
filter: {
|
||||
id: { eq: noteTargetId },
|
||||
not: { deletedAt: { is: 'NULL' } },
|
||||
},
|
||||
});
|
||||
|
||||
const noteTargetResponse = await makeGraphqlAPIRequest(
|
||||
findNoteTargetsOperation,
|
||||
);
|
||||
|
||||
expect(noteTargetResponse.body.data.noteTargets.edges).toHaveLength(1);
|
||||
expect(
|
||||
noteTargetResponse.body.data.noteTargets.edges[0].node.deletedAt,
|
||||
).not.toBeNull();
|
||||
});
|
||||
|
||||
it('deleteMany should soft delete related noteTargets', async () => {
|
||||
const noteId1 = randomUUID();
|
||||
const noteId2 = randomUUID();
|
||||
const noteTargetId1 = randomUUID();
|
||||
const noteTargetId2 = randomUUID();
|
||||
|
||||
noteIds.push(noteId1, noteId2);
|
||||
noteTargetIds.push(noteTargetId1, noteTargetId2);
|
||||
|
||||
const createNote1Operation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'note',
|
||||
gqlFields: NOTE_GQL_FIELDS,
|
||||
data: { id: noteId1, title: 'Test Note 1 for DeleteMany' },
|
||||
});
|
||||
|
||||
const createNote2Operation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'note',
|
||||
gqlFields: NOTE_GQL_FIELDS,
|
||||
data: { id: noteId2, title: 'Test Note 2 for DeleteMany' },
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
makeGraphqlAPIRequest(createNote1Operation),
|
||||
makeGraphqlAPIRequest(createNote2Operation),
|
||||
]);
|
||||
|
||||
const createNoteTarget1Operation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'noteTarget',
|
||||
gqlFields: NOTE_TARGET_GQL_FIELDS,
|
||||
data: { id: noteTargetId1, noteId: noteId1 },
|
||||
});
|
||||
|
||||
const createNoteTarget2Operation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'noteTarget',
|
||||
gqlFields: NOTE_TARGET_GQL_FIELDS,
|
||||
data: { id: noteTargetId2, noteId: noteId2 },
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
makeGraphqlAPIRequest(createNoteTarget1Operation),
|
||||
makeGraphqlAPIRequest(createNoteTarget2Operation),
|
||||
]);
|
||||
|
||||
const deleteNotesOperation = deleteManyOperationFactory({
|
||||
objectMetadataSingularName: 'note',
|
||||
objectMetadataPluralName: 'notes',
|
||||
gqlFields: NOTE_GQL_FIELDS,
|
||||
filter: { id: { in: [noteId1, noteId2] } },
|
||||
});
|
||||
|
||||
const deleteResponse = await makeGraphqlAPIRequest(deleteNotesOperation);
|
||||
|
||||
expect(deleteResponse.body.data.deleteNotes).toHaveLength(2);
|
||||
|
||||
const findNoteTargetsOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'noteTarget',
|
||||
objectMetadataPluralName: 'noteTargets',
|
||||
gqlFields: NOTE_TARGET_GQL_FIELDS,
|
||||
filter: {
|
||||
id: { in: [noteTargetId1, noteTargetId2] },
|
||||
not: { deletedAt: { is: 'NULL' } },
|
||||
},
|
||||
});
|
||||
|
||||
const noteTargetResponse = await makeGraphqlAPIRequest(
|
||||
findNoteTargetsOperation,
|
||||
);
|
||||
|
||||
expect(noteTargetResponse.body.data.noteTargets.edges).toHaveLength(2);
|
||||
expect(
|
||||
noteTargetResponse.body.data.noteTargets.edges[0].node.deletedAt,
|
||||
).not.toBeNull();
|
||||
expect(
|
||||
noteTargetResponse.body.data.noteTargets.edges[1].node.deletedAt,
|
||||
).not.toBeNull();
|
||||
});
|
||||
|
||||
it('restoreOne should restore related noteTargets', async () => {
|
||||
const noteId = randomUUID();
|
||||
const noteTargetId = randomUUID();
|
||||
|
||||
noteIds.push(noteId);
|
||||
noteTargetIds.push(noteTargetId);
|
||||
|
||||
const createNoteOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'note',
|
||||
gqlFields: NOTE_GQL_FIELDS,
|
||||
data: { id: noteId, title: 'Test Note for RestoreOne' },
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(createNoteOperation);
|
||||
|
||||
const createNoteTargetOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'noteTarget',
|
||||
gqlFields: NOTE_TARGET_GQL_FIELDS,
|
||||
data: { id: noteTargetId, noteId },
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(createNoteTargetOperation);
|
||||
|
||||
const deleteNoteOperation = deleteOneOperationFactory({
|
||||
objectMetadataSingularName: 'note',
|
||||
gqlFields: NOTE_GQL_FIELDS,
|
||||
recordId: noteId,
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(deleteNoteOperation);
|
||||
|
||||
const restoreNoteOperation = restoreOneOperationFactory({
|
||||
objectMetadataSingularName: 'note',
|
||||
gqlFields: NOTE_GQL_FIELDS,
|
||||
recordId: noteId,
|
||||
});
|
||||
|
||||
const restoreResponse = await makeGraphqlAPIRequest(restoreNoteOperation);
|
||||
|
||||
expect(restoreResponse.body.data.restoreNote).toBeDefined();
|
||||
expect(restoreResponse.body.data.restoreNote.deletedAt).toBeNull();
|
||||
|
||||
const findNoteTargetsOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'noteTarget',
|
||||
objectMetadataPluralName: 'noteTargets',
|
||||
gqlFields: NOTE_TARGET_GQL_FIELDS,
|
||||
filter: { id: { eq: noteTargetId } },
|
||||
});
|
||||
|
||||
const noteTargetResponse = await makeGraphqlAPIRequest(
|
||||
findNoteTargetsOperation,
|
||||
);
|
||||
|
||||
expect(noteTargetResponse.body.data.noteTargets.edges).toHaveLength(1);
|
||||
expect(
|
||||
noteTargetResponse.body.data.noteTargets.edges[0].node.deletedAt,
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('restoreMany should restore related noteTargets', async () => {
|
||||
const noteId1 = randomUUID();
|
||||
const noteId2 = randomUUID();
|
||||
const noteTargetId1 = randomUUID();
|
||||
const noteTargetId2 = randomUUID();
|
||||
|
||||
noteIds.push(noteId1, noteId2);
|
||||
noteTargetIds.push(noteTargetId1, noteTargetId2);
|
||||
|
||||
const createNote1Operation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'note',
|
||||
gqlFields: NOTE_GQL_FIELDS,
|
||||
data: { id: noteId1, title: 'Test Note 1 for RestoreMany' },
|
||||
});
|
||||
|
||||
const createNote2Operation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'note',
|
||||
gqlFields: NOTE_GQL_FIELDS,
|
||||
data: { id: noteId2, title: 'Test Note 2 for RestoreMany' },
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
makeGraphqlAPIRequest(createNote1Operation),
|
||||
makeGraphqlAPIRequest(createNote2Operation),
|
||||
]);
|
||||
|
||||
const createNoteTarget1Operation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'noteTarget',
|
||||
gqlFields: NOTE_TARGET_GQL_FIELDS,
|
||||
data: { id: noteTargetId1, noteId: noteId1 },
|
||||
});
|
||||
|
||||
const createNoteTarget2Operation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'noteTarget',
|
||||
gqlFields: NOTE_TARGET_GQL_FIELDS,
|
||||
data: { id: noteTargetId2, noteId: noteId2 },
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
makeGraphqlAPIRequest(createNoteTarget1Operation),
|
||||
makeGraphqlAPIRequest(createNoteTarget2Operation),
|
||||
]);
|
||||
|
||||
const deleteNotesOperation = deleteManyOperationFactory({
|
||||
objectMetadataSingularName: 'note',
|
||||
objectMetadataPluralName: 'notes',
|
||||
gqlFields: NOTE_GQL_FIELDS,
|
||||
filter: { id: { in: [noteId1, noteId2] } },
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(deleteNotesOperation);
|
||||
|
||||
const restoreNotesOperation = restoreManyOperationFactory({
|
||||
objectMetadataSingularName: 'note',
|
||||
objectMetadataPluralName: 'notes',
|
||||
gqlFields: NOTE_GQL_FIELDS,
|
||||
filter: { id: { in: [noteId1, noteId2] } },
|
||||
});
|
||||
|
||||
const restoreResponse = await makeGraphqlAPIRequest(restoreNotesOperation);
|
||||
|
||||
expect(restoreResponse.body.data.restoreNotes).toHaveLength(2);
|
||||
|
||||
const findNoteTargetsOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'noteTarget',
|
||||
objectMetadataPluralName: 'noteTargets',
|
||||
gqlFields: NOTE_TARGET_GQL_FIELDS,
|
||||
filter: { id: { in: [noteTargetId1, noteTargetId2] } },
|
||||
});
|
||||
|
||||
const noteTargetResponse = await makeGraphqlAPIRequest(
|
||||
findNoteTargetsOperation,
|
||||
);
|
||||
|
||||
expect(noteTargetResponse.body.data.noteTargets.edges).toHaveLength(2);
|
||||
expect(
|
||||
noteTargetResponse.body.data.noteTargets.edges[0].node.deletedAt,
|
||||
).toBeNull();
|
||||
expect(
|
||||
noteTargetResponse.body.data.noteTargets.edges[1].node.deletedAt,
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
import { randomUUID } from 'crypto';
|
||||
|
||||
import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util';
|
||||
import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util';
|
||||
import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util';
|
||||
import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util';
|
||||
import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util';
|
||||
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
|
||||
import { restoreManyOperationFactory } from 'test/integration/graphql/utils/restore-many-operation-factory.util';
|
||||
import { restoreOneOperationFactory } from 'test/integration/graphql/utils/restore-one-operation-factory.util';
|
||||
|
||||
const TASK_GQL_FIELDS = `
|
||||
id
|
||||
title
|
||||
deletedAt
|
||||
`;
|
||||
|
||||
const TASK_TARGET_GQL_FIELDS = `
|
||||
id
|
||||
taskId
|
||||
deletedAt
|
||||
`;
|
||||
|
||||
describe('Task post-query hooks', () => {
|
||||
const taskIds: string[] = [];
|
||||
const taskTargetIds: string[] = [];
|
||||
|
||||
afterAll(async () => {
|
||||
if (taskIds.length > 0) {
|
||||
const destroyTasksOperation = destroyManyOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
objectMetadataPluralName: 'tasks',
|
||||
gqlFields: 'id',
|
||||
filter: { id: { in: taskIds } },
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(destroyTasksOperation);
|
||||
}
|
||||
|
||||
if (taskTargetIds.length > 0) {
|
||||
const destroyTaskTargetsOperation = destroyManyOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
objectMetadataPluralName: 'taskTargets',
|
||||
gqlFields: 'id',
|
||||
filter: { id: { in: taskTargetIds } },
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(destroyTaskTargetsOperation);
|
||||
}
|
||||
});
|
||||
|
||||
it('deleteOne should soft delete related taskTargets', async () => {
|
||||
const taskId = randomUUID();
|
||||
const taskTargetId = randomUUID();
|
||||
|
||||
taskIds.push(taskId);
|
||||
taskTargetIds.push(taskTargetId);
|
||||
|
||||
const createTaskOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
data: { id: taskId, title: 'Test Task for DeleteOne' },
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(createTaskOperation);
|
||||
|
||||
const createTaskTargetOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
data: { id: taskTargetId, taskId },
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(createTaskTargetOperation);
|
||||
|
||||
const deleteTaskOperation = deleteOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
recordId: taskId,
|
||||
});
|
||||
|
||||
const deleteResponse = await makeGraphqlAPIRequest(deleteTaskOperation);
|
||||
|
||||
expect(deleteResponse.body.data.deleteTask).toBeDefined();
|
||||
expect(deleteResponse.body.data.deleteTask.deletedAt).not.toBeNull();
|
||||
|
||||
const findTaskTargetsOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
objectMetadataPluralName: 'taskTargets',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
filter: {
|
||||
id: { eq: taskTargetId },
|
||||
not: { deletedAt: { is: 'NULL' } },
|
||||
},
|
||||
});
|
||||
|
||||
const taskTargetResponse = await makeGraphqlAPIRequest(
|
||||
findTaskTargetsOperation,
|
||||
);
|
||||
|
||||
expect(taskTargetResponse.body.data.taskTargets.edges).toHaveLength(1);
|
||||
expect(
|
||||
taskTargetResponse.body.data.taskTargets.edges[0].node.deletedAt,
|
||||
).not.toBeNull();
|
||||
});
|
||||
|
||||
it('deleteMany should soft delete related taskTargets', async () => {
|
||||
const taskId1 = randomUUID();
|
||||
const taskId2 = randomUUID();
|
||||
const taskTargetId1 = randomUUID();
|
||||
const taskTargetId2 = randomUUID();
|
||||
|
||||
taskIds.push(taskId1, taskId2);
|
||||
taskTargetIds.push(taskTargetId1, taskTargetId2);
|
||||
|
||||
const createTask1Operation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
data: { id: taskId1, title: 'Test Task 1 for DeleteMany' },
|
||||
});
|
||||
|
||||
const createTask2Operation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
data: { id: taskId2, title: 'Test Task 2 for DeleteMany' },
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
makeGraphqlAPIRequest(createTask1Operation),
|
||||
makeGraphqlAPIRequest(createTask2Operation),
|
||||
]);
|
||||
|
||||
const createTaskTarget1Operation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
data: { id: taskTargetId1, taskId: taskId1 },
|
||||
});
|
||||
|
||||
const createTaskTarget2Operation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
data: { id: taskTargetId2, taskId: taskId2 },
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
makeGraphqlAPIRequest(createTaskTarget1Operation),
|
||||
makeGraphqlAPIRequest(createTaskTarget2Operation),
|
||||
]);
|
||||
|
||||
const deleteTasksOperation = deleteManyOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
objectMetadataPluralName: 'tasks',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
filter: { id: { in: [taskId1, taskId2] } },
|
||||
});
|
||||
|
||||
const deleteResponse = await makeGraphqlAPIRequest(deleteTasksOperation);
|
||||
|
||||
expect(deleteResponse.body.data.deleteTasks).toHaveLength(2);
|
||||
|
||||
const findTaskTargetsOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
objectMetadataPluralName: 'taskTargets',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
filter: {
|
||||
id: { in: [taskTargetId1, taskTargetId2] },
|
||||
not: { deletedAt: { is: 'NULL' } },
|
||||
},
|
||||
});
|
||||
|
||||
const taskTargetResponse = await makeGraphqlAPIRequest(
|
||||
findTaskTargetsOperation,
|
||||
);
|
||||
|
||||
expect(taskTargetResponse.body.data.taskTargets.edges).toHaveLength(2);
|
||||
expect(
|
||||
taskTargetResponse.body.data.taskTargets.edges[0].node.deletedAt,
|
||||
).not.toBeNull();
|
||||
expect(
|
||||
taskTargetResponse.body.data.taskTargets.edges[1].node.deletedAt,
|
||||
).not.toBeNull();
|
||||
});
|
||||
|
||||
it('restoreOne should restore related taskTargets', async () => {
|
||||
const taskId = randomUUID();
|
||||
const taskTargetId = randomUUID();
|
||||
|
||||
taskIds.push(taskId);
|
||||
taskTargetIds.push(taskTargetId);
|
||||
|
||||
const createTaskOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
data: { id: taskId, title: 'Test Task for RestoreOne' },
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(createTaskOperation);
|
||||
|
||||
const createTaskTargetOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
data: { id: taskTargetId, taskId },
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(createTaskTargetOperation);
|
||||
|
||||
const deleteTaskOperation = deleteOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
recordId: taskId,
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(deleteTaskOperation);
|
||||
|
||||
const restoreTaskOperation = restoreOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
recordId: taskId,
|
||||
});
|
||||
|
||||
const restoreResponse = await makeGraphqlAPIRequest(restoreTaskOperation);
|
||||
|
||||
expect(restoreResponse.body.data.restoreTask).toBeDefined();
|
||||
expect(restoreResponse.body.data.restoreTask.deletedAt).toBeNull();
|
||||
|
||||
const findTaskTargetsOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
objectMetadataPluralName: 'taskTargets',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
filter: { id: { eq: taskTargetId } },
|
||||
});
|
||||
|
||||
const taskTargetResponse = await makeGraphqlAPIRequest(
|
||||
findTaskTargetsOperation,
|
||||
);
|
||||
|
||||
expect(taskTargetResponse.body.data.taskTargets.edges).toHaveLength(1);
|
||||
expect(
|
||||
taskTargetResponse.body.data.taskTargets.edges[0].node.deletedAt,
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('restoreMany should restore related taskTargets', async () => {
|
||||
const taskId1 = randomUUID();
|
||||
const taskId2 = randomUUID();
|
||||
const taskTargetId1 = randomUUID();
|
||||
const taskTargetId2 = randomUUID();
|
||||
|
||||
taskIds.push(taskId1, taskId2);
|
||||
taskTargetIds.push(taskTargetId1, taskTargetId2);
|
||||
|
||||
const createTask1Operation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
data: { id: taskId1, title: 'Test Task 1 for RestoreMany' },
|
||||
});
|
||||
|
||||
const createTask2Operation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
data: { id: taskId2, title: 'Test Task 2 for RestoreMany' },
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
makeGraphqlAPIRequest(createTask1Operation),
|
||||
makeGraphqlAPIRequest(createTask2Operation),
|
||||
]);
|
||||
|
||||
const createTaskTarget1Operation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
data: { id: taskTargetId1, taskId: taskId1 },
|
||||
});
|
||||
|
||||
const createTaskTarget2Operation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
data: { id: taskTargetId2, taskId: taskId2 },
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
makeGraphqlAPIRequest(createTaskTarget1Operation),
|
||||
makeGraphqlAPIRequest(createTaskTarget2Operation),
|
||||
]);
|
||||
|
||||
const deleteTasksOperation = deleteManyOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
objectMetadataPluralName: 'tasks',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
filter: { id: { in: [taskId1, taskId2] } },
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(deleteTasksOperation);
|
||||
|
||||
const restoreTasksOperation = restoreManyOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
objectMetadataPluralName: 'tasks',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
filter: { id: { in: [taskId1, taskId2] } },
|
||||
});
|
||||
|
||||
const restoreResponse = await makeGraphqlAPIRequest(restoreTasksOperation);
|
||||
|
||||
expect(restoreResponse.body.data.restoreTasks).toHaveLength(2);
|
||||
|
||||
const findTaskTargetsOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
objectMetadataPluralName: 'taskTargets',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
filter: { id: { in: [taskTargetId1, taskTargetId2] } },
|
||||
});
|
||||
|
||||
const taskTargetResponse = await makeGraphqlAPIRequest(
|
||||
findTaskTargetsOperation,
|
||||
);
|
||||
|
||||
expect(taskTargetResponse.body.data.taskTargets.edges).toHaveLength(2);
|
||||
expect(
|
||||
taskTargetResponse.body.data.taskTargets.edges[0].node.deletedAt,
|
||||
).toBeNull();
|
||||
expect(
|
||||
taskTargetResponse.body.data.taskTargets.edges[1].node.deletedAt,
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user