From 05f055df01e5d6f61ebaf799e17673f067bfc86b Mon Sep 17 00:00:00 2001 From: Patrick Sachmann <20001160+psachmann@users.noreply.github.com> Date: Thu, 18 Apr 2024 09:52:14 +0200 Subject: [PATCH] EW-799 Common Cartridge Export of Column Board Link Elements (#4932) * Adding link elements from the column board to the common cartridge export * Tested for Common Cartridge 1.1.0 --- .../mapper/common-cartridge.mapper.spec.ts | 51 ++++++++++++++----- .../mapper/common-cartridge.mapper.ts | 11 +++- .../common-cartridge-export.service.spec.ts | 31 +++++++++-- .../common-cartridge-export.service.ts | 38 +++++++++++--- 4 files changed, 104 insertions(+), 27 deletions(-) diff --git a/apps/server/src/modules/learnroom/mapper/common-cartridge.mapper.spec.ts b/apps/server/src/modules/learnroom/mapper/common-cartridge.mapper.spec.ts index 501ff37aaa9..526ad859a29 100644 --- a/apps/server/src/modules/learnroom/mapper/common-cartridge.mapper.spec.ts +++ b/apps/server/src/modules/learnroom/mapper/common-cartridge.mapper.spec.ts @@ -15,9 +15,15 @@ import { import { ConfigService } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; import { ComponentProperties, ComponentType } from '@shared/domain/entity'; -import { courseFactory, lessonFactory, setupEntities, taskFactory, userFactory } from '@shared/testing'; -import { RichTextElement, RichTextElementProps } from '@shared/domain/domainobject'; -import { InputFormat } from '@shared/domain/types'; +import { + courseFactory, + lessonFactory, + linkElementFactory, + richTextElementFactory, + setupEntities, + taskFactory, + userFactory, +} from '@shared/testing'; import { LearnroomConfig } from '../learnroom.config'; import { CommonCartridgeMapper } from './common-cartridge.mapper'; @@ -403,16 +409,7 @@ describe('CommonCartridgeMapper', () => { describe('mapRichTextElementToResource', () => { describe('when mapping rich text element', () => { const setup = () => { - const richTextElementProps: RichTextElementProps = { - text: faker.lorem.paragraph(), - inputFormat: InputFormat.RICH_TEXT_CK5, - id: faker.string.uuid(), - createdAt: faker.date.recent(), - updatedAt: faker.date.recent(), - }; - const richTextElement = new RichTextElement(richTextElementProps); - - configServiceMock.getOrThrow.mockReturnValue(faker.internet.url()); + const richTextElement = richTextElementFactory.build(); return { richTextElement }; }; @@ -425,11 +422,37 @@ describe('CommonCartridgeMapper', () => { expect(resourceProps).toStrictEqual({ type: CommonCartridgeResourceType.WEB_CONTENT, identifier: expect.any(String), - title: richTextElement.text.slice(0, 50).concat('...'), + title: richTextElement.text + .slice(0, 50) + .replace(/<[^>]*>?/gm, '') + .concat('...'), html: `

${richTextElement.text}

`, intendedUse: CommonCartridgeIntendedUseType.UNSPECIFIED, }); }); }); }); + + describe('mapLinkElementToResource', () => { + describe('when mapping link element', () => { + const setup = () => { + const linkElement = linkElementFactory.build(); + + return { linkElement }; + }; + + it('should map to web link', () => { + const { linkElement } = setup(); + + const resourceProps = sut.mapLinkElementToResource(linkElement); + + expect(resourceProps).toStrictEqual({ + type: CommonCartridgeResourceType.WEB_LINK, + identifier: createIdentifier(linkElement.id), + title: linkElement.title, + url: linkElement.url, + }); + }); + }); + }); }); diff --git a/apps/server/src/modules/learnroom/mapper/common-cartridge.mapper.ts b/apps/server/src/modules/learnroom/mapper/common-cartridge.mapper.ts index 130125b7132..068f029fb3d 100644 --- a/apps/server/src/modules/learnroom/mapper/common-cartridge.mapper.ts +++ b/apps/server/src/modules/learnroom/mapper/common-cartridge.mapper.ts @@ -11,8 +11,8 @@ import { } from '@modules/common-cartridge'; import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; +import { LinkElement, RichTextElement } from '@shared/domain/domainobject'; import { ComponentProperties, ComponentType, Course, LessonEntity, Task } from '@shared/domain/entity'; -import { RichTextElement } from '@shared/domain/domainobject'; import { LearnroomConfig } from '../learnroom.config'; @Injectable() @@ -129,6 +129,15 @@ export class CommonCartridgeMapper { }; } + public mapLinkElementToResource(element: LinkElement): CommonCartridgeResourceProps { + return { + type: CommonCartridgeResourceType.WEB_LINK, + identifier: createIdentifier(element.id), + title: element.title, + url: element.url, + }; + } + private getTextTitle(text: string): string { const title = text .slice(0, 50) diff --git a/apps/server/src/modules/learnroom/service/common-cartridge-export.service.spec.ts b/apps/server/src/modules/learnroom/service/common-cartridge-export.service.spec.ts index 6e526152704..d131e9c4774 100644 --- a/apps/server/src/modules/learnroom/service/common-cartridge-export.service.spec.ts +++ b/apps/server/src/modules/learnroom/service/common-cartridge-export.service.spec.ts @@ -8,14 +8,15 @@ import { ConfigService } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; import { ComponentType } from '@shared/domain/entity'; import { + cardFactory, columnBoardFactory, columnFactory, - cardFactory, courseFactory, lessonFactory, + linkElementFactory, + richTextElementFactory, setupEntities, taskFactory, - richTextElementFactory, } from '@shared/testing'; import { ColumnBoardService } from '@src/modules/board'; import AdmZip from 'adm-zip'; @@ -73,7 +74,8 @@ describe('CommonCartridgeExportService', () => { const [lesson] = lessons; const taskFromLesson = taskFactory.buildWithId({ course, lesson }); const textCardElement = richTextElementFactory.build(); - const card = cardFactory.build({ children: [textCardElement] }); + const linkElement = linkElementFactory.build(); + const card = cardFactory.build({ children: [textCardElement, linkElement] }); const column = columnFactory.build({ children: [card] }); const columnBoard = columnBoardFactory.build({ children: [column] }); @@ -94,7 +96,7 @@ describe('CommonCartridgeExportService', () => { ); const archive = new AdmZip(buffer); - return { archive, course, lessons, tasks, taskFromLesson, columnBoard, column, card, textCardElement }; + return { archive, course, lessons, tasks, taskFromLesson, columnBoard, column, card, textCardElement, linkElement }; }; beforeAll(async () => { @@ -199,6 +201,20 @@ describe('CommonCartridgeExportService', () => { expect(manifest).toContain(createXmlString('title', card.title)); }); + + it('should add content element of cards', async () => { + const { archive, textCardElement } = await setup(); + const manifest = getFileContent(archive, 'imsmanifest.xml'); + + expect(manifest).toContain(` { + const { archive, linkElement } = await setup(); + const manifest = getFileContent(archive, 'imsmanifest.xml'); + + expect(manifest).toContain(` { @@ -269,6 +285,13 @@ describe('CommonCartridgeExportService', () => { expect(manifest).toContain(` { + const { archive, linkElement } = await setup(); + const manifest = getFileContent(archive, 'imsmanifest.xml'); + + expect(manifest).toContain(` { diff --git a/apps/server/src/modules/learnroom/service/common-cartridge-export.service.ts b/apps/server/src/modules/learnroom/service/common-cartridge-export.service.ts index ee6c414f633..d38814a8305 100644 --- a/apps/server/src/modules/learnroom/service/common-cartridge-export.service.ts +++ b/apps/server/src/modules/learnroom/service/common-cartridge-export.service.ts @@ -6,7 +6,16 @@ import { import { LessonService } from '@modules/lesson'; import { TaskService } from '@modules/task'; import { Injectable } from '@nestjs/common'; -import { BoardExternalReferenceType, Column, Card, RichTextElement } from '@shared/domain/domainobject'; +import { + AnyBoardDo, + BoardExternalReferenceType, + Card, + Column, + isCard, + isColumn, + isLinkElement, + isRichTextElement, +} from '@shared/domain/domainobject'; import { ComponentProperties } from '@shared/domain/entity'; import { EntityId } from '@shared/domain/types'; import { ColumnBoardService } from '@src/modules/board'; @@ -110,7 +119,7 @@ export class CommonCartridgeExportService { }); columnBoard.children - .filter((child) => child instanceof Column) + .filter((child) => isColumn(child)) .forEach((column) => this.addColumnToOrganization(column as Column, organization)); } } @@ -123,7 +132,7 @@ export class CommonCartridgeExportService { }); column.children - .filter((child) => child instanceof Card) + .filter((child) => isCard(child)) .forEach((card) => this.addCardToOrganization(card as Card, columnOrganization)); } @@ -134,11 +143,24 @@ export class CommonCartridgeExportService { identifier: createIdentifier(id), }); - card.children - .filter((child) => child instanceof RichTextElement) - .forEach((child) => - cardOrganization.addResource(this.commonCartridgeMapper.mapRichTextElementToResource(child as RichTextElement)) - ); + card.children.forEach((child) => this.addCardElementToOrganization(child, cardOrganization)); + } + + private addCardElementToOrganization( + element: AnyBoardDo, + organizationBuilder: CommonCartridgeOrganizationBuilder + ): void { + if (isRichTextElement(element)) { + const resource = this.commonCartridgeMapper.mapRichTextElementToResource(element); + + organizationBuilder.addResource(resource); + } + + if (isLinkElement(element)) { + const resource = this.commonCartridgeMapper.mapLinkElementToResource(element); + + organizationBuilder.addResource(resource); + } } private addComponentToOrganization(