From 58ac8c49039cd385041c7930ad056d156eeb8eea Mon Sep 17 00:00:00 2001 From: Max Bischof <106820326+bischofmax@users.noreply.github.com> Date: Mon, 3 Jun 2024 16:08:35 +0200 Subject: [PATCH] BC-7143 Show copy status for column board copy (#5040) --- .../board-do-copy.service.spec.ts | 9 +- .../board-do-copy.service.ts | 10 +- .../recursive-copy.visitor.spec.ts | 136 ++++++++++++++++-- .../recursive-copy.visitor.ts | 12 +- 4 files changed, 150 insertions(+), 17 deletions(-) diff --git a/apps/server/src/modules/board/service/board-do-copy-service/board-do-copy.service.spec.ts b/apps/server/src/modules/board/service/board-do-copy-service/board-do-copy.service.spec.ts index 2a2da5f0e08..3be8034cdde 100644 --- a/apps/server/src/modules/board/service/board-do-copy-service/board-do-copy.service.spec.ts +++ b/apps/server/src/modules/board/service/board-do-copy-service/board-do-copy.service.spec.ts @@ -1,7 +1,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { FileRecordParentType } from '@infra/rabbitmq'; import { ObjectId } from '@mikro-orm/mongodb'; -import { CopyElementType, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; +import { CopyElementType, CopyHelperService, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; import { ContextExternalTool } from '@modules/tool/context-external-tool/domain'; import { ContextExternalToolService } from '@modules/tool/context-external-tool/service'; import { contextExternalToolFactory } from '@modules/tool/context-external-tool/testing'; @@ -48,6 +48,7 @@ describe('recursive board copy visitor', () => { let service: BoardDoCopyService; let contextExternalToolService: DeepMocked; + let copyHelperService: DeepMocked; beforeAll(async () => { module = await Test.createTestingModule({ @@ -57,6 +58,10 @@ describe('recursive board copy visitor', () => { provide: ContextExternalToolService, useValue: createMock(), }, + { + provide: CopyHelperService, + useValue: createMock(), + }, { provide: ToolFeatures, useValue: { @@ -68,6 +73,7 @@ describe('recursive board copy visitor', () => { service = module.get(BoardDoCopyService); contextExternalToolService = module.get(ContextExternalToolService); + copyHelperService = module.get(CopyHelperService); await setupEntities(); }); @@ -92,6 +98,7 @@ describe('recursive board copy visitor', () => { describe('when copying empty column board', () => { const setup = () => { const original = columnBoardFactory.build(); + copyHelperService.deriveStatusFromElements.mockReturnValueOnce(CopyStatusEnum.SUCCESS); return { original, ...setupfileCopyService() }; }; diff --git a/apps/server/src/modules/board/service/board-do-copy-service/board-do-copy.service.ts b/apps/server/src/modules/board/service/board-do-copy-service/board-do-copy.service.ts index f981653ec37..a288bd73d12 100644 --- a/apps/server/src/modules/board/service/board-do-copy-service/board-do-copy.service.ts +++ b/apps/server/src/modules/board/service/board-do-copy-service/board-do-copy.service.ts @@ -1,8 +1,8 @@ +import { CopyHelperService, CopyStatus } from '@modules/copy-helper'; import { ContextExternalToolService } from '@modules/tool/context-external-tool/service'; -import { CopyStatus } from '@modules/copy-helper'; +import { IToolFeatures, ToolFeatures } from '@modules/tool/tool-config'; import { Inject, Injectable } from '@nestjs/common'; import { AnyBoardDo } from '@shared/domain/domainobject'; -import { IToolFeatures, ToolFeatures } from '@modules/tool/tool-config'; import { RecursiveCopyVisitor } from './recursive-copy.visitor'; import { SchoolSpecificFileCopyService } from './school-specific-file-copy.interface'; @@ -15,14 +15,16 @@ export type BoardDoCopyParams = { export class BoardDoCopyService { constructor( @Inject(ToolFeatures) private readonly toolFeatures: IToolFeatures, - private readonly contextExternalToolService: ContextExternalToolService + private readonly contextExternalToolService: ContextExternalToolService, + private readonly copyHelperService: CopyHelperService ) {} public async copy(params: BoardDoCopyParams): Promise { const visitor = new RecursiveCopyVisitor( params.fileCopyService, this.contextExternalToolService, - this.toolFeatures + this.toolFeatures, + this.copyHelperService ); const result = await visitor.copy(params.original); diff --git a/apps/server/src/modules/board/service/board-do-copy-service/recursive-copy.visitor.spec.ts b/apps/server/src/modules/board/service/board-do-copy-service/recursive-copy.visitor.spec.ts index 6b417993976..c53f5327513 100644 --- a/apps/server/src/modules/board/service/board-do-copy-service/recursive-copy.visitor.spec.ts +++ b/apps/server/src/modules/board/service/board-do-copy-service/recursive-copy.visitor.spec.ts @@ -2,9 +2,12 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ContextExternalToolService } from '@modules/tool/context-external-tool/service'; import { IToolFeatures, ToolFeatures } from '@modules/tool/tool-config'; import { Test, TestingModule } from '@nestjs/testing'; -import { CollaborativeTextEditorElement, LinkElement } from '@shared/domain/domainobject'; +import { Card, CollaborativeTextEditorElement, Column, LinkElement } from '@shared/domain/domainobject'; import { + cardFactory, collaborativeTextEditorElementFactory, + columnBoardFactory, + columnFactory, linkElementFactory, mediaBoardFactory, mediaExternalToolElementFactory, @@ -12,7 +15,7 @@ import { setupEntities, } from '@shared/testing'; import { CopyFileDto } from '@src/modules/files-storage-client/dto'; -import { CopyElementType, CopyStatus, CopyStatusEnum } from '../../../copy-helper'; +import { CopyElementType, CopyHelperService, CopyStatus, CopyStatusEnum } from '../../../copy-helper'; import { RecursiveCopyVisitor } from './recursive-copy.visitor'; import { SchoolSpecificFileCopyServiceFactory } from './school-specific-file-copy-service.factory'; import { SchoolSpecificFileCopyService } from './school-specific-file-copy.interface'; @@ -22,6 +25,7 @@ describe(RecursiveCopyVisitor.name, () => { let fileCopyServiceFactory: DeepMocked; let contextExternalToolService: DeepMocked; + let copyHelperService: DeepMocked; let toolFeatures: IToolFeatures; @@ -37,6 +41,10 @@ describe(RecursiveCopyVisitor.name, () => { provide: ContextExternalToolService, useValue: createMock(), }, + { + provide: CopyHelperService, + useValue: createMock(), + }, { provide: ToolFeatures, useValue: { @@ -48,6 +56,7 @@ describe(RecursiveCopyVisitor.name, () => { fileCopyServiceFactory = module.get(SchoolSpecificFileCopyServiceFactory); contextExternalToolService = module.get(ContextExternalToolService); + copyHelperService = module.get(CopyHelperService); toolFeatures = module.get(ToolFeatures); await setupEntities(); @@ -86,7 +95,12 @@ describe(RecursiveCopyVisitor.name, () => { const { fileCopyServiceMock } = setup(); const linkElement = linkElementFactory.build(); - const visitor = new RecursiveCopyVisitor(fileCopyServiceMock, contextExternalToolService, toolFeatures); + const visitor = new RecursiveCopyVisitor( + fileCopyServiceMock, + contextExternalToolService, + toolFeatures, + copyHelperService + ); await visitor.visitLinkElementAsync(linkElement); @@ -99,7 +113,12 @@ describe(RecursiveCopyVisitor.name, () => { const { fileCopyServiceMock, imageUrl } = setup({ withFileCopy: true }); const linkElement = linkElementFactory.build({ imageUrl }); - const visitor = new RecursiveCopyVisitor(fileCopyServiceMock, contextExternalToolService, toolFeatures); + const visitor = new RecursiveCopyVisitor( + fileCopyServiceMock, + contextExternalToolService, + toolFeatures, + copyHelperService + ); await visitor.visitLinkElementAsync(linkElement); @@ -112,7 +131,12 @@ describe(RecursiveCopyVisitor.name, () => { const { fileCopyServiceMock, imageUrl, newFileId } = setup({ withFileCopy: true }); const linkElement = linkElementFactory.build({ imageUrl }); - const visitor = new RecursiveCopyVisitor(fileCopyServiceMock, contextExternalToolService, toolFeatures); + const visitor = new RecursiveCopyVisitor( + fileCopyServiceMock, + contextExternalToolService, + toolFeatures, + copyHelperService + ); await visitor.visitLinkElementAsync(linkElement); const copy = visitor.copyMap.get(linkElement.id) as LinkElement; @@ -126,7 +150,12 @@ describe(RecursiveCopyVisitor.name, () => { const { fileCopyServiceMock } = setup({ withFileCopy: true }); const linkElement = linkElementFactory.build({ imageUrl: `https://abc.de/file/unknown-file-id` }); - const visitor = new RecursiveCopyVisitor(fileCopyServiceMock, contextExternalToolService, toolFeatures); + const visitor = new RecursiveCopyVisitor( + fileCopyServiceMock, + contextExternalToolService, + toolFeatures, + copyHelperService + ); await visitor.visitLinkElementAsync(linkElement); const copy = visitor.copyMap.get(linkElement.id) as LinkElement; @@ -145,7 +174,8 @@ describe(RecursiveCopyVisitor.name, () => { const visitor = new RecursiveCopyVisitor( createMock(), contextExternalToolService, - toolFeatures + toolFeatures, + copyHelperService ); return { @@ -184,7 +214,8 @@ describe(RecursiveCopyVisitor.name, () => { const visitor = new RecursiveCopyVisitor( createMock(), contextExternalToolService, - toolFeatures + toolFeatures, + copyHelperService ); const nowMock = new Date(2020, 1, 1, 0, 0, 0); @@ -228,4 +259,93 @@ describe(RecursiveCopyVisitor.name, () => { expect(copyMapElement?.updatedAt).toEqual(nowMock); }); }); + + describe('visitColumnBoardAsync', () => { + const setup = () => { + const recursiveCopyVisitor = new RecursiveCopyVisitor( + createMock(), + contextExternalToolService, + toolFeatures, + copyHelperService + ); + + const collaborativeTextEditorElement = collaborativeTextEditorElementFactory.build(); + const card = cardFactory.build({ children: [collaborativeTextEditorElement] }); + const boardDo = columnFactory.build({ children: [card] }); + const columnBoard = columnBoardFactory.build({ children: [boardDo] }); + + copyHelperService.deriveStatusFromElements.mockReturnValueOnce(CopyStatusEnum.PARTIAL); + + return { + columnBoard, + recursiveCopyVisitor, + collaborativeTextEditorElement, + }; + }; + + it('should set result map', async () => { + const { columnBoard, recursiveCopyVisitor } = setup(); + + await recursiveCopyVisitor.visitColumnBoardAsync(columnBoard); + + const expectedElements = [ + { + copyEntity: expect.any(Column), + elements: [ + { + copyEntity: expect.any(Card), + elements: [ + { + copyEntity: expect.any(CollaborativeTextEditorElement), + status: CopyStatusEnum.PARTIAL, + type: CopyElementType.COLLABORATIVE_TEXT_EDITOR_ELEMENT, + }, + ], + status: CopyStatusEnum.SUCCESS, + type: CopyElementType.CARD, + }, + ], + status: CopyStatusEnum.SUCCESS, + type: CopyElementType.COLUMN, + }, + ]; + const expectedEntity = { + id: expect.any(String), + title: columnBoard.title, + context: columnBoard.context, + createdAt: expect.any(Date), + updatedAt: expect.any(Date), + isVisible: false, + layout: columnBoard.layout, + }; + const expectedResult = { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + copyEntity: expect.objectContaining(expectedEntity), + type: CopyElementType.COLUMNBOARD, + status: CopyStatusEnum.PARTIAL, + elements: expectedElements, + }; + + expect(recursiveCopyVisitor.resultMap.get(columnBoard.id)).toEqual(expectedResult); + }); + + it('should set copyMap', async () => { + const { columnBoard, recursiveCopyVisitor } = setup(); + + await recursiveCopyVisitor.visitColumnBoardAsync(columnBoard); + + const expectedEntity = { + id: expect.any(String), + title: columnBoard.title, + context: columnBoard.context, + createdAt: expect.any(Date), + updatedAt: expect.any(Date), + isVisible: false, + layout: columnBoard.layout, + }; + + const copyMapElement = recursiveCopyVisitor.copyMap.get(columnBoard.id); + expect(copyMapElement).toEqual(expect.objectContaining(expectedEntity)); + }); + }); }); diff --git a/apps/server/src/modules/board/service/board-do-copy-service/recursive-copy.visitor.ts b/apps/server/src/modules/board/service/board-do-copy-service/recursive-copy.visitor.ts index 0ef5a5b4bb9..bfab5b0cf94 100644 --- a/apps/server/src/modules/board/service/board-do-copy-service/recursive-copy.visitor.ts +++ b/apps/server/src/modules/board/service/board-do-copy-service/recursive-copy.visitor.ts @@ -1,6 +1,6 @@ import { FileRecordParentType } from '@infra/rabbitmq'; import { ObjectId } from '@mikro-orm/mongodb'; -import { CopyElementType, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; +import { CopyElementType, CopyHelperService, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; import { ContextExternalTool } from '@modules/tool/context-external-tool/domain'; import { ContextExternalToolService } from '@modules/tool/context-external-tool/service'; import { IToolFeatures } from '@modules/tool/tool-config'; @@ -33,7 +33,8 @@ export class RecursiveCopyVisitor implements BoardCompositeVisitorAsync { constructor( private readonly fileCopyService: SchoolSpecificFileCopyService, private readonly contextExternalToolService: ContextExternalToolService, - private readonly toolFeatures: IToolFeatures + private readonly toolFeatures: IToolFeatures, + private readonly copyHelperService: CopyHelperService ) {} public async copy(original: AnyBoardDo): Promise { @@ -61,11 +62,14 @@ export class RecursiveCopyVisitor implements BoardCompositeVisitorAsync { layout: original.layout, }); + const copyStatusOfChildren = this.getCopyStatusesForChildrenOf(original); + const status = this.copyHelperService.deriveStatusFromElements(copyStatusOfChildren); + this.resultMap.set(original.id, { copyEntity: copy, type: CopyElementType.COLUMNBOARD, - status: CopyStatusEnum.SUCCESS, - elements: this.getCopyStatusesForChildrenOf(original), + status, + elements: copyStatusOfChildren, }); this.copyMap.set(original.id, copy); }