From 49cdb0feadefc8e02fccc3d53bc56b541aab6e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20=C3=96hlerking?= <103562092+MarvinOehlerkingCap@users.noreply.github.com> Date: Mon, 15 Jan 2024 13:33:36 +0100 Subject: [PATCH] N21-1598 Change calculation of Tool usage (#4689) --- .../modules/board/repo/board-do.repo.spec.ts | 68 +++++++- .../src/modules/board/repo/board-do.repo.ts | 18 ++- .../service/content-element.service.spec.ts | 39 ++++- .../board/service/content-element.service.ts | 9 ++ .../modules/tool/common/common-tool.module.ts | 22 ++- .../common-tool-metadata.service.spec.ts | 149 ++++++++++++++++++ .../service/common-tool-metadata.service.ts | 95 +++++++++++ .../controller/api-test/tool.api.spec.ts | 30 ++-- .../external-tool/external-tool.module.ts | 13 +- .../external-tool-metadata.service.spec.ts | 145 ----------------- .../service/external-tool-metadata.service.ts | 50 ------ .../tool/external-tool/service/index.ts | 1 - .../external-tool/uc/external-tool.uc.spec.ts | 20 +-- .../tool/external-tool/uc/external-tool.uc.ts | 12 +- .../api-test/tool-school.api.spec.ts | 27 ++-- .../school-external-tool.module.ts | 10 +- .../school-external-tool/service/index.ts | 1 - ...ool-external-tool-metadata.service.spec.ts | 93 ----------- .../school-external-tool-metadata.service.ts | 37 ----- .../uc/school-external-tool.uc.spec.ts | 17 +- .../uc/school-external-tool.uc.ts | 11 +- ...ext-external-tool.repo.integration.spec.ts | 96 ++++------- .../context-external-tool.repo.ts | 13 +- 23 files changed, 496 insertions(+), 480 deletions(-) create mode 100644 apps/server/src/modules/tool/common/service/common-tool-metadata.service.spec.ts create mode 100644 apps/server/src/modules/tool/common/service/common-tool-metadata.service.ts delete mode 100644 apps/server/src/modules/tool/external-tool/service/external-tool-metadata.service.spec.ts delete mode 100644 apps/server/src/modules/tool/external-tool/service/external-tool-metadata.service.ts delete mode 100644 apps/server/src/modules/tool/school-external-tool/service/school-external-tool-metadata.service.spec.ts delete mode 100644 apps/server/src/modules/tool/school-external-tool/service/school-external-tool-metadata.service.ts diff --git a/apps/server/src/modules/board/repo/board-do.repo.spec.ts b/apps/server/src/modules/board/repo/board-do.repo.spec.ts index 1d79f961ae8..eca4e5b61fd 100644 --- a/apps/server/src/modules/board/repo/board-do.repo.spec.ts +++ b/apps/server/src/modules/board/repo/board-do.repo.spec.ts @@ -1,13 +1,15 @@ import { createMock } from '@golevelup/ts-jest'; import { MongoMemoryDatabaseModule } from '@infra/database'; import { NotFoundError } from '@mikro-orm/core'; -import { EntityManager } from '@mikro-orm/mongodb'; +import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; +import { DrawingElementAdapterService } from '@modules/tldraw-client/service/drawing-element-adapter.service'; import { ContextExternalToolService } from '@modules/tool/context-external-tool/service'; import { NotFoundException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { AnyBoardDo, BoardExternalReferenceType, Card, Column, ColumnBoard } from '@shared/domain/domainobject'; -import { CardNode, RichTextElementNode } from '@shared/domain/entity'; +import { CardNode, ColumnBoardNode, ExternalToolElementNodeEntity, RichTextElementNode } from '@shared/domain/entity'; +import { EntityId } from '@shared/domain/types'; import { cardFactory, cardNodeFactory, @@ -16,12 +18,16 @@ import { columnBoardNodeFactory, columnFactory, columnNodeFactory, + contextExternalToolEntityFactory, + contextExternalToolFactory, courseFactory, + externalToolElementNodeFactory, fileElementFactory, richTextElementFactory, richTextElementNodeFactory, } from '@shared/testing'; -import { DrawingElementAdapterService } from '@modules/tldraw-client/service/drawing-element-adapter.service'; +import { ContextExternalToolEntity } from '../../tool'; +import { ContextExternalTool } from '../../tool/context-external-tool/domain'; import { BoardDoRepo } from './board-do.repo'; import { BoardNodeRepo } from './board-node.repo'; import { RecursiveDeleteVisitor } from './recursive-delete.vistor'; @@ -267,6 +273,62 @@ describe(BoardDoRepo.name, () => { }); }); + describe('countBoardUsageForExternalTools', () => { + describe('when counting the amount of boards used by the selected tools', () => { + const setup = async () => { + const contextExternalToolId: EntityId = new ObjectId().toHexString(); + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId( + undefined, + contextExternalToolId + ); + const contextExternalToolEntity: ContextExternalToolEntity = contextExternalToolEntityFactory.buildWithId( + undefined, + contextExternalToolId + ); + const otherContextExternalToolEntity: ContextExternalToolEntity = + contextExternalToolEntityFactory.buildWithId(); + + const board: ColumnBoardNode = columnBoardNodeFactory.buildWithId(); + const otherBoard: ColumnBoardNode = columnBoardNodeFactory.buildWithId(); + const card: CardNode = cardNodeFactory.buildWithId({ parent: board }); + const otherCard: CardNode = cardNodeFactory.buildWithId({ parent: otherBoard }); + const externalToolElements: ExternalToolElementNodeEntity[] = externalToolElementNodeFactory.buildListWithId( + 2, + { + parent: card, + contextExternalTool: contextExternalToolEntity, + } + ); + const otherExternalToolElement: ExternalToolElementNodeEntity = externalToolElementNodeFactory.buildWithId({ + parent: otherCard, + contextExternalTool: otherContextExternalToolEntity, + }); + + await em.persistAndFlush([ + board, + otherBoard, + card, + otherCard, + ...externalToolElements, + otherExternalToolElement, + contextExternalToolEntity, + ]); + + return { + contextExternalTool, + }; + }; + + it('should return the amount of boards used by the selected tools', async () => { + const { contextExternalTool } = await setup(); + + const result: number = await repo.countBoardUsageForExternalTools([contextExternalTool]); + + expect(result).toEqual(1); + }); + }); + }); + describe('getAncestorIds', () => { describe('when having only a root boardnode', () => { const setup = async () => { diff --git a/apps/server/src/modules/board/repo/board-do.repo.ts b/apps/server/src/modules/board/repo/board-do.repo.ts index 79ab3dc3c48..dee31a166f3 100644 --- a/apps/server/src/modules/board/repo/board-do.repo.ts +++ b/apps/server/src/modules/board/repo/board-do.repo.ts @@ -1,8 +1,9 @@ import { Utils } from '@mikro-orm/core'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; +import type { ContextExternalTool } from '@modules/tool/context-external-tool/domain'; import { Injectable, NotFoundException } from '@nestjs/common'; import { AnyBoardDo, BoardExternalReference } from '@shared/domain/domainobject'; -import { BoardNode, ColumnBoardNode } from '@shared/domain/entity'; +import { BoardNode, ColumnBoardNode, ExternalToolElementNodeEntity } from '@shared/domain/entity'; import { EntityId } from '@shared/domain/types'; import { BoardDoBuilderImpl } from './board-do.builder-impl'; import { BoardNodeRepo } from './board-node.repo'; @@ -81,6 +82,21 @@ export class BoardDoRepo { return domainObject; } + async countBoardUsageForExternalTools(contextExternalTools: ContextExternalTool[]) { + const toolIds: EntityId[] = contextExternalTools + .map((tool: ContextExternalTool): EntityId | undefined => tool.id) + .filter((id: EntityId | undefined): id is EntityId => !!id); + + const boardNodes: ExternalToolElementNodeEntity[] = await this.em.find(ExternalToolElementNodeEntity, { + contextExternalTool: { $in: toolIds }, + }); + + const boardIds: EntityId[] = boardNodes.map((node: ExternalToolElementNodeEntity): EntityId => node.ancestorIds[0]); + const boardCount: number = new Set(boardIds).size; + + return boardCount; + } + async getAncestorIds(boardDo: AnyBoardDo): Promise { const boardNode = await this.boardNodeRepo.findById(boardDo.id); return boardNode.ancestorIds; diff --git a/apps/server/src/modules/board/service/content-element.service.spec.ts b/apps/server/src/modules/board/service/content-element.service.spec.ts index 5c638270218..5849debb8da 100644 --- a/apps/server/src/modules/board/service/content-element.service.spec.ts +++ b/apps/server/src/modules/board/service/content-element.service.spec.ts @@ -1,4 +1,5 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { ContextExternalTool } from '@modules/tool/context-external-tool/domain'; import { BadRequestException, NotFoundException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { @@ -9,14 +10,16 @@ import { SubmissionContainerElement, } from '@shared/domain/domainobject'; import { InputFormat } from '@shared/domain/types'; -import { drawingElementFactory, setupEntities } from '@shared/testing'; import { cardFactory, + contextExternalToolFactory, + drawingElementFactory, fileElementFactory, linkElementFactory, richTextElementFactory, + setupEntities, submissionContainerElementFactory, -} from '@shared/testing/factory/domainobject'; +} from '@shared/testing'; import { DrawingContentBody, FileContentBody, @@ -118,7 +121,7 @@ describe(ContentElementService.name, () => { }); describe('findParentOfId', () => { - describe('when parent is a vaid node', () => { + describe('when parent is a valid node', () => { const setup = () => { const card = cardFactory.build(); const element = richTextElementFactory.build(); @@ -154,6 +157,36 @@ describe(ContentElementService.name, () => { }); }); + describe('countBoardUsageForExternalTools', () => { + describe('when counting the amount of boards used by tools', () => { + const setup = () => { + const contextExternalTools: ContextExternalTool[] = contextExternalToolFactory.buildListWithId(3); + + boardDoRepo.countBoardUsageForExternalTools.mockResolvedValueOnce(3); + + return { + contextExternalTools, + }; + }; + + it('should count the usages', async () => { + const { contextExternalTools } = setup(); + + await service.countBoardUsageForExternalTools(contextExternalTools); + + expect(boardDoRepo.countBoardUsageForExternalTools).toHaveBeenCalledWith(contextExternalTools); + }); + + it('should return the amount of boards', async () => { + const { contextExternalTools } = setup(); + + const result: number = await service.countBoardUsageForExternalTools(contextExternalTools); + + expect(result).toEqual(3); + }); + }); + }); + describe('create', () => { describe('when creating a content element of type', () => { const setup = () => { diff --git a/apps/server/src/modules/board/service/content-element.service.ts b/apps/server/src/modules/board/service/content-element.service.ts index b6ac32434f3..fd4e51029b8 100644 --- a/apps/server/src/modules/board/service/content-element.service.ts +++ b/apps/server/src/modules/board/service/content-element.service.ts @@ -1,3 +1,4 @@ +import type { ContextExternalTool } from '@modules/tool/context-external-tool/domain'; import { Injectable, NotFoundException } from '@nestjs/common'; import { AnyBoardDo, @@ -40,10 +41,18 @@ export class ContentElementService { return parent; } + async countBoardUsageForExternalTools(contextExternalTools: ContextExternalTool[]): Promise { + const count: number = await this.boardDoRepo.countBoardUsageForExternalTools(contextExternalTools); + + return count; + } + async create(parent: Card | SubmissionItem, type: ContentElementType): Promise { const element = this.contentElementFactory.build(type); parent.addChild(element); + await this.boardDoRepo.save(parent.children, parent); + return element; } diff --git a/apps/server/src/modules/tool/common/common-tool.module.ts b/apps/server/src/modules/tool/common/common-tool.module.ts index 57375c67e96..83f42275b54 100644 --- a/apps/server/src/modules/tool/common/common-tool.module.ts +++ b/apps/server/src/modules/tool/common/common-tool.module.ts @@ -1,13 +1,27 @@ +import { BoardModule } from '@modules/board'; import { LegacySchoolModule } from '@modules/legacy-school'; -import { Module } from '@nestjs/common'; +import { forwardRef, Module } from '@nestjs/common'; import { ContextExternalToolRepo, SchoolExternalToolRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; import { CommonToolService, CommonToolValidationService } from './service'; +import { CommonToolMetadataService } from './service/common-tool-metadata.service'; @Module({ - imports: [LoggerModule, LegacySchoolModule], + imports: [LoggerModule, LegacySchoolModule, forwardRef(() => BoardModule)], // TODO: make deletion of entities cascading, adjust ExternalToolService.deleteExternalTool and remove the repos from here - providers: [CommonToolService, CommonToolValidationService, SchoolExternalToolRepo, ContextExternalToolRepo], - exports: [CommonToolService, CommonToolValidationService, SchoolExternalToolRepo, ContextExternalToolRepo], + providers: [ + CommonToolService, + CommonToolValidationService, + SchoolExternalToolRepo, + ContextExternalToolRepo, + CommonToolMetadataService, + ], + exports: [ + CommonToolService, + CommonToolValidationService, + SchoolExternalToolRepo, + ContextExternalToolRepo, + CommonToolMetadataService, + ], }) export class CommonToolModule {} diff --git a/apps/server/src/modules/tool/common/service/common-tool-metadata.service.spec.ts b/apps/server/src/modules/tool/common/service/common-tool-metadata.service.spec.ts new file mode 100644 index 00000000000..794e08f56eb --- /dev/null +++ b/apps/server/src/modules/tool/common/service/common-tool-metadata.service.spec.ts @@ -0,0 +1,149 @@ +import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { ObjectId } from '@mikro-orm/mongodb'; +import { ContentElementService } from '@modules/board'; +import { Test, TestingModule } from '@nestjs/testing'; +import { ContextExternalToolRepo, SchoolExternalToolRepo } from '@shared/repo'; +import { contextExternalToolFactory, schoolExternalToolFactory } from '@shared/testing'; +import { ContextExternalTool } from '../../context-external-tool/domain'; +import { ExternalToolMetadata } from '../../external-tool/domain'; +import { SchoolExternalTool, SchoolExternalToolMetadata } from '../../school-external-tool/domain'; +import { CommonToolMetadataService } from './common-tool-metadata.service'; + +describe(CommonToolMetadataService.name, () => { + let module: TestingModule; + let service: CommonToolMetadataService; + + let schoolExternalToolRepo: DeepMocked; + let contextExternalToolRepo: DeepMocked; + let contentElementService: DeepMocked; + + beforeAll(async () => { + module = await Test.createTestingModule({ + providers: [ + CommonToolMetadataService, + { + provide: SchoolExternalToolRepo, + useValue: createMock(), + }, + { + provide: ContextExternalToolRepo, + useValue: createMock(), + }, + { + provide: ContentElementService, + useValue: createMock(), + }, + ], + }).compile(); + + service = module.get(CommonToolMetadataService); + schoolExternalToolRepo = module.get(SchoolExternalToolRepo); + contextExternalToolRepo = module.get(ContextExternalToolRepo); + contentElementService = module.get(ContentElementService); + }); + + afterAll(async () => { + await module.close(); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + describe('getMetadataForExternalTool', () => { + describe('when the tool has no usages', () => { + const setup = () => { + schoolExternalToolRepo.findByExternalToolId.mockResolvedValueOnce([]); + }; + + it('should return 0 usages for all contexts', async () => { + setup(); + + const result: ExternalToolMetadata = await service.getMetadataForExternalTool(new ObjectId().toHexString()); + + expect(result).toEqual({ + schoolExternalToolCount: 0, + contextExternalToolCountPerContext: { + course: 0, + boardElement: 0, + }, + }); + }); + }); + + describe('when the tool has usages in all contexts', () => { + const setup = () => { + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.buildWithId(); + const contextExternalTools: ContextExternalTool[] = contextExternalToolFactory.buildListWithId(2); + + schoolExternalToolRepo.findByExternalToolId.mockResolvedValueOnce([schoolExternalTool]); + contextExternalToolRepo.findBySchoolToolIdsAndContextType.mockResolvedValueOnce(contextExternalTools); + contextExternalToolRepo.findBySchoolToolIdsAndContextType.mockResolvedValueOnce(contextExternalTools); + contentElementService.countBoardUsageForExternalTools.mockResolvedValueOnce(3); + }; + + it('should return the amount of usages for all contexts', async () => { + setup(); + + const result: ExternalToolMetadata = await service.getMetadataForExternalTool(new ObjectId().toHexString()); + + expect(result).toEqual({ + schoolExternalToolCount: 1, + contextExternalToolCountPerContext: { + course: 2, + boardElement: 3, + }, + }); + }); + }); + }); + + describe('getMetadataForSchoolExternalTool', () => { + describe('when the tool has no usages', () => { + const setup = () => { + contextExternalToolRepo.findBySchoolToolIdsAndContextType.mockResolvedValueOnce([]); + contextExternalToolRepo.findBySchoolToolIdsAndContextType.mockResolvedValueOnce([]); + }; + + it('should return 0 usages for all contexts', async () => { + setup(); + + const result: SchoolExternalToolMetadata = await service.getMetadataForSchoolExternalTool( + new ObjectId().toHexString() + ); + + expect(result).toEqual({ + contextExternalToolCountPerContext: { + course: 0, + boardElement: 0, + }, + }); + }); + }); + + describe('when the tool has usages in all contexts', () => { + const setup = () => { + const contextExternalTools: ContextExternalTool[] = contextExternalToolFactory.buildListWithId(2); + + contextExternalToolRepo.findBySchoolToolIdsAndContextType.mockResolvedValueOnce(contextExternalTools); + contextExternalToolRepo.findBySchoolToolIdsAndContextType.mockResolvedValueOnce(contextExternalTools); + contentElementService.countBoardUsageForExternalTools.mockResolvedValueOnce(3); + }; + + it('should return the amount of usages for all contexts', async () => { + setup(); + + const result: SchoolExternalToolMetadata = await service.getMetadataForSchoolExternalTool( + new ObjectId().toHexString() + ); + + expect(result).toEqual({ + contextExternalToolCountPerContext: { + course: 2, + boardElement: 3, + }, + }); + }); + }); + }); +}); diff --git a/apps/server/src/modules/tool/common/service/common-tool-metadata.service.ts b/apps/server/src/modules/tool/common/service/common-tool-metadata.service.ts new file mode 100644 index 00000000000..9ed04389a5d --- /dev/null +++ b/apps/server/src/modules/tool/common/service/common-tool-metadata.service.ts @@ -0,0 +1,95 @@ +import { ContentElementService } from '@modules/board'; +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { EntityId } from '@shared/domain/types'; +import { ContextExternalToolRepo, SchoolExternalToolRepo } from '@shared/repo'; +import { ContextExternalTool } from '../../context-external-tool/domain'; +import { ContextExternalToolType } from '../../context-external-tool/entity'; +import { ExternalToolMetadata } from '../../external-tool/domain'; +import { SchoolExternalTool, SchoolExternalToolMetadata } from '../../school-external-tool/domain'; +import { ToolContextType } from '../enum'; +import { ToolContextMapper } from '../mapper/tool-context.mapper'; + +@Injectable() +export class CommonToolMetadataService { + constructor( + private readonly schoolToolRepo: SchoolExternalToolRepo, + private readonly contextToolRepo: ContextExternalToolRepo, + @Inject(forwardRef(() => ContentElementService)) + private readonly contentElementService: ContentElementService + ) {} + + async getMetadataForExternalTool(toolId: EntityId): Promise { + const schoolExternalTools: SchoolExternalTool[] = await this.schoolToolRepo.findByExternalToolId(toolId); + + const schoolExternalToolIds: string[] = schoolExternalTools.map( + (schoolExternalTool: SchoolExternalTool): string => + // We can be sure that the repo returns the id + schoolExternalTool.id as string + ); + + const externalToolMetadata: ExternalToolMetadata = await this.getMetadata(schoolExternalToolIds); + + return externalToolMetadata; + } + + async getMetadataForSchoolExternalTool(schoolExternalToolId: EntityId): Promise { + const externalToolMetadata: ExternalToolMetadata = await this.getMetadata([schoolExternalToolId]); + + const schoolExternalToolMetadata: SchoolExternalToolMetadata = new SchoolExternalToolMetadata({ + contextExternalToolCountPerContext: externalToolMetadata.contextExternalToolCountPerContext, + }); + + return schoolExternalToolMetadata; + } + + private async getMetadata(schoolExternalToolIds: EntityId[]): Promise { + const contextExternalToolCount: Record = { + [ContextExternalToolType.BOARD_ELEMENT]: 0, + [ContextExternalToolType.COURSE]: 0, + }; + + if (schoolExternalToolIds.length) { + await Promise.all( + Object.values(ToolContextType).map(async (contextType: ToolContextType): Promise => { + const type: ContextExternalToolType = ToolContextMapper.contextMapping[contextType]; + + const count: number = await this.countUsageForType(schoolExternalToolIds, type); + + contextExternalToolCount[type] = count; + }) + ); + } + + const externalToolMetadata: ExternalToolMetadata = new ExternalToolMetadata({ + schoolExternalToolCount: schoolExternalToolIds.length, + contextExternalToolCountPerContext: contextExternalToolCount, + }); + + return externalToolMetadata; + } + + private async countUsageForType( + schoolExternalToolIds: string[], + contextType: ContextExternalToolType + ): Promise { + const contextExternalTools: ContextExternalTool[] = await this.contextToolRepo.findBySchoolToolIdsAndContextType( + schoolExternalToolIds, + contextType + ); + + let count = 0; + if (contextExternalTools.length) { + if (contextType === ContextExternalToolType.BOARD_ELEMENT) { + count = await this.contentElementService.countBoardUsageForExternalTools(contextExternalTools); + } else { + const contextIds: EntityId[] = contextExternalTools.map( + (contextExternalTool: ContextExternalTool): EntityId => contextExternalTool.contextRef.id + ); + + count = new Set(contextIds).size; + } + } + + return count; + } +} diff --git a/apps/server/src/modules/tool/external-tool/controller/api-test/tool.api.spec.ts b/apps/server/src/modules/tool/external-tool/controller/api-test/tool.api.spec.ts index e41be01e880..7de0817cc28 100644 --- a/apps/server/src/modules/tool/external-tool/controller/api-test/tool.api.spec.ts +++ b/apps/server/src/modules/tool/external-tool/controller/api-test/tool.api.spec.ts @@ -3,17 +3,19 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { ServerTestModule } from '@modules/server'; import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import { SchoolEntity } from '@shared/domain/entity'; +import { ColumnBoardNode, ExternalToolElementNodeEntity, SchoolEntity } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { - TestApiClient, - UserAndAccountTestFactory, cleanupCollections, + columnBoardNodeFactory, contextExternalToolEntityFactory, + externalToolElementNodeFactory, externalToolEntityFactory, externalToolFactory, schoolExternalToolEntityFactory, schoolFactory, + TestApiClient, + UserAndAccountTestFactory, } from '@shared/testing'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; @@ -26,7 +28,6 @@ import { } from '../../../common/enum'; import { ContextExternalToolEntity, ContextExternalToolType } from '../../../context-external-tool/entity'; import { SchoolExternalToolEntity } from '../../../school-external-tool/entity'; -import { ExternalToolMetadata } from '../../domain'; import { ExternalToolEntity } from '../../entity'; import { ExternalToolCreateParams, @@ -669,6 +670,7 @@ describe('ToolController (API)', () => { const courseTools: ContextExternalToolEntity[] = contextExternalToolEntityFactory.buildList(3, { schoolTool: schoolExternalToolEntitys[0], contextType: ContextExternalToolType.COURSE, + contextId: new ObjectId().toHexString(), }); const boardTools: ContextExternalToolEntity[] = contextExternalToolEntityFactory.buildList(2, { @@ -677,10 +679,14 @@ describe('ToolController (API)', () => { contextId: new ObjectId().toHexString(), }); - const externalToolMetadata: ExternalToolMetadata = new ExternalToolMetadata({ - schoolExternalToolCount: 2, - contextExternalToolCountPerContext: { course: 3, boardElement: 2 }, - }); + const board: ColumnBoardNode = columnBoardNodeFactory.buildWithId(); + const externalToolElements: ExternalToolElementNodeEntity[] = externalToolElementNodeFactory.buildListWithId( + 2, + { + contextExternalTool: boardTools[0], + parent: board, + } + ); const { adminUser, adminAccount } = UserAndAccountTestFactory.buildAdmin({}, [Permission.TOOL_ADMIN]); await em.persistAndFlush([ @@ -691,12 +697,14 @@ describe('ToolController (API)', () => { ...schoolExternalToolEntitys, ...courseTools, ...boardTools, + board, + ...externalToolElements, ]); em.clear(); const loggedInClient: TestApiClient = await testApiClient.login(adminAccount); - return { loggedInClient, toolId, externalToolEntity, externalToolMetadata }; + return { loggedInClient, toolId, externalToolEntity }; }; it('should return the metadata of externalTool', async () => { @@ -708,8 +716,8 @@ describe('ToolController (API)', () => { expect(response.body).toEqual({ schoolExternalToolCount: 2, contextExternalToolCountPerContext: { - course: 3, - boardElement: 2, + course: 1, + boardElement: 1, }, }); }); diff --git a/apps/server/src/modules/tool/external-tool/external-tool.module.ts b/apps/server/src/modules/tool/external-tool/external-tool.module.ts index a84734fecb2..8f5a194a779 100644 --- a/apps/server/src/modules/tool/external-tool/external-tool.module.ts +++ b/apps/server/src/modules/tool/external-tool/external-tool.module.ts @@ -1,12 +1,13 @@ +import { EncryptionModule } from '@infra/encryption'; +import { OauthProviderServiceModule } from '@infra/oauth-provider'; import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; -import { LoggerModule } from '@src/core/logger'; -import { OauthProviderServiceModule } from '@infra/oauth-provider'; -import { EncryptionModule } from '@infra/encryption'; import { ExternalToolRepo } from '@shared/repo'; +import { LoggerModule } from '@src/core/logger'; +import { CommonToolModule } from '../common'; +import { ToolContextMapper } from '../common/mapper/tool-context.mapper'; import { ToolConfigModule } from '../tool-config.module'; import { ExternalToolMetadataMapper } from './mapper'; -import { ToolContextMapper } from '../common/mapper/tool-context.mapper'; import { ExternalToolConfigurationService, ExternalToolLogoService, @@ -15,9 +16,7 @@ import { ExternalToolServiceMapper, ExternalToolValidationService, ExternalToolVersionIncrementService, - ExternalToolMetadataService, } from './service'; -import { CommonToolModule } from '../common'; @Module({ imports: [CommonToolModule, ToolConfigModule, LoggerModule, OauthProviderServiceModule, EncryptionModule, HttpModule], @@ -30,7 +29,6 @@ import { CommonToolModule } from '../common'; ExternalToolConfigurationService, ExternalToolLogoService, ExternalToolRepo, - ExternalToolMetadataService, ExternalToolMetadataMapper, ToolContextMapper, ], @@ -40,7 +38,6 @@ import { CommonToolModule } from '../common'; ExternalToolVersionIncrementService, ExternalToolConfigurationService, ExternalToolLogoService, - ExternalToolMetadataService, ], }) export class ExternalToolModule {} diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-metadata.service.spec.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-metadata.service.spec.ts deleted file mode 100644 index 4753cc805f4..00000000000 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-metadata.service.spec.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { ObjectId } from '@mikro-orm/mongodb'; -import { Test, TestingModule } from '@nestjs/testing'; -import { ContextExternalToolRepo, SchoolExternalToolRepo } from '@shared/repo'; -import { externalToolFactory, legacySchoolDoFactory, schoolExternalToolFactory } from '@shared/testing'; -import { ContextExternalToolType } from '../../context-external-tool/entity'; -import { SchoolExternalTool } from '../../school-external-tool/domain'; -import { ExternalTool, ExternalToolMetadata } from '../domain'; -import { ExternalToolMetadataService } from './external-tool-metadata.service'; - -describe('ExternalToolMetadataService', () => { - let module: TestingModule; - let service: ExternalToolMetadataService; - - let schoolExternalToolRepo: DeepMocked; - let contextExternalToolRepo: DeepMocked; - - beforeAll(async () => { - module = await Test.createTestingModule({ - providers: [ - ExternalToolMetadataService, - { - provide: SchoolExternalToolRepo, - useValue: createMock(), - }, - { - provide: ContextExternalToolRepo, - useValue: createMock(), - }, - ], - }).compile(); - - service = module.get(ExternalToolMetadataService); - schoolExternalToolRepo = module.get(SchoolExternalToolRepo); - contextExternalToolRepo = module.get(ContextExternalToolRepo); - }); - - afterAll(async () => { - await module.close(); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - describe('getMetadata', () => { - describe('when externalToolId is given', () => { - const setup = () => { - const toolId: string = new ObjectId().toHexString(); - - const school = legacySchoolDoFactory.buildWithId(); - const school1 = legacySchoolDoFactory.buildWithId(); - - const schoolToolId: string = new ObjectId().toHexString(); - const schoolToolId1: string = new ObjectId().toHexString(); - - const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.buildWithId({ - toolId, - schoolId: school.id, - id: schoolToolId, - }); - const schoolExternalTool1: SchoolExternalTool = schoolExternalToolFactory.buildWithId( - { toolId, schoolId: school1.id, id: schoolToolId1 }, - schoolToolId1 - ); - - const externalToolMetadata: ExternalToolMetadata = new ExternalToolMetadata({ - schoolExternalToolCount: 2, - contextExternalToolCountPerContext: { course: 3, boardElement: 3 }, - }); - - schoolExternalToolRepo.findByExternalToolId.mockResolvedValue([schoolExternalTool, schoolExternalTool1]); - contextExternalToolRepo.countBySchoolToolIdsAndContextType.mockResolvedValue(3); - - return { - toolId, - externalToolMetadata, - schoolExternalTool, - schoolExternalTool1, - }; - }; - - it('should call the repo to get schoolExternalTools by externalToolId', async () => { - const { toolId } = setup(); - - await service.getMetadata(toolId); - - expect(schoolExternalToolRepo.findByExternalToolId).toHaveBeenCalledWith(toolId); - }); - - it('should call the repo to count contextExternalTools by schoolExternalToolId and context', async () => { - const { toolId, schoolExternalTool, schoolExternalTool1 } = setup(); - - await service.getMetadata(toolId); - - expect(contextExternalToolRepo.countBySchoolToolIdsAndContextType).toHaveBeenCalledWith( - ContextExternalToolType.COURSE, - [schoolExternalTool.id, schoolExternalTool1.id] - ); - expect(contextExternalToolRepo.countBySchoolToolIdsAndContextType).toHaveBeenCalledWith( - ContextExternalToolType.BOARD_ELEMENT, - [schoolExternalTool.id, schoolExternalTool1.id] - ); - expect(contextExternalToolRepo.countBySchoolToolIdsAndContextType).toHaveBeenCalledTimes(2); - }); - - it('should return externalToolMetadata', async () => { - const { toolId, externalToolMetadata } = setup(); - - const result: ExternalToolMetadata = await service.getMetadata(toolId); - - expect(result).toEqual(externalToolMetadata); - }); - }); - - describe('when no related school external tool was found', () => { - const setup = () => { - const toolId: string = new ObjectId().toHexString(); - const externalToolEntity: ExternalTool = externalToolFactory.buildWithId(undefined, toolId); - - const externalToolMetadata: ExternalToolMetadata = new ExternalToolMetadata({ - schoolExternalToolCount: 0, - contextExternalToolCountPerContext: { course: 0, boardElement: 0 }, - }); - - schoolExternalToolRepo.findByExternalToolId.mockResolvedValue([]); - contextExternalToolRepo.countBySchoolToolIdsAndContextType.mockResolvedValue(0); - - return { - toolId, - externalToolEntity, - externalToolMetadata, - }; - }; - - it('should return empty externalToolMetadata', async () => { - const { toolId, externalToolMetadata } = setup(); - - const result: ExternalToolMetadata = await service.getMetadata(toolId); - - expect(result).toEqual(externalToolMetadata); - }); - }); - }); -}); diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-metadata.service.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-metadata.service.ts deleted file mode 100644 index 5d99ca73935..00000000000 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-metadata.service.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { EntityId } from '@shared/domain/types'; -import { ContextExternalToolRepo, SchoolExternalToolRepo } from '@shared/repo'; -import { ToolContextType } from '../../common/enum'; -import { ToolContextMapper } from '../../common/mapper/tool-context.mapper'; -import { ContextExternalToolType } from '../../context-external-tool/entity'; -import { SchoolExternalTool } from '../../school-external-tool/domain'; -import { ExternalToolMetadata } from '../domain'; - -@Injectable() -export class ExternalToolMetadataService { - constructor( - private readonly schoolToolRepo: SchoolExternalToolRepo, - private readonly contextToolRepo: ContextExternalToolRepo - ) {} - - async getMetadata(toolId: EntityId): Promise { - const schoolExternalTools: SchoolExternalTool[] = await this.schoolToolRepo.findByExternalToolId(toolId); - - const schoolExternalToolIds: string[] = schoolExternalTools.map( - (schoolExternalTool: SchoolExternalTool): string => - // We can be sure that the repo returns the id - schoolExternalTool.id as string - ); - const contextExternalToolCount: Record = { - [ContextExternalToolType.BOARD_ELEMENT]: 0, - [ContextExternalToolType.COURSE]: 0, - }; - if (schoolExternalTools.length >= 1) { - await Promise.all( - Object.values(ToolContextType).map(async (contextType: ToolContextType): Promise => { - const type: ContextExternalToolType = ToolContextMapper.contextMapping[contextType]; - - const countPerContext: number = await this.contextToolRepo.countBySchoolToolIdsAndContextType( - type, - schoolExternalToolIds - ); - contextExternalToolCount[type] = countPerContext; - }) - ); - } - - const externalToolMetadata: ExternalToolMetadata = new ExternalToolMetadata({ - schoolExternalToolCount: schoolExternalTools.length, - contextExternalToolCountPerContext: contextExternalToolCount, - }); - - return externalToolMetadata; - } -} diff --git a/apps/server/src/modules/tool/external-tool/service/index.ts b/apps/server/src/modules/tool/external-tool/service/index.ts index e2a936d158b..f2290ca8969 100644 --- a/apps/server/src/modules/tool/external-tool/service/index.ts +++ b/apps/server/src/modules/tool/external-tool/service/index.ts @@ -5,4 +5,3 @@ export * from './external-tool-validation.service'; export * from './external-tool-parameter-validation.service'; export * from './external-tool-configuration.service'; export * from './external-tool-logo.service'; -export * from './external-tool-metadata.service'; diff --git a/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.spec.ts b/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.spec.ts index 47bebac803b..3d5bfe5d6d8 100644 --- a/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.spec.ts +++ b/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.spec.ts @@ -10,13 +10,9 @@ import { IFindOptions, Permission, SortOrder } from '@shared/domain/interface'; import { roleFactory, setupEntities, userFactory } from '@shared/testing'; import { externalToolFactory, oauth2ToolConfigFactory } from '@shared/testing/factory'; import { ExternalToolSearchQuery } from '../../common/interface'; +import { CommonToolMetadataService } from '../../common/service/common-tool-metadata.service'; import { ExternalTool, ExternalToolMetadata, Oauth2ToolConfig } from '../domain'; -import { - ExternalToolLogoService, - ExternalToolMetadataService, - ExternalToolService, - ExternalToolValidationService, -} from '../service'; +import { ExternalToolLogoService, ExternalToolService, ExternalToolValidationService } from '../service'; import { ExternalToolUpdate } from './dto'; import { ExternalToolUc } from './external-tool.uc'; @@ -29,7 +25,7 @@ describe('ExternalToolUc', () => { let authorizationService: DeepMocked; let toolValidationService: DeepMocked; let logoService: DeepMocked; - let externalToolMetadataService: DeepMocked; + let commonToolMetadataService: DeepMocked; beforeAll(async () => { await setupEntities(); @@ -54,8 +50,8 @@ describe('ExternalToolUc', () => { useValue: createMock(), }, { - provide: ExternalToolMetadataService, - useValue: createMock(), + provide: CommonToolMetadataService, + useValue: createMock(), }, ], }).compile(); @@ -65,7 +61,7 @@ describe('ExternalToolUc', () => { authorizationService = module.get(AuthorizationService); toolValidationService = module.get(ExternalToolValidationService); logoService = module.get(ExternalToolLogoService); - externalToolMetadataService = module.get(ExternalToolMetadataService); + commonToolMetadataService = module.get(CommonToolMetadataService); }); afterAll(async () => { @@ -551,7 +547,7 @@ describe('ExternalToolUc', () => { contextExternalToolCountPerContext: { course: 3, boardElement: 3 }, }); - externalToolMetadataService.getMetadata.mockResolvedValue(externalToolMetadata); + commonToolMetadataService.getMetadataForExternalTool.mockResolvedValue(externalToolMetadata); const user: User = userFactory.buildWithId(); const currentUser: ICurrentUser = { userId: user.id } as ICurrentUser; @@ -571,7 +567,7 @@ describe('ExternalToolUc', () => { await uc.getMetadataForExternalTool(currentUser.userId, toolId); - expect(externalToolMetadataService.getMetadata).toHaveBeenCalledWith(toolId); + expect(commonToolMetadataService.getMetadataForExternalTool).toHaveBeenCalledWith(toolId); }); it('return metadata of external tool', async () => { diff --git a/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.ts b/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.ts index c48454adb07..4897d34d07e 100644 --- a/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.ts +++ b/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.ts @@ -5,13 +5,9 @@ import { User } from '@shared/domain/entity'; import { IFindOptions, Permission } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; import { ExternalToolSearchQuery } from '../../common/interface'; +import { CommonToolMetadataService } from '../../common/service/common-tool-metadata.service'; import { ExternalTool, ExternalToolConfig, ExternalToolMetadata } from '../domain'; -import { - ExternalToolLogoService, - ExternalToolMetadataService, - ExternalToolService, - ExternalToolValidationService, -} from '../service'; +import { ExternalToolLogoService, ExternalToolService, ExternalToolValidationService } from '../service'; import { ExternalToolCreate, ExternalToolUpdate } from './dto'; @Injectable() @@ -21,7 +17,7 @@ export class ExternalToolUc { private readonly authorizationService: AuthorizationService, private readonly toolValidationService: ExternalToolValidationService, private readonly externalToolLogoService: ExternalToolLogoService, - private readonly externalToolMetadataService: ExternalToolMetadataService + private readonly commonToolMetadataService: CommonToolMetadataService ) {} async createExternalTool(userId: EntityId, externalToolCreate: ExternalToolCreate): Promise { @@ -87,7 +83,7 @@ export class ExternalToolUc { // TODO N21-1496: Change External Tools to use authorizationService.checkPermission await this.ensurePermission(userId, Permission.TOOL_ADMIN); - const metadata: ExternalToolMetadata = await this.externalToolMetadataService.getMetadata(toolId); + const metadata: ExternalToolMetadata = await this.commonToolMetadataService.getMetadataForExternalTool(toolId); return metadata; } diff --git a/apps/server/src/modules/tool/school-external-tool/controller/api-test/tool-school.api.spec.ts b/apps/server/src/modules/tool/school-external-tool/controller/api-test/tool-school.api.spec.ts index 7a80650eb76..459299809fb 100644 --- a/apps/server/src/modules/tool/school-external-tool/controller/api-test/tool-school.api.spec.ts +++ b/apps/server/src/modules/tool/school-external-tool/controller/api-test/tool-school.api.spec.ts @@ -3,17 +3,19 @@ import { ObjectId } from '@mikro-orm/mongodb'; import { ServerTestModule } from '@modules/server'; import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import { Account, SchoolEntity, User } from '@shared/domain/entity'; +import { Account, ColumnBoardNode, ExternalToolElementNodeEntity, SchoolEntity, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { - TestApiClient, - UserAndAccountTestFactory, accountFactory, + columnBoardNodeFactory, contextExternalToolEntityFactory, customParameterEntityFactory, + externalToolElementNodeFactory, externalToolEntityFactory, schoolExternalToolEntityFactory, schoolFactory, + TestApiClient, + UserAndAccountTestFactory, userFactory, } from '@shared/testing'; import { schoolToolConfigurationStatusFactory } from '@shared/testing/factory'; @@ -562,9 +564,14 @@ describe('ToolSchoolController (API)', () => { contextId: new ObjectId().toHexString(), }); - const schoolExternalToolMetadata: SchoolExternalToolMetadataResponse = new SchoolExternalToolMetadataResponse({ - contextExternalToolCountPerContext: { course: 3, boardElement: 2 }, - }); + const board: ColumnBoardNode = columnBoardNodeFactory.buildWithId(); + const externalToolElements: ExternalToolElementNodeEntity[] = externalToolElementNodeFactory.buildListWithId( + 2, + { + contextExternalTool: boardExternalToolEntitys[0], + parent: board, + } + ); const { adminUser, adminAccount } = UserAndAccountTestFactory.buildAdmin({ school }, [ Permission.SCHOOL_TOOL_ADMIN, @@ -576,12 +583,14 @@ describe('ToolSchoolController (API)', () => { schoolExternalToolEntity, ...courseExternalToolEntitys, ...boardExternalToolEntitys, + board, + ...externalToolElements, ]); em.clear(); const loggedInClient: TestApiClient = await testApiClient.login(adminAccount); - return { loggedInClient, schoolExternalToolEntity, schoolExternalToolMetadata }; + return { loggedInClient, schoolExternalToolEntity }; }; it('should return the metadata of schoolExternalTool', async () => { @@ -592,8 +601,8 @@ describe('ToolSchoolController (API)', () => { expect(response.statusCode).toEqual(HttpStatus.OK); expect(response.body).toEqual({ contextExternalToolCountPerContext: { - course: 3, - boardElement: 2, + course: 1, + boardElement: 1, }, }); }); diff --git a/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts b/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts index 93d4c4f6705..2ae4f66b2d8 100644 --- a/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts +++ b/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts @@ -1,16 +1,12 @@ import { Module } from '@nestjs/common'; import { CommonToolModule } from '../common'; -import { - SchoolExternalToolService, - SchoolExternalToolValidationService, - SchoolExternalToolMetadataService, -} from './service'; import { ExternalToolModule } from '../external-tool'; import { ToolConfigModule } from '../tool-config.module'; +import { SchoolExternalToolService, SchoolExternalToolValidationService } from './service'; @Module({ imports: [CommonToolModule, ExternalToolModule, ToolConfigModule], - providers: [SchoolExternalToolService, SchoolExternalToolValidationService, SchoolExternalToolMetadataService], - exports: [SchoolExternalToolService, SchoolExternalToolValidationService, SchoolExternalToolMetadataService], + providers: [SchoolExternalToolService, SchoolExternalToolValidationService], + exports: [SchoolExternalToolService, SchoolExternalToolValidationService], }) export class SchoolExternalToolModule {} diff --git a/apps/server/src/modules/tool/school-external-tool/service/index.ts b/apps/server/src/modules/tool/school-external-tool/service/index.ts index ea949d8b70a..1ceab5f3da5 100644 --- a/apps/server/src/modules/tool/school-external-tool/service/index.ts +++ b/apps/server/src/modules/tool/school-external-tool/service/index.ts @@ -1,3 +1,2 @@ export * from './school-external-tool.service'; export * from './school-external-tool-validation.service'; -export * from './school-external-tool-metadata.service'; diff --git a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-metadata.service.spec.ts b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-metadata.service.spec.ts deleted file mode 100644 index 8aa29737550..00000000000 --- a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-metadata.service.spec.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { ObjectId } from '@mikro-orm/mongodb'; -import { Test, TestingModule } from '@nestjs/testing'; -import { ContextExternalToolRepo } from '@shared/repo'; -import { Logger } from '@src/core/logger'; -import { SchoolExternalToolMetadata } from '../domain'; -import { SchoolExternalToolMetadataService } from './school-external-tool-metadata.service'; - -describe('SchoolExternalToolMetadataService', () => { - let module: TestingModule; - let service: SchoolExternalToolMetadataService; - - let contextExternalToolRepo: DeepMocked; - - beforeAll(async () => { - module = await Test.createTestingModule({ - providers: [ - SchoolExternalToolMetadataService, - { - provide: ContextExternalToolRepo, - useValue: createMock(), - }, - { - provide: Logger, - useValue: createMock(), - }, - ], - }).compile(); - - service = module.get(SchoolExternalToolMetadataService); - contextExternalToolRepo = module.get(ContextExternalToolRepo); - }); - - afterAll(async () => { - await module.close(); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - describe('getMetadata', () => { - describe('when schoolExternalToolId is given', () => { - const setup = () => { - const schoolToolId: string = new ObjectId().toHexString(); - - const schoolExternalToolMetadata: SchoolExternalToolMetadata = new SchoolExternalToolMetadata({ - contextExternalToolCountPerContext: { course: 3, boardElement: 3 }, - }); - - contextExternalToolRepo.countBySchoolToolIdsAndContextType.mockResolvedValue(3); - - return { - schoolToolId, - schoolExternalToolMetadata, - }; - }; - - it('should return externalToolMetadata', async () => { - const { schoolToolId, schoolExternalToolMetadata } = setup(); - - const result: SchoolExternalToolMetadata = await service.getMetadata(schoolToolId); - - expect(result).toEqual(schoolExternalToolMetadata); - }); - }); - - describe('when no related context external tool was found', () => { - const setup = () => { - const schoolToolId: string = new ObjectId().toHexString(); - - const schoolExternalToolMetadata: SchoolExternalToolMetadata = new SchoolExternalToolMetadata({ - contextExternalToolCountPerContext: { course: 0, boardElement: 0 }, - }); - - contextExternalToolRepo.countBySchoolToolIdsAndContextType.mockResolvedValue(0); - - return { - schoolToolId, - schoolExternalToolMetadata, - }; - }; - - it('should return empty schoolExternalToolMetadata', async () => { - const { schoolToolId, schoolExternalToolMetadata } = setup(); - - const result: SchoolExternalToolMetadata = await service.getMetadata(schoolToolId); - - expect(result).toEqual(schoolExternalToolMetadata); - }); - }); - }); -}); diff --git a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-metadata.service.ts b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-metadata.service.ts deleted file mode 100644 index 953e3decb26..00000000000 --- a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-metadata.service.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { EntityId } from '@shared/domain/types'; -import { ContextExternalToolRepo } from '@shared/repo'; -import { ToolContextType } from '../../common/enum'; -import { ToolContextMapper } from '../../common/mapper/tool-context.mapper'; -import { ContextExternalToolType } from '../../context-external-tool/entity'; -import { SchoolExternalToolMetadata } from '../domain'; - -@Injectable() -export class SchoolExternalToolMetadataService { - constructor(private readonly contextToolRepo: ContextExternalToolRepo) {} - - async getMetadata(schoolExternalToolId: EntityId) { - const contextExternalToolCount: Record = { - [ContextExternalToolType.BOARD_ELEMENT]: 0, - [ContextExternalToolType.COURSE]: 0, - }; - - await Promise.all( - Object.values(ToolContextType).map(async (contextType: ToolContextType): Promise => { - const type: ContextExternalToolType = ToolContextMapper.contextMapping[contextType]; - - const countPerContext: number = await this.contextToolRepo.countBySchoolToolIdsAndContextType(type, [ - schoolExternalToolId, - ]); - - contextExternalToolCount[type] = countPerContext; - }) - ); - - const schoolExternalToolMetadata: SchoolExternalToolMetadata = new SchoolExternalToolMetadata({ - contextExternalToolCountPerContext: contextExternalToolCount, - }); - - return schoolExternalToolMetadata; - } -} diff --git a/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.spec.ts b/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.spec.ts index d64ac866725..6584dfa6946 100644 --- a/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.spec.ts +++ b/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.spec.ts @@ -6,14 +6,11 @@ import { User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; import { schoolExternalToolFactory, setupEntities, userFactory } from '@shared/testing'; +import { CommonToolMetadataService } from '../../common/service/common-tool-metadata.service'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalToolService } from '../../context-external-tool/service'; import { SchoolExternalTool } from '../domain'; -import { - SchoolExternalToolMetadataService, - SchoolExternalToolService, - SchoolExternalToolValidationService, -} from '../service'; +import { SchoolExternalToolService, SchoolExternalToolValidationService } from '../service'; import { SchoolExternalToolQueryInput } from './dto/school-external-tool.types'; import { SchoolExternalToolUc } from './school-external-tool.uc'; @@ -25,7 +22,7 @@ describe('SchoolExternalToolUc', () => { let contextExternalToolService: DeepMocked; let schoolExternalToolValidationService: DeepMocked; let toolPermissionHelper: DeepMocked; - let schoolExternalToolMetadataService: DeepMocked; + let commonToolMetadataService: DeepMocked; beforeAll(async () => { await setupEntities(); @@ -49,8 +46,8 @@ describe('SchoolExternalToolUc', () => { useValue: createMock(), }, { - provide: SchoolExternalToolMetadataService, - useValue: createMock(), + provide: CommonToolMetadataService, + useValue: createMock(), }, ], }).compile(); @@ -60,7 +57,7 @@ describe('SchoolExternalToolUc', () => { contextExternalToolService = module.get(ContextExternalToolService); schoolExternalToolValidationService = module.get(SchoolExternalToolValidationService); toolPermissionHelper = module.get(ToolPermissionHelper); - schoolExternalToolMetadataService = module.get(SchoolExternalToolMetadataService); + commonToolMetadataService = module.get(CommonToolMetadataService); }); afterAll(async () => { @@ -418,7 +415,7 @@ describe('SchoolExternalToolUc', () => { await uc.getMetadataForSchoolExternalTool(user.id, toolId); - expect(schoolExternalToolMetadataService.getMetadata).toHaveBeenCalledWith(toolId); + expect(commonToolMetadataService.getMetadataForSchoolExternalTool).toHaveBeenCalledWith(toolId); }); }); }); diff --git a/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.ts b/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.ts index 54cba380286..1390a7649f6 100644 --- a/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.ts +++ b/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.ts @@ -2,14 +2,11 @@ import { AuthorizationContext, AuthorizationContextBuilder } from '@modules/auth import { Injectable } from '@nestjs/common'; import { Permission } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; +import { CommonToolMetadataService } from '../../common/service/common-tool-metadata.service'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalToolService } from '../../context-external-tool/service'; import { SchoolExternalTool, SchoolExternalToolMetadata } from '../domain'; -import { - SchoolExternalToolMetadataService, - SchoolExternalToolService, - SchoolExternalToolValidationService, -} from '../service'; +import { SchoolExternalToolService, SchoolExternalToolValidationService } from '../service'; import { SchoolExternalToolDto, SchoolExternalToolQueryInput } from './dto/school-external-tool.types'; @Injectable() @@ -18,7 +15,7 @@ export class SchoolExternalToolUc { private readonly schoolExternalToolService: SchoolExternalToolService, private readonly contextExternalToolService: ContextExternalToolService, private readonly schoolExternalToolValidationService: SchoolExternalToolValidationService, - private readonly schoolExternalToolMetadataService: SchoolExternalToolMetadataService, + private readonly commonToolMetadataService: CommonToolMetadataService, private readonly toolPermissionHelper: ToolPermissionHelper ) {} @@ -111,7 +108,7 @@ export class SchoolExternalToolUc { const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.SCHOOL_TOOL_ADMIN]); await this.toolPermissionHelper.ensureSchoolPermissions(userId, schoolExternalTool, context); - const metadata: SchoolExternalToolMetadata = await this.schoolExternalToolMetadataService.getMetadata( + const metadata: SchoolExternalToolMetadata = await this.commonToolMetadataService.getMetadataForSchoolExternalTool( schoolExternalToolId ); diff --git a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.integration.spec.ts b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.integration.spec.ts index 04926002677..eb777e8b5d8 100644 --- a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.integration.spec.ts @@ -448,84 +448,50 @@ describe('ContextExternalToolRepo', () => { }); }); - describe('countBySchoolToolIdsAndContextType', () => { - describe('when a ContextExternalTool is found for course context', () => { + describe('findBySchoolToolIdsAndContextType', () => { + describe('when a ContextExternalTool is found for the selected context', () => { const setup = async () => { - const schoolExternalTool = schoolExternalToolEntityFactory.buildWithId(); - const schoolExternalTool1 = schoolExternalToolEntityFactory.buildWithId(); - - const contextExternalTool = contextExternalToolEntityFactory.buildList(4, { - contextType: ContextExternalToolType.COURSE, - schoolTool: schoolExternalTool, - }); - - const contextExternalTool3 = contextExternalToolEntityFactory.buildList(2, { - contextType: ContextExternalToolType.COURSE, - schoolTool: schoolExternalTool1, - }); - - await em.persistAndFlush([ - schoolExternalTool, - schoolExternalTool1, - ...contextExternalTool, - ...contextExternalTool3, - ]); - - return { - schoolExternalTool, - schoolExternalTool1, - }; - }; - - it('should return correct results', async () => { - const { schoolExternalTool, schoolExternalTool1 } = await setup(); - - const result = await repo.countBySchoolToolIdsAndContextType(ContextExternalToolType.COURSE, [ - schoolExternalTool.id, - schoolExternalTool1.id, - ]); - - expect(result).toEqual(6); - }); - }); - - describe('when a ContextExternalTool is found for board context', () => { - const setup = async () => { - const schoolExternalTool = schoolExternalToolEntityFactory.buildWithId(); - const schoolExternalTool1 = schoolExternalToolEntityFactory.buildWithId(); - - const contextExternalTool1 = contextExternalToolEntityFactory.buildList(3, { - contextType: ContextExternalToolType.BOARD_ELEMENT, - schoolTool: schoolExternalTool, - }); - - const contextExternalTool2 = contextExternalToolEntityFactory.buildList(2, { - contextType: ContextExternalToolType.BOARD_ELEMENT, - schoolTool: schoolExternalTool1, - }); + const schoolExternalTool1: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId(); + const schoolExternalTool2: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId(); + + const contextExternalToolsInCourses: ContextExternalToolEntity[] = contextExternalToolEntityFactory.buildList( + 4, + { + contextType: ContextExternalToolType.COURSE, + schoolTool: schoolExternalTool1, + } + ); + + const contextExternalToolsOnBoards: ContextExternalToolEntity[] = contextExternalToolEntityFactory.buildList( + 2, + { + contextType: ContextExternalToolType.BOARD_ELEMENT, + schoolTool: schoolExternalTool2, + } + ); await em.persistAndFlush([ - schoolExternalTool, schoolExternalTool1, - ...contextExternalTool1, - ...contextExternalTool2, + schoolExternalTool2, + ...contextExternalToolsInCourses, + ...contextExternalToolsOnBoards, ]); return { - schoolExternalTool, schoolExternalTool1, + schoolExternalTool2, }; }; - it('should return correct results', async () => { - const { schoolExternalTool, schoolExternalTool1 } = await setup(); + it('should return the context external tools of that context', async () => { + const { schoolExternalTool1, schoolExternalTool2 } = await setup(); - const result = await repo.countBySchoolToolIdsAndContextType(ContextExternalToolType.BOARD_ELEMENT, [ - schoolExternalTool.id, - schoolExternalTool1.id, - ]); + const result: ContextExternalTool[] = await repo.findBySchoolToolIdsAndContextType( + [schoolExternalTool1.id, schoolExternalTool2.id], + ContextExternalToolType.COURSE + ); - expect(result).toEqual(5); + expect(result).toHaveLength(4); }); }); }); diff --git a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.ts b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.ts index 1dcb54f6755..7360da6dbfa 100644 --- a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.ts +++ b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.ts @@ -41,12 +41,15 @@ export class ContextExternalToolRepo extends BaseDORepo { + const entities = await this._em.find(this.entityName, { schoolTool: { $in: schoolExternalToolIds }, contextType }); + + const dos: ContextExternalTool[] = entities.map((entity: ContextExternalToolEntity) => this.mapEntityToDO(entity)); - return contextExternalToolCount; + return dos; } public override async findById(id: EntityId): Promise {