From 548ec7c0d630a9c25c04f0bcb2268245f22f8740 Mon Sep 17 00:00:00 2001 From: psachmann Date: Thu, 2 Nov 2023 16:27:05 +0100 Subject: [PATCH] EW-539 extending organization item element of CC --- ...mon-cartridge-organization-item-element.ts | 3 +- .../common-cartridge-export.service.spec.ts | 109 ++++++++++++++---- .../common-cartridge-export.service.ts | 40 +++---- 3 files changed, 106 insertions(+), 46 deletions(-) diff --git a/apps/server/src/modules/learnroom/common-cartridge/common-cartridge-organization-item-element.ts b/apps/server/src/modules/learnroom/common-cartridge/common-cartridge-organization-item-element.ts index e77fcbc0905..6db8b90fd26 100644 --- a/apps/server/src/modules/learnroom/common-cartridge/common-cartridge-organization-item-element.ts +++ b/apps/server/src/modules/learnroom/common-cartridge/common-cartridge-organization-item-element.ts @@ -6,8 +6,7 @@ export type ICommonCartridgeOrganizationProps = { identifier: string; title: string; version: string; - resources: ICommonCartridgeResourceProps[]; -}; +} & ({ children: ICommonCartridgeOrganizationProps[] } | { resources: ICommonCartridgeResourceProps[] }); export class CommonCartridgeOrganizationItemElement implements ICommonCartridgeElement { constructor(private readonly props: ICommonCartridgeOrganizationProps) {} 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 af1bc727d6a..9a0d526b991 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 @@ -1,19 +1,21 @@ +import { faker } from '@faker-js/faker'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { CourseService } from '@modules/learnroom/service'; +import { CommonCartridgeExportService } from '@modules/learnroom/service/common-cartridge-export.service'; +import { LessonService } from '@modules/lesson/service'; +import { TaskService } from '@modules/task/service/task.service'; import { Test, TestingModule } from '@nestjs/testing'; import { ComponentType, Course, + IComponentLernstoreProperties, IComponentProperties, - IComponentTextProperties, LessonEntity, Task, } from '@shared/domain'; import { courseFactory, lessonFactory, setupEntities, taskFactory } from '@shared/testing'; -import { CommonCartridgeExportService } from '@modules/learnroom/service/common-cartridge-export.service'; -import { CourseService } from '@modules/learnroom/service'; -import { LessonService } from '@modules/lesson/service'; -import { TaskService } from '@modules/task/service/task.service'; import AdmZip from 'adm-zip'; +import { writeFile } from 'fs/promises'; import { CommonCartridgeVersion } from '../common-cartridge'; describe('CommonCartridgeExportService', () => { @@ -51,33 +53,63 @@ describe('CommonCartridgeExportService', () => { lessonServiceMock = module.get(LessonService); taskServiceMock = module.get(TaskService); course = courseFactory.teachersWithId(2).buildWithId(); - lessons = lessonFactory.buildListWithId(5, { + lessons = lessonFactory.buildListWithId(1, { contents: [ { + _id: faker.string.uuid(), + hidden: false, component: ComponentType.TEXT, title: 'Text', content: { text: 'text', }, - } as IComponentProperties, + }, { + _id: faker.string.uuid(), + hidden: false, component: ComponentType.ETHERPAD, title: 'Etherpad', content: { - url: 'url', + title: faker.lorem.words(2), + description: faker.lorem.sentence(), + url: 'https://google.com', }, - } as IComponentProperties, + }, { + _id: faker.string.uuid(), + hidden: false, component: ComponentType.GEOGEBRA, title: 'Geogebra', content: { - materialId: 'materialId', + materialId: 'https://google.com', }, - } as IComponentProperties, - {} as IComponentProperties, + }, + { + _id: faker.string.uuid(), + hidden: false, + // AI next 18 lines + component: ComponentType.LERNSTORE, + title: 'Lernstore', + content: { + resources: [ + { + client: faker.company.name(), + title: faker.lorem.words(2), + description: faker.lorem.sentence(), + url: faker.internet.url(), + }, + { + client: faker.company.name(), + title: faker.lorem.words(2), + description: faker.lorem.sentence(), + url: faker.internet.url(), + }, + ], + }, + }, ], }); - tasks = taskFactory.buildListWithId(5); + tasks = taskFactory.buildListWithId(2); }); afterAll(async () => { @@ -87,20 +119,14 @@ describe('CommonCartridgeExportService', () => { describe('exportCourse', () => { const setupExport = async (version: CommonCartridgeVersion) => { const [lesson] = lessons; - const textContent = { text: 'Some random text' } as IComponentTextProperties; - const lessonContent: IComponentProperties = { - _id: 'random_id', - title: 'A random title', - hidden: false, - component: ComponentType.TEXT, - content: textContent, - }; - lesson.contents = [lessonContent]; + lessonServiceMock.findById.mockResolvedValueOnce(lesson); courseServiceMock.findById.mockResolvedValueOnce(course); lessonServiceMock.findByCourseIds.mockResolvedValueOnce([lessons, lessons.length]); taskServiceMock.findBySingleParent.mockResolvedValueOnce([tasks, tasks.length]); + const archive = new AdmZip(await courseExportService.exportCourse(course.id, '', version)); + return archive; }; @@ -203,5 +229,44 @@ describe('CommonCartridgeExportService', () => { expect(manifest).toContain(CommonCartridgeVersion.V_1_3_0); }); }); + + describe('when exporting learn store content from course with Common Cartridge 1.3', () => { + let archive: AdmZip; + let lernstoreProps: IComponentProperties; + + beforeAll(async () => { + const [lesson] = lessons; + + archive = await setupExport(CommonCartridgeVersion.V_1_3_0); + lernstoreProps = lesson.contents.filter((content) => content.component === ComponentType.LERNSTORE)[0]; + + await writeFile('test.zip', archive.toBuffer(), 'binary'); + }); + + it('should add learn store content to manifest file', () => { + // AI next 3 lines + const manifest = archive.getEntry('imsmanifest.xml')?.getData().toString(); + + expect(manifest).toContain(lernstoreProps.title); + }); + + it.skip('should create directory for each learn store content', () => { + // AI next 3 lines + const directory = archive.getEntry(`i${lernstoreProps._id as string}/`); + + expect(directory).toBeDefined(); + }); + + it.skip('should add learn store content as web links to directory', () => { + expect(lernstoreProps.content).toBeDefined(); + expect((lernstoreProps.content as IComponentLernstoreProperties).resources).toHaveLength(2); + + (lernstoreProps.content as IComponentLernstoreProperties).resources.forEach((resource) => { + const file = archive.getEntry(`i${lernstoreProps._id as string}/${resource.title}.html`); + + expect(file).toBeDefined(); + }); + }); + }); }); }); 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 83fd91edfc6..fb471eac10d 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 @@ -87,19 +87,18 @@ export class CommonCartridgeExportService { content: IComponentProperties, version: CommonCartridgeVersion ): ICommonCartridgeResourceProps | undefined { - const commonProps = { - version, - identifier: createIdentifier(content._id), - href: `${createIdentifier(lessonId)}/${createIdentifier(content._id)}.html`, - title: content.title, - }; - - if (content.component === ComponentType.TEXT) { + const commonProps = (fileExt: 'html' | 'xml') => { return { version, identifier: createIdentifier(content._id), - href: `${createIdentifier(lessonId)}/${createIdentifier(content._id)}.html`, + href: `${createIdentifier(lessonId)}/${createIdentifier(content._id)}.${fileExt}`, title: content.title, + }; + }; + + if (content.component === ComponentType.TEXT) { + return { + ...commonProps('html'), type: CommonCartridgeResourceType.WEB_CONTENT, intendedUse: CommonCartridgeIntendedUseType.UNSPECIFIED, html: `

${content.title}

${content.content.text}

`, @@ -109,35 +108,32 @@ export class CommonCartridgeExportService { if (content.component === ComponentType.GEOGEBRA) { const url = `https://www.geogebra.org/m/${content.content.materialId}`; return version === CommonCartridgeVersion.V_1_3_0 - ? { ...commonProps, type: CommonCartridgeResourceType.WEB_LINK_V3, url } - : { ...commonProps, type: CommonCartridgeResourceType.WEB_LINK_V1, url }; + ? { ...commonProps('xml'), type: CommonCartridgeResourceType.WEB_LINK_V3, url } + : { ...commonProps('xml'), type: CommonCartridgeResourceType.WEB_LINK_V1, url }; } if (content.component === ComponentType.ETHERPAD) { return version === CommonCartridgeVersion.V_1_3_0 ? { - ...commonProps, + ...commonProps('xml'), type: CommonCartridgeResourceType.WEB_LINK_V3, url: content.content.url, title: content.content.description, } : { - ...commonProps, + ...commonProps('xml'), type: CommonCartridgeResourceType.WEB_LINK_V1, url: content.content.url, title: content.content.description, }; } - if (content.component === ComponentType.LERNSTORE) { - if (content.content && Array.isArray(content.content.resources) && content.content.resources.length > 0) { - content.content.resources.map((resource) => { - const url = resource.url; - return version === CommonCartridgeVersion.V_1_3_0 - ? { ...commonProps, type: CommonCartridgeResourceType.WEB_LINK_V3, url } - : { ...commonProps, type: CommonCartridgeResourceType.WEB_LINK_V1, url }; - }); - } + if (content.component === ComponentType.LERNSTORE && content.content) { + const { resources } = content.content; + + return version === CommonCartridgeVersion.V_1_3_0 + ? { type: CommonCartridgeResourceType.WEB_LINK_V3, url: resources[0].url, ...commonProps('xml') } + : { type: CommonCartridgeResourceType.WEB_LINK_V1, url: resources[0].url, ...commonProps('xml') }; } return undefined;