From dc46eb25e93889d9fb12994202e7b4356a33465f Mon Sep 17 00:00:00 2001 From: Simone Radtke <94017602+SimoneRadtke-Cap@users.noreply.github.com> Date: Fri, 21 Jun 2024 14:14:44 +0200 Subject: [PATCH] EW-793 - Refactor Common Cartridge Export (#4918) * Adding loggable exceptions * Renaming folder and adjust regarding imports * Adding common cartridge guard for intended use * Restructuring Common Cartridge export mechanism --------- Co-authored-by: Patrick Sachmann <20001160+psachmann@users.noreply.github.com> --- apps/server/src/modules/board/index.ts | 29 ++-- .../common-cartridge-file-builder.spec.ts | 154 ++++++++--------- .../builders/common-cartridge-file-builder.ts | 79 ++++----- ...mon-cartridge-organization-builder.spec.ts | 87 ---------- .../common-cartridge-organization-builder.ts | 82 --------- ...common-cartridge-organization-node.spec.ts | 157 ++++++++++++++++++ .../common-cartridge-organization-node.ts | 71 ++++++++ ...tridge-resource-collection-builder.spec.ts | 74 +++++++++ ...n-cartridge-resource-collection-builder.ts | 16 ++ .../common-cartridge-resource-node.spec.ts | 46 +++++ .../common-cartridge-resource-node.ts | 23 +++ .../export/common-cartridge.enums.ts | 6 +- .../export/common-cartridge.guard.spec.ts | 29 ++++ .../export/common-cartridge.guard.ts | 9 + ...cartridge-organizations-wrapper-element.ts | 53 ++++++ ...mon-cartridge-resources-wrapper-element.ts | 40 +++++ .../common-cartridge-element-factory.spec.ts | 6 +- .../common-cartridge-element-factory.ts | 5 +- .../common-cartridge-element-factory.spec.ts | 6 +- .../common-cartridge-element-factory.ts | 4 +- .../common-cartridge-metadata-element.spec.ts | 30 +++- .../common-cartridge-metadata-element.ts | 14 +- ...mon-cartridge-organization-element.spec.ts | 39 +++-- .../common-cartridge-organization-element.ts | 49 +++--- ...idge-organizations-wrapper-element.spec.ts | 30 +++- ...cartridge-organizations-wrapper-element.ts | 40 +---- ...artridge-resources-wrapper-element.spec.ts | 28 +++- ...mon-cartridge-resources-wrapper-element.ts | 29 +--- .../common-cartridge-element-factory.spec.ts | 6 +- .../common-cartridge-element-factory.ts | 4 +- .../common-cartridge-metadata-element.spec.ts | 31 +++- .../common-cartridge-metadata-element.ts | 14 +- ...mon-cartridge-organization-element.spec.ts | 37 +++-- .../common-cartridge-organization-element.ts | 49 +++--- ...idge-organizations-wrapper-element.spec.ts | 28 +++- ...cartridge-organizations-wrapper-element.ts | 40 +---- ...artridge-resources-wrapper-element.spec.ts | 28 +++- ...mon-cartridge-resources-wrapper-element.ts | 29 +--- ...e-not-supported.loggable-exception.spec.ts | 23 +++ ...t-type-not-supported.loggable-exception.ts | 24 +++ .../export/errors/error.enums.ts | 7 + .../common-cartridge/export/errors/index.ts | 5 + ...e-not-supported.loggable-exception.spec.ts | 23 +++ ...ed-use-not-supported.loggable-exception.ts | 24 +++ ...issing-metadata.loggable-exception.spec.ts | 22 +++ .../missing-metadata.loggable-exception.ts | 23 +++ ...e-not-supported.loggable-exception.spec.ts | 23 +++ ...e-type-not-supported.loggable-exception.ts | 24 +++ ...n-not-supported.loggable-exception.spec.ts | 23 +++ ...ersion-not-supported.loggable-exception.ts | 24 +++ .../common-cartridge-base.interface.ts | 30 ++++ .../common-cartridge-element.interface.ts | 39 +---- .../common-cartridge-resource.interface.ts | 6 - .../export/interfaces/index.ts | 2 + .../export/interfaces/xml-object.interface.ts | 1 + .../common-cartridge-resource-factory.spec.ts | 6 +- .../common-cartridge-resource-factory.ts | 7 +- ...common-cartridge-manifest-resource.spec.ts | 68 ++++++-- .../common-cartridge-manifest-resource.ts | 30 ++-- .../common-cartridge-resource-factory.spec.ts | 6 +- .../common-cartridge-resource-factory.ts | 4 +- ...mon-cartridge-web-content-resource.spec.ts | 70 +++++--- .../common-cartridge-web-content-resource.ts | 39 ++++- ...common-cartridge-web-link-resource.spec.ts | 67 +++++--- .../common-cartridge-web-link-resource.ts | 40 ++++- ...common-cartridge-manifest-resource.spec.ts | 68 ++++++-- .../common-cartridge-manifest-resource.ts | 26 +-- .../common-cartridge-resource-factory.spec.ts | 6 +- .../common-cartridge-resource-factory.ts | 4 +- ...mon-cartridge-web-content-resource.spec.ts | 70 +++++--- .../common-cartridge-web-content-resource.ts | 39 ++++- ...common-cartridge-web-link-resource.spec.ts | 72 +++++--- .../common-cartridge-web-link-resource.ts | 38 ++++- .../export/resources/v1.3.0/index.ts | 2 +- .../common-cartridge/export/utils.spec.ts | 60 +------ .../modules/common-cartridge/export/utils.ts | 20 --- .../src/modules/common-cartridge/index.ts | 10 +- .../common-cartridge-element-props.factory.ts | 33 +++- ...common-cartridge-resource-props.factory.ts | 30 +++- .../src/modules/learnroom/learnroom.module.ts | 4 +- ...=> common-cartridge-export.mapper.spec.ts} | 23 ++- ...r.ts => common-cartridge-export.mapper.ts} | 26 +-- .../common-cartridge-import.mapper.spec.ts | 9 +- .../mapper/common-cartridge-import.mapper.ts | 22 +-- .../common-cartridge-export.service.spec.ts | 20 +-- .../common-cartridge-export.service.ts | 74 ++++----- .../common-cartridge-import.service.ts | 36 ++-- .../service/course-copy.service.spec.ts | 3 +- .../learnroom/uc/course-import.uc.spec.ts | 2 +- .../modules/learnroom/uc/course-import.uc.ts | 2 +- .../modules/learnroom/uc/course.uc.spec.ts | 6 +- .../src/modules/learnroom/uc/course.uc.ts | 4 +- 92 files changed, 1872 insertions(+), 998 deletions(-) delete mode 100644 apps/server/src/modules/common-cartridge/export/builders/common-cartridge-organization-builder.spec.ts delete mode 100644 apps/server/src/modules/common-cartridge/export/builders/common-cartridge-organization-builder.ts create mode 100644 apps/server/src/modules/common-cartridge/export/builders/common-cartridge-organization-node.spec.ts create mode 100644 apps/server/src/modules/common-cartridge/export/builders/common-cartridge-organization-node.ts create mode 100644 apps/server/src/modules/common-cartridge/export/builders/common-cartridge-resource-collection-builder.spec.ts create mode 100644 apps/server/src/modules/common-cartridge/export/builders/common-cartridge-resource-collection-builder.ts create mode 100644 apps/server/src/modules/common-cartridge/export/builders/common-cartridge-resource-node.spec.ts create mode 100644 apps/server/src/modules/common-cartridge/export/builders/common-cartridge-resource-node.ts create mode 100644 apps/server/src/modules/common-cartridge/export/common-cartridge.guard.spec.ts create mode 100644 apps/server/src/modules/common-cartridge/export/common-cartridge.guard.ts create mode 100644 apps/server/src/modules/common-cartridge/export/elements/abstract/common-cartridge-organizations-wrapper-element.ts create mode 100644 apps/server/src/modules/common-cartridge/export/elements/abstract/common-cartridge-resources-wrapper-element.ts create mode 100644 apps/server/src/modules/common-cartridge/export/errors/element-type-not-supported.loggable-exception.spec.ts create mode 100644 apps/server/src/modules/common-cartridge/export/errors/element-type-not-supported.loggable-exception.ts create mode 100644 apps/server/src/modules/common-cartridge/export/errors/error.enums.ts create mode 100644 apps/server/src/modules/common-cartridge/export/errors/index.ts create mode 100644 apps/server/src/modules/common-cartridge/export/errors/intended-use-not-supported.loggable-exception.spec.ts create mode 100644 apps/server/src/modules/common-cartridge/export/errors/intended-use-not-supported.loggable-exception.ts create mode 100644 apps/server/src/modules/common-cartridge/export/errors/missing-metadata.loggable-exception.spec.ts create mode 100644 apps/server/src/modules/common-cartridge/export/errors/missing-metadata.loggable-exception.ts create mode 100644 apps/server/src/modules/common-cartridge/export/errors/resource-type-not-supported.loggable-exception.spec.ts create mode 100644 apps/server/src/modules/common-cartridge/export/errors/resource-type-not-supported.loggable-exception.ts create mode 100644 apps/server/src/modules/common-cartridge/export/errors/version-not-supported.loggable-exception.spec.ts create mode 100644 apps/server/src/modules/common-cartridge/export/errors/version-not-supported.loggable-exception.ts create mode 100644 apps/server/src/modules/common-cartridge/export/interfaces/common-cartridge-base.interface.ts create mode 100644 apps/server/src/modules/common-cartridge/export/interfaces/xml-object.interface.ts rename apps/server/src/modules/learnroom/mapper/{common-cartridge.mapper.spec.ts => common-cartridge-export.mapper.spec.ts} (96%) rename apps/server/src/modules/learnroom/mapper/{common-cartridge.mapper.ts => common-cartridge-export.mapper.ts} (88%) diff --git a/apps/server/src/modules/board/index.ts b/apps/server/src/modules/board/index.ts index 1148ed77b61..0321fee95c2 100644 --- a/apps/server/src/modules/board/index.ts +++ b/apps/server/src/modules/board/index.ts @@ -1,28 +1,37 @@ -export { BoardModule } from './board.module'; export { BoardConfig } from './board.config'; +export { BoardModule } from './board.module'; +export { AnyElementContentBody, LinkContentBody, RichTextContentBody } from './controller/dto'; export { - BoardNode, - BoardNodeAuthorizable, - BoardExternalReferenceType, + AnyBoardNode, BoardExternalReference, + BoardExternalReferenceType, BoardLayout, + BoardNode, + BoardNodeAuthorizable, BoardNodeFactory, - ColumnBoard, // @modules/authorization/domain/rules/board-node.rule.ts BoardRoles, + Card, + Column, + ColumnBoard, + ContentElementType, + // @modules/tool/tool-launch/service/auto-parameter-strategy/auto-context-name.strategy.ts + MediaBoard, + SubmissionItem, + UserWithBoardRoles, + isCard, + isColumn, isDrawingElement, + isLinkElement, + isRichTextElement, isSubmissionItem, isSubmissionItemContent, - SubmissionItem, - UserWithBoardRoles, - // @modules/tool/tool-launch/service/auto-parameter-strategy/auto-context-name.strategy.ts - MediaBoard, } from './domain'; export { + BoardCommonToolService, BoardNodeAuthorizableService, BoardNodeService, - BoardCommonToolService, ColumnBoardService, MediaAvailableLineService, MediaBoardService, diff --git a/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-file-builder.spec.ts b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-file-builder.spec.ts index 5c5db130cf5..308f05b2ff8 100644 --- a/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-file-builder.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-file-builder.spec.ts @@ -1,109 +1,95 @@ import { faker } from '@faker-js/faker'; -import AdmZip from 'adm-zip'; import { - CommonCartridgeElementType, - CommonCartridgeIntendedUseType, - CommonCartridgeResourceType, - CommonCartridgeVersion, -} from '../common-cartridge.enums'; -import { CommonCartridgeElementProps } from '../elements/common-cartridge-element-factory'; -import { CommonCartridgeResourceProps } from '../resources/common-cartridge-resource-factory'; -import { CommonCartridgeFileBuilder } from './common-cartridge-file-builder'; -import { CommonCartridgeOrganizationBuilderOptions } from './common-cartridge-organization-builder'; + createCommonCartridgeMetadataElementProps, + createCommonCartridgeOrganizationProps, +} from '../../testing/common-cartridge-element-props.factory'; +import { createCommonCartridgeWebLinkResourceProps } from '../../testing/common-cartridge-resource-props.factory'; +import { CommonCartridgeVersion } from '../common-cartridge.enums'; +import { CommonCartridgeElementFactory } from '../elements/common-cartridge-element-factory'; +import { MissingMetadataLoggableException } from '../errors'; +import { CommonCartridgeFileBuilder, CommonCartridgeFileBuilderProps } from './common-cartridge-file-builder'; +import { CommonCartridgeOrganizationNode } from './common-cartridge-organization-node'; describe('CommonCartridgeFileBuilder', () => { - const getFileContentAsString = (zip: AdmZip, path: string): string | undefined => - zip.getEntry(path)?.getData().toString(); + let sut: CommonCartridgeFileBuilder; - describe('build', () => { - describe('when a common cartridge archive has been created', () => { - const setup = async () => { - const metadataProps: CommonCartridgeElementProps = { - type: CommonCartridgeElementType.METADATA, - title: faker.lorem.words(), - creationDate: new Date(), - copyrightOwners: ['John Doe', 'Jane Doe'], - }; - const organizationOptions: CommonCartridgeOrganizationBuilderOptions = { - identifier: faker.string.uuid(), - title: faker.lorem.words(), - }; - const resourceProps: CommonCartridgeResourceProps = { - type: CommonCartridgeResourceType.WEB_CONTENT, - identifier: faker.string.uuid(), - title: faker.lorem.words(), - html: faker.lorem.paragraphs(), - intendedUse: CommonCartridgeIntendedUseType.UNSPECIFIED, - }; - const builder = new CommonCartridgeFileBuilder({ - version: CommonCartridgeVersion.V_1_1_0, - identifier: faker.string.uuid(), - }); - - builder - .addMetadata(metadataProps) - .addOrganization(organizationOptions) - .addResource(resourceProps) - .addSubOrganization(organizationOptions) - .addResource(resourceProps) - .addSubOrganization(organizationOptions) - .addResource(resourceProps); - - const archive = new AdmZip(await builder.build()); - - return { archive, metadataProps, organizationOptions, resourceProps }; - }; + const builderProps: CommonCartridgeFileBuilderProps = { + version: CommonCartridgeVersion.V_1_1_0, + identifier: faker.string.uuid(), + }; - it('should have a imsmanifest.xml in archive root', async () => { - const { archive } = await setup(); + beforeEach(() => { + sut = new CommonCartridgeFileBuilder(builderProps); + jest.clearAllMocks(); + }); - const manifest = getFileContentAsString(archive, 'imsmanifest.xml'); + describe('addMetadata', () => { + describe('when metadata is added to the CommonCartridgeFileBuilder', () => { + const setup = () => { + const createElementSpy = jest.spyOn(CommonCartridgeElementFactory, 'createElement'); + const metadataProps = createCommonCartridgeMetadataElementProps(); - expect(manifest).toBeDefined(); - }); + return { metadataProps, createElementSpy }; + }; - it('should have included the resource in organization folder', async () => { - const { archive, organizationOptions, resourceProps } = await setup(); + it('should set the metadata element', () => { + const { metadataProps, createElementSpy } = setup(); - const resource = getFileContentAsString( - archive, - `${organizationOptions.identifier}/${resourceProps.identifier}.html` - ); + sut.addMetadata(metadataProps); - expect(resource).toBeDefined(); + expect(createElementSpy).toHaveBeenCalledWith({ ...metadataProps, version: builderProps.version }); }); + }); + }); - it('should have included the resource in sub-organization folder', async () => { - const { archive, organizationOptions, resourceProps } = await setup(); - - const resource = getFileContentAsString( - archive, - `${organizationOptions.identifier}/${organizationOptions.identifier}/${resourceProps.identifier}.html` - ); + describe('createOrganization', () => { + describe('when an organization is created in the CommonCartridgeFileBuilder', () => { + const setup = () => { + const organizationProps = createCommonCartridgeOrganizationProps(); - expect(resource).toBeDefined(); - }); + return { organizationProps }; + }; - it('should have included the resource in sub-sub-organization folder', async () => { - const { archive, organizationOptions, resourceProps } = await setup(); + it('should create and return an organization node', () => { + const { organizationProps } = setup(); - const resource = getFileContentAsString( - archive, - `${organizationOptions.identifier}/${organizationOptions.identifier}/${organizationOptions.identifier}/${resourceProps.identifier}.html` - ); + const organizationNode = sut.createOrganization(organizationProps); - expect(resource).toBeDefined(); + expect(organizationNode).toBeInstanceOf(CommonCartridgeOrganizationNode); }); }); + }); - describe('when metadata has not been provide', () => { - const sut = new CommonCartridgeFileBuilder({ - version: CommonCartridgeVersion.V_1_1_0, - identifier: faker.string.uuid(), + describe('build', () => { + describe('when metadata has not been provided', () => { + it('should throw MissingMetadataLoggableException', () => { + expect(() => { + sut.build(); + }).toThrow(MissingMetadataLoggableException); }); + }); + + describe('when metadata has been provided', () => { + const setup = () => { + const metadataProps = createCommonCartridgeMetadataElementProps(); + const organizationProps = createCommonCartridgeOrganizationProps(); + const resourceProps = createCommonCartridgeWebLinkResourceProps(); + + return { metadataProps, organizationProps, resourceProps }; + }; + + it('should build the common cartridge file', () => { + const { metadataProps, organizationProps, resourceProps } = setup(); + + sut.addMetadata(metadataProps); + + const org = sut.createOrganization(organizationProps); + + org.addResource(resourceProps); + + const result = sut.build(); - it('should throw an error', async () => { - await expect(sut.build()).rejects.toThrow('Metadata is not defined'); + expect(result).toBeDefined(); }); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-file-builder.ts b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-file-builder.ts index 0f605b5561d..35e84aa7115 100644 --- a/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-file-builder.ts +++ b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-file-builder.ts @@ -1,80 +1,81 @@ import AdmZip from 'adm-zip'; -import { CommonCartridgeResourceType, CommonCartridgeVersion } from '../common-cartridge.enums'; +import { + CommonCartridgeElementType, + CommonCartridgeResourceType, + CommonCartridgeVersion, +} from '../common-cartridge.enums'; import { CommonCartridgeElementFactory, CommonCartridgeElementProps, } from '../elements/common-cartridge-element-factory'; -import { CommonCartridgeElement, CommonCartridgeResource } from '../interfaces'; +import { MissingMetadataLoggableException } from '../errors'; +import { CommonCartridgeElement } from '../interfaces'; import { CommonCartridgeResourceFactory } from '../resources/common-cartridge-resource-factory'; -import { OmitVersion } from '../utils'; import { - CommonCartridgeOrganizationBuilder, - CommonCartridgeOrganizationBuilderOptions, -} from './common-cartridge-organization-builder'; + CommonCartridgeOrganizationNode, + CommonCartridgeOrganizationNodeProps, +} from './common-cartridge-organization-node'; +import { CommonCartridgeResourceCollectionBuilder } from './common-cartridge-resource-collection-builder'; export type CommonCartridgeFileBuilderProps = { version: CommonCartridgeVersion; identifier: string; }; -export class CommonCartridgeFileBuilder { - private readonly archive: AdmZip = new AdmZip(); +export type CommonCartridgeOrganizationProps = Omit; - private readonly organizationBuilders = new Array(); +export class CommonCartridgeFileBuilder { + private readonly resourcesBuilder: CommonCartridgeResourceCollectionBuilder = + new CommonCartridgeResourceCollectionBuilder(); - private readonly resources = new Array(); + private readonly organizationsRoot: CommonCartridgeOrganizationNode[] = []; - private metadata?: CommonCartridgeElement; + private metadataElement: CommonCartridgeElement | null = null; constructor(private readonly props: CommonCartridgeFileBuilderProps) {} - public addMetadata(props: CommonCartridgeElementProps): CommonCartridgeFileBuilder { - this.metadata = CommonCartridgeElementFactory.createElement({ + public addMetadata(metadataProps: CommonCartridgeElementProps): void { + this.metadataElement = CommonCartridgeElementFactory.createElement({ version: this.props.version, - ...props, + ...metadataProps, }); - - return this; } - public addOrganization( - props: OmitVersion - ): CommonCartridgeOrganizationBuilder { - const builder = new CommonCartridgeOrganizationBuilder( - { ...props, version: this.props.version }, - (resource: CommonCartridgeResource) => this.resources.push(resource) + public createOrganization(organizationProps: CommonCartridgeOrganizationProps): CommonCartridgeOrganizationNode { + const organization = new CommonCartridgeOrganizationNode( + { ...organizationProps, version: this.props.version, type: CommonCartridgeElementType.ORGANIZATION }, + this.resourcesBuilder, + null ); - this.organizationBuilders.push(builder); + this.organizationsRoot.push(organization); - return builder; + return organization; } - public async build(): Promise { - if (!this.metadata) { - throw new Error('Metadata is not defined'); + public build(): Buffer { + if (!this.metadataElement) { + throw new MissingMetadataLoggableException(); } - const organizations = this.organizationBuilders.map((builder) => builder.build()); + const archive = new AdmZip(); + const organizations = this.organizationsRoot.map((organization) => organization.build()); + const resources = this.resourcesBuilder.build(); const manifest = CommonCartridgeResourceFactory.createResource({ type: CommonCartridgeResourceType.MANIFEST, version: this.props.version, identifier: this.props.identifier, - metadata: this.metadata, + metadata: this.metadataElement, organizations, - resources: this.resources, + resources, }); - for (const resources of this.resources) { - if (!resources.canInline()) { - this.archive.addFile(resources.getFilePath(), Buffer.from(resources.getFileContent())); - } - } - - this.archive.addFile(manifest.getFilePath(), Buffer.from(manifest.getFileContent())); + archive.addFile(manifest.getFilePath(), Buffer.from(manifest.getFileContent())); - const buffer = await this.archive.toBufferPromise(); + resources.forEach((resource) => { + archive.addFile(resource.getFilePath(), Buffer.from(resource.getFileContent())); + }); - return buffer; + return archive.toBuffer(); } } diff --git a/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-organization-builder.spec.ts b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-organization-builder.spec.ts deleted file mode 100644 index ba9a36001ae..00000000000 --- a/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-organization-builder.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { faker } from '@faker-js/faker/locale/af_ZA'; -import { createCommonCartridgeWebContentResourcePropsV110 } from '../../testing/common-cartridge-resource-props.factory'; -import { CommonCartridgeVersion } from '../common-cartridge.enums'; -import { CommonCartridgeElement, CommonCartridgeResource } from '../interfaces'; -import { - CommonCartridgeOrganizationBuilder, - CommonCartridgeOrganizationBuilderOptions, -} from './common-cartridge-organization-builder'; - -describe('CommonCartridgeOrganizationBuilder', () => { - describe('build', () => { - describe('when building a Common Cartridge organization with resources', () => { - const setup = () => { - const resources = new Array(); - - const organizationOptions: CommonCartridgeOrganizationBuilderOptions = { - identifier: faker.string.uuid(), - title: faker.lorem.words(), - }; - - const resourceProps = createCommonCartridgeWebContentResourcePropsV110(); - - const sut = new CommonCartridgeOrganizationBuilder( - { - ...organizationOptions, - version: CommonCartridgeVersion.V_1_1_0, - }, - (resource) => resources.push(resource) - ) - .addResource(resourceProps) - .addSubOrganization(organizationOptions) - .addResource(resourceProps) - .addSubOrganization(organizationOptions) - .addResource(resourceProps); - - return { sut, resources }; - }; - - it('should return a common cartridge element', () => { - const { sut, resources } = setup(); - - const element = sut.build(); - - expect(element).toBeInstanceOf(CommonCartridgeElement); - expect(resources.length).toBe(3); - }); - }); - - describe('when building a Common Cartridge organization with items', () => { - const setup = () => { - const resources = new Array(); - - const organizationOptions: CommonCartridgeOrganizationBuilderOptions = { - identifier: faker.string.uuid(), - title: faker.lorem.words(), - }; - - const resourceProps = createCommonCartridgeWebContentResourcePropsV110(); - - const sut = new CommonCartridgeOrganizationBuilder( - { - ...organizationOptions, - version: CommonCartridgeVersion.V_1_1_0, - }, - (resource) => resources.push(resource) - ) - .addResource(resourceProps) - .addSubOrganization(organizationOptions) - .addResource(resourceProps) - .addSubOrganization(organizationOptions) - .addResource(resourceProps) - .addResource(resourceProps); - - return { sut, resources }; - }; - - it('should return a common cartridge element', () => { - const { sut, resources } = setup(); - - const element = sut.build(); - - expect(element).toBeInstanceOf(CommonCartridgeElement); - expect(resources.length).toBe(4); - }); - }); - }); -}); diff --git a/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-organization-builder.ts b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-organization-builder.ts deleted file mode 100644 index 20ff6d3e528..00000000000 --- a/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-organization-builder.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { CommonCartridgeElementType, CommonCartridgeVersion } from '../common-cartridge.enums'; -import { CommonCartridgeElementFactory } from '../elements/common-cartridge-element-factory'; -import { CommonCartridgeElement } from '../interfaces/common-cartridge-element.interface'; -import { CommonCartridgeResource } from '../interfaces/common-cartridge-resource.interface'; -import { - CommonCartridgeResourceFactory, - CommonCartridgeResourceProps, -} from '../resources/common-cartridge-resource-factory'; -import { OmitVersionAndFolder } from '../utils'; - -export type CommonCartridgeOrganizationBuilderOptions = - OmitVersionAndFolder; - -type CommonCartridgeOrganizationBuilderOptionsInternal = { - version: CommonCartridgeVersion; - identifier: string; - title: string; - folder?: string; -}; - -export class CommonCartridgeOrganizationBuilder { - private readonly resources: CommonCartridgeResource[] = []; - - private readonly subOrganizations: CommonCartridgeOrganizationBuilder[] = []; - - constructor( - protected readonly options: CommonCartridgeOrganizationBuilderOptionsInternal, - private readonly addResourceToFileBuilder: (resource: CommonCartridgeResource) => void - ) {} - - private get folder(): string { - return this.options.folder ? `${this.options.folder}/${this.options.identifier}` : this.options.identifier; - } - - public addSubOrganization( - options: OmitVersionAndFolder - ): CommonCartridgeOrganizationBuilder { - const subOrganization = new CommonCartridgeOrganizationBuilder( - { ...options, version: this.options.version, folder: this.folder }, - (resource: CommonCartridgeResource) => this.addResourceToFileBuilder(resource) - ); - - this.subOrganizations.push(subOrganization); - - return subOrganization; - } - - public addResource(props: CommonCartridgeResourceProps): CommonCartridgeOrganizationBuilder { - const resource = CommonCartridgeResourceFactory.createResource({ - version: this.options.version, - folder: this.folder, - ...props, - }); - - this.resources.push(resource); - this.addResourceToFileBuilder(resource); - - return this; - } - - public build(): CommonCartridgeElement { - const organizationElement = CommonCartridgeElementFactory.createElement({ - type: CommonCartridgeElementType.ORGANIZATION, - version: this.options.version, - identifier: this.options.identifier, - title: this.options.title, - items: this.buildItems(), - }); - - return organizationElement; - } - - private buildItems(): (CommonCartridgeElement | CommonCartridgeResource)[] { - if (this.resources.length === 1 && this.subOrganizations.length === 0) { - return [...this.resources]; - } - - const items = [...this.resources, ...this.subOrganizations.map((subOrganization) => subOrganization.build())]; - - return items; - } -} diff --git a/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-organization-node.spec.ts b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-organization-node.spec.ts new file mode 100644 index 00000000000..204ae5d1685 --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-organization-node.spec.ts @@ -0,0 +1,157 @@ +import { createMock } from '@golevelup/ts-jest'; +import { createCommonCartridgeOrganizationNodeProps } from '../../testing/common-cartridge-element-props.factory'; +import { createCommonCartridgeWebLinkResourceProps } from '../../testing/common-cartridge-resource-props.factory'; +import { CommonCartridgeElement } from '../interfaces'; +import { CommonCartridgeOrganizationNode } from './common-cartridge-organization-node'; +import { CommonCartridgeResourceCollectionBuilder } from './common-cartridge-resource-collection-builder'; + +describe('CommonCartridgeOrganizationNode', () => { + const setupOrganizationNodeProps = () => { + const props = createCommonCartridgeOrganizationNodeProps(); + + return props; + }; + const setupResourcesMock = () => { + const mock = createMock(); + + return mock; + }; + const setupResourceProps = () => { + const resourceProps = createCommonCartridgeWebLinkResourceProps(); + + return resourceProps; + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('folder', () => { + describe('when organization node has no parent', () => { + const setup = () => { + const resourcesMock = setupResourcesMock(); + const props = setupOrganizationNodeProps(); + const sut = new CommonCartridgeOrganizationNode(props, resourcesMock, null); + + return { sut, props }; + }; + + it('should return its own id as folder', () => { + const { sut, props } = setup(); + + const result = sut.folder; + + expect(result).toBe(props.identifier); + }); + }); + + describe('when organization node has parent', () => { + // AI next 15 lines + const setup = () => { + const resourcesMock = setupResourcesMock(); + const props = setupOrganizationNodeProps(); + const parentProps = setupOrganizationNodeProps(); + const parent = new CommonCartridgeOrganizationNode(parentProps, resourcesMock, null); + const sut = new CommonCartridgeOrganizationNode(props, resourcesMock, parent); + + return { sut, props, parentProps }; + }; + + it('should construct folder path from parent and own identifier', () => { + const { sut, props, parentProps } = setup(); + + const result = sut.folder; + + expect(result).toBe(`${parentProps.identifier}/${props.identifier}`); + }); + }); + }); + + describe('createChild', () => { + describe('when creating a child organization node', () => { + const setup = () => { + const resourcesMock = setupResourcesMock(); + const childrenMock = createMock(); + const props = setupOrganizationNodeProps(); + const childProps = setupOrganizationNodeProps(); + const sut = new CommonCartridgeOrganizationNode(props, resourcesMock, null); + + Reflect.set(sut, 'children', childrenMock); + + return { sut, childProps, childrenMock }; + }; + + it('should return a new organization node', () => { + const { sut, childProps } = setup(); + + const result = sut.createChild(childProps); + + expect(result).toBeInstanceOf(CommonCartridgeOrganizationNode); + expect(result).not.toBe(sut); + }); + + it('should add new organization node to children', () => { + const { sut, childProps, childrenMock } = setup(); + + const result = sut.createChild(childProps); + + expect(result).toBeDefined(); + expect(childrenMock.push).toHaveBeenCalledTimes(1); + }); + }); + }); + + describe('addResource', () => { + describe('when adding a resource to an organization node', () => { + const setup = () => { + const resourcesMock = setupResourcesMock(); + const childrenMock = createMock(); + const props = setupOrganizationNodeProps(); + const resourceProps = setupResourceProps(); + const sut = new CommonCartridgeOrganizationNode(props, resourcesMock, null); + + return { sut, resourceProps, childrenMock, resourcesMock }; + }; + + it('should call addResource on resource collection builder', () => { + const { sut, resourceProps, resourcesMock } = setup(); + + sut.addResource(resourceProps); + + expect(resourcesMock.addResource).toHaveBeenCalledTimes(1); + }); + }); + }); + + describe('build', () => { + describe('when building an organization node', () => { + const setup = () => { + const resourcesMock = setupResourcesMock(); + const props = setupOrganizationNodeProps(); + const sut = new CommonCartridgeOrganizationNode(props, resourcesMock, null); + const childProps = setupOrganizationNodeProps(); + const childNode = sut.createChild(childProps); + const childNodeBuildSpy = jest.spyOn(childNode, 'build'); + + return { sut, childNodeBuildSpy }; + }; + + it('should return an common cartridge element', () => { + const { sut } = setup(); + + const result = sut.build(); + + expect(result).toBeInstanceOf(CommonCartridgeElement); + }); + + it('should build all children', () => { + const { sut, childNodeBuildSpy } = setup(); + + const result = sut.build(); + + expect(result).toBeDefined(); + expect(childNodeBuildSpy).toHaveBeenCalledTimes(1); + }); + }); + }); +}); diff --git a/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-organization-node.ts b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-organization-node.ts new file mode 100644 index 00000000000..9185d9db589 --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-organization-node.ts @@ -0,0 +1,71 @@ +import { CommonCartridgeElementType } from '../common-cartridge.enums'; +import { CommonCartridgeElementFactory } from '../elements/common-cartridge-element-factory'; +import { CommonCartridgeOrganizationElementPropsV110 } from '../elements/v1.1.0'; +import { CommonCartridgeOrganizationElementPropsV130 } from '../elements/v1.3.0'; +import { CommonCartridgeElement } from '../interfaces'; +import { CommonCartridgeResourceProps } from '../resources/common-cartridge-resource-factory'; +import type { CommonCartridgeOrganizationProps } from './common-cartridge-file-builder'; +import { CommonCartridgeResourceCollectionBuilder } from './common-cartridge-resource-collection-builder'; +import { CommonCartridgeResourceNode } from './common-cartridge-resource-node'; + +export type CommonCartridgeOrganizationNodeProps = Omit< + CommonCartridgeOrganizationElementPropsV110 | CommonCartridgeOrganizationElementPropsV130, + 'items' +>; + +export class CommonCartridgeOrganizationNode { + private readonly parent: CommonCartridgeOrganizationNode | null = null; + + private readonly children: (CommonCartridgeOrganizationNode | CommonCartridgeResourceNode)[] = []; + + constructor( + private readonly props: CommonCartridgeOrganizationNodeProps, + private readonly resourcesBuilder: CommonCartridgeResourceCollectionBuilder, + parent: CommonCartridgeOrganizationNode | null + ) { + this.parent = parent; + } + + public get folder(): string { + return this.parent ? `${this.parent.folder}/${this.props.identifier}` : this.props.identifier; + } + + public createChild(childProps: CommonCartridgeOrganizationProps): CommonCartridgeOrganizationNode { + const organization = new CommonCartridgeOrganizationNode( + { + ...childProps, + version: this.props.version, + type: CommonCartridgeElementType.ORGANIZATION, + }, + this.resourcesBuilder, + this + ); + + this.children.push(organization); + + return organization; + } + + public addResource(resourceProps: CommonCartridgeResourceProps): void { + const resource = new CommonCartridgeResourceNode( + { + ...resourceProps, + version: this.props.version, + }, + this + ); + + this.children.push(resource); + this.resourcesBuilder.addResource(resource); + } + + public build(): CommonCartridgeElement { + const organization = CommonCartridgeElementFactory.createElement({ + ...this.props, + version: this.props.version, + items: this.children.map((child) => child.build()), + }); + + return organization; + } +} diff --git a/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-resource-collection-builder.spec.ts b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-resource-collection-builder.spec.ts new file mode 100644 index 00000000000..c7c6d1e8020 --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-resource-collection-builder.spec.ts @@ -0,0 +1,74 @@ +import { createMock } from '@golevelup/ts-jest'; +import { createCommonCartridgeOrganizationNodeProps } from '../../testing/common-cartridge-element-props.factory'; +import { createCommonCartridgeWebContentResourceProps } from '../../testing/common-cartridge-resource-props.factory'; +import { CommonCartridgeVersion } from '../common-cartridge.enums'; +import { CommonCartridgeOrganizationNode } from './common-cartridge-organization-node'; +import { CommonCartridgeResourceCollectionBuilder } from './common-cartridge-resource-collection-builder'; +import { CommonCartridgeResourceNode, CommonCartridgeResourceNodeProps } from './common-cartridge-resource-node'; + +describe('CommonCartridgeResourceCollectionBuilder', () => { + let sut: CommonCartridgeResourceCollectionBuilder; + + const setupResourceNode = () => { + const resourceNodeProps: CommonCartridgeResourceNodeProps = { + ...createCommonCartridgeWebContentResourceProps(), + version: CommonCartridgeVersion.V_1_1_0, + }; + const organizationNodeProps = createCommonCartridgeOrganizationNodeProps(); + const organizationNode = new CommonCartridgeOrganizationNode(organizationNodeProps, sut, null); + const resourceNode = new CommonCartridgeResourceNode(resourceNodeProps, organizationNode); + + return resourceNode; + }; + + beforeEach(() => { + sut = new CommonCartridgeResourceCollectionBuilder(); + jest.clearAllMocks(); + }); + + describe('addResource', () => { + describe('when a resource is added to the CommonCartridgeResourceCollectionBuilder', () => { + const setup = () => { + const resourceNode = setupResourceNode(); + const resourceNodesMock = createMock(); + + Reflect.set(sut, 'resourceNodes', resourceNodesMock); + + return { resourceNode, resourceNodesMock }; + }; + + it('should add the resource node to the collection', () => { + const { resourceNode, resourceNodesMock } = setup(); + + sut.addResource(resourceNode); + + expect(resourceNodesMock.push).toHaveBeenCalledTimes(1); + expect(resourceNodesMock.push).toHaveBeenCalledWith(resourceNode); + }); + }); + }); + + describe('build', () => { + describe('when build method is called', () => { + const setup = () => { + const resourceNode1 = setupResourceNode(); + const resourceNode2 = setupResourceNode(); + + return { resourceNode1, resourceNode2 }; + }; + + it('should return the resource collection', () => { + const { resourceNode1, resourceNode2 } = setup(); + + sut.addResource(resourceNode1); + sut.addResource(resourceNode2); + + const resources = sut.build(); + + expect(resources).toHaveLength(2); + expect(resources).toContainEqual(resourceNode1.build()); + expect(resources).toContainEqual(resourceNode2.build()); + }); + }); + }); +}); diff --git a/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-resource-collection-builder.ts b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-resource-collection-builder.ts new file mode 100644 index 00000000000..62b4b082480 --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-resource-collection-builder.ts @@ -0,0 +1,16 @@ +import { CommonCartridgeResource } from '../interfaces'; +import { CommonCartridgeResourceNode } from './common-cartridge-resource-node'; + +export class CommonCartridgeResourceCollectionBuilder { + private readonly resourceNodes: CommonCartridgeResourceNode[] = []; + + public addResource(resourceNode: CommonCartridgeResourceNode): void { + this.resourceNodes.push(resourceNode); + } + + public build(): CommonCartridgeResource[] { + const resources = this.resourceNodes.map((resource) => resource.build()); + + return resources; + } +} diff --git a/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-resource-node.spec.ts b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-resource-node.spec.ts new file mode 100644 index 00000000000..4cf86f6bb60 --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-resource-node.spec.ts @@ -0,0 +1,46 @@ +import { createCommonCartridgeOrganizationNodeProps } from '../../testing/common-cartridge-element-props.factory'; +import { createCommonCartridgeWebLinkResourceProps } from '../../testing/common-cartridge-resource-props.factory'; +import { CommonCartridgeVersion } from '../common-cartridge.enums'; +import { CommonCartridgeResource } from '../interfaces'; +import { CommonCartridgeOrganizationNode } from './common-cartridge-organization-node'; +import { CommonCartridgeResourceCollectionBuilder } from './common-cartridge-resource-collection-builder'; +import { CommonCartridgeResourceNode, CommonCartridgeResourceNodeProps } from './common-cartridge-resource-node'; + +describe('CommonCartridgeResourceNode', () => { + let sut: CommonCartridgeResourceNode; + + describe('build', () => { + describe('when build is called', () => { + const setup = () => { + const resourceNodeProps: CommonCartridgeResourceNodeProps = { + ...createCommonCartridgeWebLinkResourceProps(), + version: CommonCartridgeVersion.V_1_1_0, + }; + + const organizationNodeProps = createCommonCartridgeOrganizationNodeProps(); + + const resourceCollectionBuilder: CommonCartridgeResourceCollectionBuilder = + new CommonCartridgeResourceCollectionBuilder(); + + const organizationNode = new CommonCartridgeOrganizationNode( + organizationNodeProps, + resourceCollectionBuilder, + null + ); + + return { resourceNodeProps, organizationNode }; + }; + + it('should return a CommonCartridgeResource', () => { + const { resourceNodeProps, organizationNode } = setup(); + + sut = new CommonCartridgeResourceNode(resourceNodeProps, organizationNode); + + const result = sut.build(); + + expect(result).toBeDefined(); + expect(result).toBeInstanceOf(CommonCartridgeResource); + }); + }); + }); +}); diff --git a/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-resource-node.ts b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-resource-node.ts new file mode 100644 index 00000000000..912c969a59a --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/builders/common-cartridge-resource-node.ts @@ -0,0 +1,23 @@ +import { CommonCartridgeVersion } from '../common-cartridge.enums'; +import { CommonCartridgeResource } from '../interfaces'; +import { + CommonCartridgeResourceFactory, + CommonCartridgeResourceProps, +} from '../resources/common-cartridge-resource-factory'; +import type { CommonCartridgeOrganizationNode } from './common-cartridge-organization-node'; + +export type CommonCartridgeResourceNodeProps = CommonCartridgeResourceProps & { version: CommonCartridgeVersion }; + +export class CommonCartridgeResourceNode { + private readonly parent: CommonCartridgeOrganizationNode; + + constructor(private readonly props: CommonCartridgeResourceNodeProps, parent: CommonCartridgeOrganizationNode) { + this.parent = parent; + } + + public build(): CommonCartridgeResource { + const resource = CommonCartridgeResourceFactory.createResource({ ...this.props, folder: this.parent.folder }); + + return resource; + } +} diff --git a/apps/server/src/modules/common-cartridge/export/common-cartridge.enums.ts b/apps/server/src/modules/common-cartridge/export/common-cartridge.enums.ts index 8e474d7c3df..183cc0f31d4 100644 --- a/apps/server/src/modules/common-cartridge/export/common-cartridge.enums.ts +++ b/apps/server/src/modules/common-cartridge/export/common-cartridge.enums.ts @@ -21,8 +21,10 @@ export enum CommonCartridgeIntendedUseType { } export enum CommonCartridgeElementType { + MANIFEST = 'manifest', METADATA = 'metadata', ORGANIZATION = 'organization', - RESOURCES_WRAPPER = 'resourceswrapper', - ORGANIZATIONS_WRAPPER = 'organizationswrapper', + ORGANIZATIONS_WRAPPER = 'organizations-wrapper', + RESOURCES_WRAPPER = 'resources-wrapper', + RESOURCE = 'resource', } diff --git a/apps/server/src/modules/common-cartridge/export/common-cartridge.guard.spec.ts b/apps/server/src/modules/common-cartridge/export/common-cartridge.guard.spec.ts new file mode 100644 index 00000000000..0739b6307b3 --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/common-cartridge.guard.spec.ts @@ -0,0 +1,29 @@ +import { CommonCartridgeGuard } from './common-cartridge.guard'; + +describe('CommonCartridgeGuard', () => { + describe('checkIntendedUse', () => { + describe('when intended use is supported', () => { + const supportedIntendedUses = ['use1', 'use2', 'use3']; + + it('should not throw an exception', () => { + const intendedUse = 'use1'; + + expect(() => { + CommonCartridgeGuard.checkIntendedUse(intendedUse, supportedIntendedUses); + }).not.toThrow(); + }); + }); + + describe('when intended use is not supported', () => { + const supportedIntendedUses = ['use1', 'use2', 'use3']; + + it('should throw an exception', () => { + const intendedUse = 'use4'; + + expect(() => { + CommonCartridgeGuard.checkIntendedUse(intendedUse, supportedIntendedUses); + }).toThrow(); + }); + }); + }); +}); diff --git a/apps/server/src/modules/common-cartridge/export/common-cartridge.guard.ts b/apps/server/src/modules/common-cartridge/export/common-cartridge.guard.ts new file mode 100644 index 00000000000..2b0a012cef6 --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/common-cartridge.guard.ts @@ -0,0 +1,9 @@ +import { IntendedUseNotSupportedLoggableException } from './errors'; + +export class CommonCartridgeGuard { + public static checkIntendedUse(intendedUse: string, supportedIntendedUses: string[]): void { + if (!supportedIntendedUses.includes(intendedUse)) { + throw new IntendedUseNotSupportedLoggableException(intendedUse); + } + } +} diff --git a/apps/server/src/modules/common-cartridge/export/elements/abstract/common-cartridge-organizations-wrapper-element.ts b/apps/server/src/modules/common-cartridge/export/elements/abstract/common-cartridge-organizations-wrapper-element.ts new file mode 100644 index 00000000000..e30c80e0d87 --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/elements/abstract/common-cartridge-organizations-wrapper-element.ts @@ -0,0 +1,53 @@ +import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; +import { CommonCartridgeElement, XmlObject } from '../../interfaces'; + +export type CommonCartridgeOrganizationsWrapperElementProps = { + type: CommonCartridgeElementType.ORGANIZATIONS_WRAPPER; + version: CommonCartridgeVersion; + items: CommonCartridgeElement[]; +}; + +/** + * This abstract class was created to reduce code duplication and + * keep the SonarCloud code duplication rate below 3%. + */ +export abstract class CommonCartridgeOrganizationsWrapperElement extends CommonCartridgeElement { + constructor(private readonly props: CommonCartridgeOrganizationsWrapperElementProps) { + super(props); + } + + abstract getSupportedVersion(): CommonCartridgeVersion; + + public getManifestXmlObject(elementType: CommonCartridgeElementType): XmlObject { + switch (elementType) { + case CommonCartridgeElementType.ORGANIZATIONS_WRAPPER: + return this.getManifestXmlObjectInternal(); + default: + throw new ElementTypeNotSupportedLoggableException(elementType); + } + } + + private getManifestXmlObjectInternal(): XmlObject { + return { + organization: [ + { + $: { + identifier: 'org-1', + structure: 'rooted-hierarchy', + }, + item: [ + { + $: { + identifier: 'LearningModules', + }, + item: this.props.items.map((items) => + items.getManifestXmlObject(CommonCartridgeElementType.ORGANIZATION) + ), + }, + ], + }, + ], + }; + } +} diff --git a/apps/server/src/modules/common-cartridge/export/elements/abstract/common-cartridge-resources-wrapper-element.ts b/apps/server/src/modules/common-cartridge/export/elements/abstract/common-cartridge-resources-wrapper-element.ts new file mode 100644 index 00000000000..06059be68ea --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/elements/abstract/common-cartridge-resources-wrapper-element.ts @@ -0,0 +1,40 @@ +import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; +import { CommonCartridgeElement, XmlObject } from '../../interfaces'; + +export type CommonCartridgeResourcesWrapperElementProps = { + type: CommonCartridgeElementType.RESOURCES_WRAPPER; + version: CommonCartridgeVersion; + items: CommonCartridgeElement[]; +}; + +/** + * This abstract class was created to reduce code duplication and + * keep the SonarCloud code duplication rate below 3%. + */ +export abstract class CommonCartridgeResourcesWrapperElement extends CommonCartridgeElement { + constructor(private readonly props: CommonCartridgeResourcesWrapperElementProps) { + super(props); + } + + abstract getSupportedVersion(): CommonCartridgeVersion; + + public getManifestXmlObject(elementType: CommonCartridgeElementType): XmlObject { + switch (elementType) { + case CommonCartridgeElementType.RESOURCES_WRAPPER: + return this.getManifestXmlObjectInternal(); + default: + throw new ElementTypeNotSupportedLoggableException(elementType); + } + } + + private getManifestXmlObjectInternal(): XmlObject { + return { + resources: [ + { + resource: this.props.items.map((items) => items.getManifestXmlObject(CommonCartridgeElementType.RESOURCE)), + }, + ], + }; + } +} diff --git a/apps/server/src/modules/common-cartridge/export/elements/common-cartridge-element-factory.spec.ts b/apps/server/src/modules/common-cartridge/export/elements/common-cartridge-element-factory.spec.ts index b891d1aa49a..2aad5899e57 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/common-cartridge-element-factory.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/common-cartridge-element-factory.spec.ts @@ -1,9 +1,9 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { createCommonCartridgeMetadataElementPropsV110, createCommonCartridgeMetadataElementPropsV130, } from '../../testing/common-cartridge-element-props.factory'; import { CommonCartridgeElementType, CommonCartridgeVersion } from '../common-cartridge.enums'; +import { VersionNotSupportedLoggableException } from '../errors'; import { CommonCartridgeElementFactory } from './common-cartridge-element-factory'; import { CommonCartridgeMetadataElementPropsV110, CommonCartridgeMetadataElementV110 } from './v1.1.0'; import { CommonCartridgeMetadataElementV130 } from './v1.3.0'; @@ -36,14 +36,14 @@ describe('CommonCartridgeElementFactory', () => { CommonCartridgeVersion.V_1_4_0, ]; - it('should throw InternalServerErrorException', () => { + it('should throw VersionNotSupportedLoggableException', () => { notSupportedVersions.forEach((version) => { expect(() => CommonCartridgeElementFactory.createElement({ version, type: CommonCartridgeElementType.METADATA, } as CommonCartridgeMetadataElementPropsV110) - ).toThrow(InternalServerErrorException); + ).toThrow(VersionNotSupportedLoggableException); }); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/elements/common-cartridge-element-factory.ts b/apps/server/src/modules/common-cartridge/export/elements/common-cartridge-element-factory.ts index 046f7033fde..7765e79d7ac 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/common-cartridge-element-factory.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/common-cartridge-element-factory.ts @@ -1,6 +1,7 @@ import { CommonCartridgeVersion } from '../common-cartridge.enums'; +import { VersionNotSupportedLoggableException } from '../errors'; import { CommonCartridgeElement } from '../interfaces'; -import { OmitVersionAndFolder, createVersionNotSupportedError } from '../utils'; +import { OmitVersionAndFolder } from '../utils'; import { CommonCartridgeElementFactoryV110, CommonCartridgeMetadataElementPropsV110, @@ -46,7 +47,7 @@ export class CommonCartridgeElementFactory { case CommonCartridgeVersion.V_1_3_0: return CommonCartridgeElementFactoryV130.createElement(props); default: - throw createVersionNotSupportedError(version); + throw new VersionNotSupportedLoggableException(version); } } } diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-element-factory.spec.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-element-factory.spec.ts index 3c43cf3a830..3c4fceabe83 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-element-factory.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-element-factory.spec.ts @@ -1,4 +1,3 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { createCommonCartridgeMetadataElementPropsV110, createCommonCartridgeOrganizationElementPropsV110, @@ -6,6 +5,7 @@ import { createCommonCartridgeResourcesWrapperElementPropsV110, } from '../../../testing/common-cartridge-element-props.factory'; import { CommonCartridgeElementType } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeElementFactoryV110 } from './common-cartridge-element-factory'; import { CommonCartridgeMetadataElementV110 } from './common-cartridge-metadata-element'; import { CommonCartridgeOrganizationElementV110 } from './common-cartridge-organization-element'; @@ -53,9 +53,9 @@ describe('CommonCartridgeElementFactoryV110', () => { notSupportedProps.type = 'not-supported' as CommonCartridgeElementType.METADATA; - it('should throw error', () => { + it('should throw ElementTypeNotSupportedLoggableException', () => { expect(() => CommonCartridgeElementFactoryV110.createElement(notSupportedProps)).toThrow( - InternalServerErrorException + ElementTypeNotSupportedLoggableException ); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-element-factory.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-element-factory.ts index 88095cf218a..b623cd277f8 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-element-factory.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-element-factory.ts @@ -1,6 +1,6 @@ import { CommonCartridgeElementType } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeElement } from '../../interfaces'; -import { createElementTypeNotSupportedError } from '../../utils'; import { CommonCartridgeMetadataElementPropsV110, CommonCartridgeMetadataElementV110, @@ -39,7 +39,7 @@ export class CommonCartridgeElementFactoryV110 { case CommonCartridgeElementType.RESOURCES_WRAPPER: return new CommonCartridgeResourcesWrapperElementV110(props); default: - throw createElementTypeNotSupportedError(type); + throw new ElementTypeNotSupportedLoggableException(type); } } } diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-metadata-element.spec.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-metadata-element.spec.ts index cd12efa1590..1ab4e0421c4 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-metadata-element.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-metadata-element.spec.ts @@ -1,6 +1,6 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { createCommonCartridgeMetadataElementPropsV110 } from '../../../testing/common-cartridge-element-props.factory'; -import { CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException, VersionNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeMetadataElementV110 } from './common-cartridge-metadata-element'; describe('CommonCartridgeMetadataElementV110', () => { @@ -27,13 +27,15 @@ describe('CommonCartridgeMetadataElementV110', () => { notSupportedProps.version = CommonCartridgeVersion.V_1_3_0; it('should throw error', () => { - expect(() => new CommonCartridgeMetadataElementV110(notSupportedProps)).toThrow(InternalServerErrorException); + expect(() => new CommonCartridgeMetadataElementV110(notSupportedProps)).toThrow( + VersionNotSupportedLoggableException + ); }); }); }); describe('getManifestXmlObject', () => { - describe('when using Common Cartridge version 1.1', () => { + describe('when creating metadata xml object', () => { const setup = () => { const props = createCommonCartridgeMetadataElementPropsV110(); const sut = new CommonCartridgeMetadataElementV110(props); @@ -41,10 +43,10 @@ describe('CommonCartridgeMetadataElementV110', () => { return { sut, props }; }; - it('should return correct manifest xml object', () => { + it('should return metadata manifest fragment', () => { const { sut, props } = setup(); - const result = sut.getManifestXmlObject(); + const result = sut.getManifestXmlObject(CommonCartridgeElementType.METADATA); expect(result).toStrictEqual({ schema: 'IMS Common Cartridge', @@ -67,5 +69,21 @@ describe('CommonCartridgeMetadataElementV110', () => { }); }); }); + + describe('when using unsupported element type', () => { + const setup = () => { + const unknownElementType = 'unknown' as CommonCartridgeElementType; + const props = createCommonCartridgeMetadataElementPropsV110(); + const sut = new CommonCartridgeMetadataElementV110(props); + + return { sut, unknownElementType }; + }; + + it('should throw error', () => { + const { sut, unknownElementType } = setup(); + + expect(() => sut.getManifestXmlObject(unknownElementType)).toThrow(ElementTypeNotSupportedLoggableException); + }); + }); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-metadata-element.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-metadata-element.ts index c70cbd2c892..f51b842e011 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-metadata-element.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-metadata-element.ts @@ -1,5 +1,6 @@ import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; -import { CommonCartridgeElement } from '../../interfaces'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; +import { CommonCartridgeElement, XmlObject } from '../../interfaces'; export type CommonCartridgeMetadataElementPropsV110 = { type: CommonCartridgeElementType.METADATA; @@ -18,7 +19,16 @@ export class CommonCartridgeMetadataElementV110 extends CommonCartridgeElement { return CommonCartridgeVersion.V_1_1_0; } - public getManifestXmlObject(): Record { + public getManifestXmlObject(elementType: CommonCartridgeElementType): XmlObject { + switch (elementType) { + case CommonCartridgeElementType.METADATA: + return this.getManifestMetadataXmlObjectInternal(); + default: + throw new ElementTypeNotSupportedLoggableException(elementType); + } + } + + private getManifestMetadataXmlObjectInternal() { return { schema: 'IMS Common Cartridge', schemaversion: '1.1.0', diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-organization-element.spec.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-organization-element.spec.ts index 8e1be0c920f..f42cc140d5d 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-organization-element.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-organization-element.spec.ts @@ -1,7 +1,7 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { createCommonCartridgeOrganizationElementPropsV110 } from '../../../testing/common-cartridge-element-props.factory'; import { createCommonCartridgeWeblinkResourcePropsV110 } from '../../../testing/common-cartridge-resource-props.factory'; -import { CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException, VersionNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeResourceFactory } from '../../resources/common-cartridge-resource-factory'; import { CommonCartridgeElementFactory } from '../common-cartridge-element-factory'; import { CommonCartridgeOrganizationElementV110 } from './common-cartridge-organization-element'; @@ -31,25 +31,20 @@ describe('CommonCartridgeOrganizationElementV110', () => { it('should throw error', () => { expect(() => new CommonCartridgeOrganizationElementV110(notSupportedProps)).toThrowError( - InternalServerErrorException + VersionNotSupportedLoggableException ); }); }); }); describe('getManifestXmlObject', () => { - describe('when using Common Cartridge version 1.1.0', () => { + describe('when creating organization xml object', () => { const setup = () => { const resourceProps = createCommonCartridgeWeblinkResourcePropsV110(); - - const subOrganization1Props = createCommonCartridgeOrganizationElementPropsV110( - CommonCartridgeResourceFactory.createResource(resourceProps) - ); - + const subOrganization1Props = createCommonCartridgeOrganizationElementPropsV110(); const subOrganization2Props = createCommonCartridgeOrganizationElementPropsV110([ CommonCartridgeResourceFactory.createResource(resourceProps), ]); - const organizationProps = createCommonCartridgeOrganizationElementPropsV110([ CommonCartridgeElementFactory.createElement(subOrganization1Props), CommonCartridgeElementFactory.createElement(subOrganization2Props), @@ -60,10 +55,10 @@ describe('CommonCartridgeOrganizationElementV110', () => { return { sut, organizationProps, subOrganization1Props, subOrganization2Props, resourceProps }; }; - it('should return correct manifest xml object', () => { + it('should return organization manifest fragment', () => { const { sut, organizationProps, subOrganization1Props, subOrganization2Props, resourceProps } = setup(); - const result = sut.getManifestXmlObject(); + const result = sut.getManifestXmlObject(CommonCartridgeElementType.ORGANIZATION); expect(result).toStrictEqual({ $: { @@ -74,9 +69,9 @@ describe('CommonCartridgeOrganizationElementV110', () => { { $: { identifier: subOrganization1Props.identifier, - identifierref: resourceProps.identifier, }, title: subOrganization1Props.title, + item: [], }, { $: { @@ -97,5 +92,23 @@ describe('CommonCartridgeOrganizationElementV110', () => { }); }); }); + + describe('when using unsupported element type', () => { + const setup = () => { + const unknownElementType = 'unknown' as CommonCartridgeElementType; + const props = createCommonCartridgeOrganizationElementPropsV110(); + const sut = new CommonCartridgeOrganizationElementV110(props); + + return { sut, unknownElementType }; + }; + + it('should throw error', () => { + const { sut, unknownElementType } = setup(); + + expect(() => sut.getManifestXmlObject(unknownElementType)).toThrowError( + ElementTypeNotSupportedLoggableException + ); + }); + }); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-organization-element.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-organization-element.ts index ff304d6ea08..215f68d7a38 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-organization-element.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-organization-element.ts @@ -1,13 +1,13 @@ import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; -import { CommonCartridgeElement, CommonCartridgeResource } from '../../interfaces'; -import { createIdentifier } from '../../utils'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; +import { CommonCartridgeElement, CommonCartridgeResource, XmlObject } from '../../interfaces'; export type CommonCartridgeOrganizationElementPropsV110 = { type: CommonCartridgeElementType.ORGANIZATION; version: CommonCartridgeVersion; identifier: string; title: string; - items: CommonCartridgeResource | Array; + items: CommonCartridgeResource | Array; }; export class CommonCartridgeOrganizationElementV110 extends CommonCartridgeElement { @@ -19,35 +19,32 @@ export class CommonCartridgeOrganizationElementV110 extends CommonCartridgeEleme return CommonCartridgeVersion.V_1_1_0; } - public getManifestXmlObject(): Record { - if (this.props.items instanceof CommonCartridgeResource) { - return { - $: { - identifier: this.identifier, - identifierref: this.props.items.identifier, - }, - title: this.title, - }; + public getManifestXmlObject(elementType: CommonCartridgeElementType): XmlObject { + switch (elementType) { + case CommonCartridgeElementType.ORGANIZATION: + return this.getManifestXmlObjectInternal(); + default: + throw new ElementTypeNotSupportedLoggableException(elementType); } + } + + private getManifestXmlObjectInternal(): XmlObject { + const xmlObject = Array.isArray(this.props.items) + ? this.getManifestXmlObjectForMany(this.props.items) + : this.props.items.getManifestXmlObject(CommonCartridgeElementType.ORGANIZATION); - return { + return xmlObject; + } + + private getManifestXmlObjectForMany(items: Array): XmlObject { + const xmlObject = { $: { identifier: this.identifier, }, title: this.title, - item: this.props.items.map((item) => { - if (item instanceof CommonCartridgeResource) { - return { - $: { - identifier: createIdentifier(), - identifierref: item.identifier, - }, - title: item.title, - }; - } - - return item.getManifestXmlObject(); - }), + item: items.map((item) => item.getManifestXmlObject(CommonCartridgeElementType.ORGANIZATION)), }; + + return xmlObject; } } diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-organizations-wrapper-element.spec.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-organizations-wrapper-element.spec.ts index fb5ae465a28..2efc3000a3c 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-organizations-wrapper-element.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-organizations-wrapper-element.spec.ts @@ -1,9 +1,9 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { createCommonCartridgeOrganizationElementPropsV110, createCommonCartridgeOrganizationsWrapperElementPropsV110, } from '../../../testing/common-cartridge-element-props.factory'; -import { CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException, VersionNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeElementFactory } from '../common-cartridge-element-factory'; import { CommonCartridgeOrganizationsWrapperElementV110 } from './common-cartridge-organizations-wrapper-element'; @@ -31,15 +31,15 @@ describe('CommonCartridgeOrganizationsWrapperElementV110', () => { notSupportedProps.version = CommonCartridgeVersion.V_1_3_0; it('should throw error', () => { - expect(() => new CommonCartridgeOrganizationsWrapperElementV110(notSupportedProps)).toThrowError( - InternalServerErrorException + expect(() => new CommonCartridgeOrganizationsWrapperElementV110(notSupportedProps)).toThrow( + VersionNotSupportedLoggableException ); }); }); }); describe('getManifestXmlObject', () => { - describe('when using Common Cartridge version 1.1.0', () => { + describe('when creating organization wrapper xml object', () => { const setup = () => { const organizationProps = createCommonCartridgeOrganizationElementPropsV110(); const props = createCommonCartridgeOrganizationsWrapperElementPropsV110([ @@ -50,10 +50,10 @@ describe('CommonCartridgeOrganizationsWrapperElementV110', () => { return { sut, organizationProps }; }; - it('should return correct manifest xml object', () => { + it('should return organization wrapper manifest fragment', () => { const { sut, organizationProps } = setup(); - const result = sut.getManifestXmlObject(); + const result = sut.getManifestXmlObject(CommonCartridgeElementType.ORGANIZATIONS_WRAPPER); expect(result).toStrictEqual({ organization: [ @@ -83,5 +83,21 @@ describe('CommonCartridgeOrganizationsWrapperElementV110', () => { }); }); }); + + describe('when using unsupported element type', () => { + const setup = () => { + const unknownElementType = 'unknown' as CommonCartridgeElementType; + const props = createCommonCartridgeOrganizationsWrapperElementPropsV110(); + const sut = new CommonCartridgeOrganizationsWrapperElementV110(props); + + return { sut, unknownElementType }; + }; + + it('should throw error', () => { + const { sut, unknownElementType } = setup(); + + expect(() => sut.getManifestXmlObject(unknownElementType)).toThrow(ElementTypeNotSupportedLoggableException); + }); + }); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-organizations-wrapper-element.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-organizations-wrapper-element.ts index 9d1c44ec85d..564d010b79c 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-organizations-wrapper-element.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-organizations-wrapper-element.ts @@ -1,39 +1,13 @@ -import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; -import { CommonCartridgeElement } from '../../interfaces'; +import { + CommonCartridgeOrganizationsWrapperElement, + CommonCartridgeOrganizationsWrapperElementProps, +} from '../abstract/common-cartridge-organizations-wrapper-element'; +import { CommonCartridgeVersion } from '../../common-cartridge.enums'; -export type CommonCartridgeOrganizationsWrapperElementPropsV110 = { - type: CommonCartridgeElementType.ORGANIZATIONS_WRAPPER; - version: CommonCartridgeVersion; - items: CommonCartridgeElement[]; -}; - -export class CommonCartridgeOrganizationsWrapperElementV110 extends CommonCartridgeElement { - constructor(private readonly props: CommonCartridgeOrganizationsWrapperElementPropsV110) { - super(props); - } +export type CommonCartridgeOrganizationsWrapperElementPropsV110 = CommonCartridgeOrganizationsWrapperElementProps; +export class CommonCartridgeOrganizationsWrapperElementV110 extends CommonCartridgeOrganizationsWrapperElement { public getSupportedVersion(): CommonCartridgeVersion { return CommonCartridgeVersion.V_1_1_0; } - - public getManifestXmlObject(): Record { - return { - organization: [ - { - $: { - identifier: 'org-1', - structure: 'rooted-hierarchy', - }, - item: [ - { - $: { - identifier: 'LearningModules', - }, - item: this.props.items.map((items) => items.getManifestXmlObject()), - }, - ], - }, - ], - }; - } } diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-resources-wrapper-element.spec.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-resources-wrapper-element.spec.ts index 0106eefbee5..ed54df3df8e 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-resources-wrapper-element.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-resources-wrapper-element.spec.ts @@ -1,7 +1,7 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { createCommonCartridgeResourcesWrapperElementPropsV110 } from '../../../testing/common-cartridge-element-props.factory'; import { createCommonCartridgeWeblinkResourcePropsV110 } from '../../../testing/common-cartridge-resource-props.factory'; -import { CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException, VersionNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeResourceFactory } from '../../resources/common-cartridge-resource-factory'; import { CommonCartridgeResourcesWrapperElementV110 } from './common-cartridge-resources-wrapper-element'; @@ -30,14 +30,14 @@ describe('CommonCartridgeResourcesWrapperElementV110', () => { it('should throw error', () => { expect(() => new CommonCartridgeResourcesWrapperElementV110(notSupportedProps)).toThrow( - InternalServerErrorException + VersionNotSupportedLoggableException ); }); }); }); describe('getManifestXmlObject', () => { - describe('when using common cartridge version 1.1.0', () => { + describe('when creating resources wrapper xml object', () => { const setup = () => { const resourceProps = createCommonCartridgeWeblinkResourcePropsV110(); const props = createCommonCartridgeResourcesWrapperElementPropsV110([ @@ -48,10 +48,10 @@ describe('CommonCartridgeResourcesWrapperElementV110', () => { return { sut, resourceProps }; }; - it('should return correct manifest xml object', () => { + it('should return resources wrapper manifest fragment', () => { const { sut, resourceProps } = setup(); - const result = sut.getManifestXmlObject(); + const result = sut.getManifestXmlObject(CommonCartridgeElementType.RESOURCES_WRAPPER); expect(result).toStrictEqual({ resources: [ @@ -74,5 +74,21 @@ describe('CommonCartridgeResourcesWrapperElementV110', () => { }); }); }); + + describe('when using unsupported element type', () => { + const setup = () => { + const unknownElementType = 'unknown' as CommonCartridgeElementType; + const props = createCommonCartridgeResourcesWrapperElementPropsV110(); + const sut = new CommonCartridgeResourcesWrapperElementV110(props); + + return { sut, unknownElementType }; + }; + + it('should throw error', () => { + const { sut, unknownElementType } = setup(); + + expect(() => sut.getManifestXmlObject(unknownElementType)).toThrow(ElementTypeNotSupportedLoggableException); + }); + }); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-resources-wrapper-element.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-resources-wrapper-element.ts index 4048787732a..e179834e99e 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-resources-wrapper-element.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.1.0/common-cartridge-resources-wrapper-element.ts @@ -1,28 +1,13 @@ -import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; -import { CommonCartridgeElement } from '../../interfaces'; +import { CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { + CommonCartridgeResourcesWrapperElement, + CommonCartridgeResourcesWrapperElementProps, +} from '../abstract/common-cartridge-resources-wrapper-element'; -export type CommonCartridgeResourcesWrapperElementPropsV110 = { - type: CommonCartridgeElementType.RESOURCES_WRAPPER; - version: CommonCartridgeVersion; - items: CommonCartridgeElement[]; -}; - -export class CommonCartridgeResourcesWrapperElementV110 extends CommonCartridgeElement { - constructor(private readonly props: CommonCartridgeResourcesWrapperElementPropsV110) { - super(props); - } +export type CommonCartridgeResourcesWrapperElementPropsV110 = CommonCartridgeResourcesWrapperElementProps; +export class CommonCartridgeResourcesWrapperElementV110 extends CommonCartridgeResourcesWrapperElement { public getSupportedVersion(): CommonCartridgeVersion { return CommonCartridgeVersion.V_1_1_0; } - - public getManifestXmlObject(): Record { - return { - resources: [ - { - resource: this.props.items.map((items) => items.getManifestXmlObject()), - }, - ], - }; - } } diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-element-factory.spec.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-element-factory.spec.ts index 7fe5e1f0cf5..7a5cdae2204 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-element-factory.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-element-factory.spec.ts @@ -1,4 +1,3 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { createCommonCartridgeMetadataElementPropsV130, createCommonCartridgeOrganizationElementPropsV130, @@ -6,6 +5,7 @@ import { createCommonCartridgeResourcesWrapperElementPropsV130, } from '../../../testing/common-cartridge-element-props.factory'; import { CommonCartridgeElementType } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeElementFactoryV130 } from './common-cartridge-element-factory'; import { CommonCartridgeMetadataElementV130 } from './common-cartridge-metadata-element'; import { CommonCartridgeOrganizationElementV130 } from './common-cartridge-organization-element'; @@ -52,9 +52,9 @@ describe('CommonCartridgeElementFactoryV130', () => { const notSupportedProps = createCommonCartridgeOrganizationsWrapperElementPropsV130(); notSupportedProps.type = 'not-supported' as CommonCartridgeElementType.ORGANIZATIONS_WRAPPER; - it('should throw error', () => { + it('should throw ElementTypeNotSupportedLoggableException', () => { expect(() => CommonCartridgeElementFactoryV130.createElement(notSupportedProps)).toThrow( - InternalServerErrorException + ElementTypeNotSupportedLoggableException ); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-element-factory.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-element-factory.ts index 6a1216e405a..adade3178cb 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-element-factory.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-element-factory.ts @@ -1,6 +1,6 @@ import { CommonCartridgeElementType } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeElement } from '../../interfaces'; -import { createElementTypeNotSupportedError } from '../../utils'; import { CommonCartridgeMetadataElementPropsV130, CommonCartridgeMetadataElementV130, @@ -39,7 +39,7 @@ export class CommonCartridgeElementFactoryV130 { case CommonCartridgeElementType.RESOURCES_WRAPPER: return new CommonCartridgeResourcesWrapperElementV130(props); default: - throw createElementTypeNotSupportedError(type); + throw new ElementTypeNotSupportedLoggableException(type); } } } diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-metadata-element.spec.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-metadata-element.spec.ts index 698b3efea04..e672a08d3d2 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-metadata-element.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-metadata-element.spec.ts @@ -1,6 +1,6 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { createCommonCartridgeMetadataElementPropsV130 } from '../../../testing/common-cartridge-element-props.factory'; -import { CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException, VersionNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeMetadataElementV130 } from './common-cartridge-metadata-element'; describe('CommonCartridgeMetadataElementV130', () => { @@ -27,13 +27,15 @@ describe('CommonCartridgeMetadataElementV130', () => { notSupportedProps.version = CommonCartridgeVersion.V_1_1_0; it('should throw error', () => { - expect(() => new CommonCartridgeMetadataElementV130(notSupportedProps)).toThrow(InternalServerErrorException); + expect(() => new CommonCartridgeMetadataElementV130(notSupportedProps)).toThrow( + VersionNotSupportedLoggableException + ); }); }); }); describe('getManifestXmlObject', () => { - describe('when using common cartridge version 1.3', () => { + describe('when creating metadata xml object', () => { const setup = () => { const props = createCommonCartridgeMetadataElementPropsV130(); const sut = new CommonCartridgeMetadataElementV130(props); @@ -41,10 +43,10 @@ describe('CommonCartridgeMetadataElementV130', () => { return { sut, props }; }; - it('should return correct manifest xml object', () => { + it('should return metadata manifest fragment', () => { const { sut, props } = setup(); - const result = sut.getManifestXmlObject(); + const result = sut.getManifestXmlObject(CommonCartridgeElementType.METADATA); expect(result).toStrictEqual({ schema: 'IMS Common Cartridge', @@ -67,5 +69,22 @@ describe('CommonCartridgeMetadataElementV130', () => { }); }); }); + + describe('when using unsupported element type', () => { + const setup = () => { + const props = createCommonCartridgeMetadataElementPropsV130(); + const sut = new CommonCartridgeMetadataElementV130(props); + + return { sut, props }; + }; + + it('should throw error', () => { + const { sut } = setup(); + + expect(() => sut.getManifestXmlObject(CommonCartridgeElementType.ORGANIZATION)).toThrow( + ElementTypeNotSupportedLoggableException + ); + }); + }); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-metadata-element.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-metadata-element.ts index a6477783e57..f7503c779dc 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-metadata-element.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-metadata-element.ts @@ -1,5 +1,6 @@ import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; -import { CommonCartridgeElement } from '../../interfaces'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; +import { CommonCartridgeElement, XmlObject } from '../../interfaces'; export type CommonCartridgeMetadataElementPropsV130 = { type: CommonCartridgeElementType.METADATA; @@ -18,7 +19,16 @@ export class CommonCartridgeMetadataElementV130 extends CommonCartridgeElement { return CommonCartridgeVersion.V_1_3_0; } - public getManifestXmlObject(): Record { + public getManifestXmlObject(elementType: CommonCartridgeElementType): XmlObject { + switch (elementType) { + case CommonCartridgeElementType.METADATA: + return this.getManifestXmlObjectInternal(); + default: + throw new ElementTypeNotSupportedLoggableException(elementType); + } + } + + private getManifestXmlObjectInternal(): XmlObject { return { schema: 'IMS Common Cartridge', schemaversion: '1.3.0', diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-organization-element.spec.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-organization-element.spec.ts index 73cb9b1af5a..6ada585495b 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-organization-element.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-organization-element.spec.ts @@ -1,7 +1,7 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { createCommonCartridgeOrganizationElementPropsV130 } from '../../../testing/common-cartridge-element-props.factory'; import { createCommonCartridgeWeblinkResourcePropsV130 } from '../../../testing/common-cartridge-resource-props.factory'; -import { CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException, VersionNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeResourceFactory } from '../../resources/common-cartridge-resource-factory'; import { CommonCartridgeElementFactory } from '../common-cartridge-element-factory'; import { CommonCartridgeOrganizationElementV130 } from './common-cartridge-organization-element'; @@ -31,25 +31,20 @@ describe('CommonCartridgeOrganizationElementV130', () => { it('should throw error', () => { expect(() => new CommonCartridgeOrganizationElementV130(notSupportedProps)).toThrow( - InternalServerErrorException + VersionNotSupportedLoggableException ); }); }); }); describe('getManifestXmlObject', () => { - describe('when using common cartridge version 1.3.0', () => { + describe('when creating organization xml object', () => { const setup = () => { const resourceProps = createCommonCartridgeWeblinkResourcePropsV130(); - - const subOrganization1Props = createCommonCartridgeOrganizationElementPropsV130( - CommonCartridgeResourceFactory.createResource(resourceProps) - ); - + const subOrganization1Props = createCommonCartridgeOrganizationElementPropsV130(); const subOrganization2Props = createCommonCartridgeOrganizationElementPropsV130([ CommonCartridgeResourceFactory.createResource(resourceProps), ]); - const organizationProps = createCommonCartridgeOrganizationElementPropsV130([ CommonCartridgeElementFactory.createElement(subOrganization1Props), CommonCartridgeElementFactory.createElement(subOrganization2Props), @@ -60,10 +55,10 @@ describe('CommonCartridgeOrganizationElementV130', () => { return { sut, organizationProps, subOrganization1Props, subOrganization2Props, resourceProps }; }; - it('should return correct manifest xml object', () => { + it('should return organization manifest fragment', () => { const { sut, organizationProps, subOrganization1Props, subOrganization2Props, resourceProps } = setup(); - const result = sut.getManifestXmlObject(); + const result = sut.getManifestXmlObject(CommonCartridgeElementType.ORGANIZATION); expect(result).toStrictEqual({ $: { @@ -74,9 +69,9 @@ describe('CommonCartridgeOrganizationElementV130', () => { { $: { identifier: subOrganization1Props.identifier, - identifierref: resourceProps.identifier, }, title: subOrganization1Props.title, + item: [], }, { $: { @@ -97,5 +92,21 @@ describe('CommonCartridgeOrganizationElementV130', () => { }); }); }); + + describe('when using unsupported element type', () => { + const setup = () => { + const unknownElementType = 'unknown' as CommonCartridgeElementType; + const props = createCommonCartridgeOrganizationElementPropsV130(); + const sut = new CommonCartridgeOrganizationElementV130(props); + + return { sut, unknownElementType }; + }; + + it('should throw error', () => { + const { sut, unknownElementType } = setup(); + + expect(() => sut.getManifestXmlObject(unknownElementType)).toThrow(ElementTypeNotSupportedLoggableException); + }); + }); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-organization-element.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-organization-element.ts index 14696edfd95..9c71e8b3e29 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-organization-element.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-organization-element.ts @@ -1,13 +1,13 @@ import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; -import { CommonCartridgeElement, CommonCartridgeResource } from '../../interfaces'; -import { createIdentifier } from '../../utils'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; +import { CommonCartridgeElement, CommonCartridgeResource, XmlObject } from '../../interfaces'; export type CommonCartridgeOrganizationElementPropsV130 = { type: CommonCartridgeElementType.ORGANIZATION; version: CommonCartridgeVersion; identifier: string; title: string; - items: CommonCartridgeResource | Array; + items: CommonCartridgeResource | Array; }; export class CommonCartridgeOrganizationElementV130 extends CommonCartridgeElement { @@ -19,35 +19,32 @@ export class CommonCartridgeOrganizationElementV130 extends CommonCartridgeEleme return CommonCartridgeVersion.V_1_3_0; } - public getManifestXmlObject(): Record { - if (this.props.items instanceof CommonCartridgeResource) { - return { - $: { - identifier: this.identifier, - identifierref: this.props.items.identifier, - }, - title: this.title, - }; + public getManifestXmlObject(elementType: CommonCartridgeElementType): XmlObject { + switch (elementType) { + case CommonCartridgeElementType.ORGANIZATION: + return this.getManifestXmlObjectInternal(); + default: + throw new ElementTypeNotSupportedLoggableException(elementType); } + } + + public getManifestXmlObjectInternal(): XmlObject { + const xmlObject = Array.isArray(this.props.items) + ? this.getManifestXmlObjectForCollection(this.props.items) + : this.props.items.getManifestXmlObject(CommonCartridgeElementType.ORGANIZATION); - return { + return xmlObject; + } + + private getManifestXmlObjectForCollection(items: Array): XmlObject { + const xmlObject = { $: { identifier: this.identifier, }, title: this.title, - item: this.props.items.map((item) => { - if (item instanceof CommonCartridgeResource) { - return { - $: { - identifier: createIdentifier(), - identifierref: item.identifier, - }, - title: item.title, - }; - } - - return item.getManifestXmlObject(); - }), + item: items.map((item) => item.getManifestXmlObject(CommonCartridgeElementType.ORGANIZATION)), }; + + return xmlObject; } } diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-organizations-wrapper-element.spec.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-organizations-wrapper-element.spec.ts index 47c68046764..09cccd60b72 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-organizations-wrapper-element.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-organizations-wrapper-element.spec.ts @@ -1,9 +1,9 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { createCommonCartridgeOrganizationElementPropsV130, createCommonCartridgeOrganizationsWrapperElementPropsV130, } from '../../../testing/common-cartridge-element-props.factory'; -import { CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException, VersionNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeElementFactory } from '../common-cartridge-element-factory'; import { CommonCartridgeOrganizationsWrapperElementV130 } from './common-cartridge-organizations-wrapper-element'; @@ -32,14 +32,14 @@ describe('CommonCartridgeOrganizationsWrapperElementV130', () => { it('should throw error', () => { expect(() => new CommonCartridgeOrganizationsWrapperElementV130(notSupportedProps)).toThrow( - InternalServerErrorException + VersionNotSupportedLoggableException ); }); }); }); describe('getManifestXmlObject', () => { - describe('when using common cartridge version 1.3.0', () => { + describe('when creating organizations wrapper xml object', () => { const setup = () => { const organizationProps = createCommonCartridgeOrganizationElementPropsV130(); const props = createCommonCartridgeOrganizationsWrapperElementPropsV130([ @@ -50,10 +50,10 @@ describe('CommonCartridgeOrganizationsWrapperElementV130', () => { return { sut, organizationProps }; }; - it('should return correct manifest xml object', () => { + it('should return organizations wrapper manifest fragment', () => { const { sut, organizationProps } = setup(); - const result = sut.getManifestXmlObject(); + const result = sut.getManifestXmlObject(CommonCartridgeElementType.ORGANIZATIONS_WRAPPER); expect(result).toStrictEqual({ organization: [ @@ -83,5 +83,21 @@ describe('CommonCartridgeOrganizationsWrapperElementV130', () => { }); }); }); + + describe('when using unsupported element type', () => { + const setup = () => { + const unknownElementType = 'unknown' as CommonCartridgeElementType; + const props = createCommonCartridgeOrganizationsWrapperElementPropsV130(); + const sut = new CommonCartridgeOrganizationsWrapperElementV130(props); + + return { sut, unknownElementType }; + }; + + it('should throw error', () => { + const { sut, unknownElementType } = setup(); + + expect(() => sut.getManifestXmlObject(unknownElementType)).toThrow(ElementTypeNotSupportedLoggableException); + }); + }); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-organizations-wrapper-element.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-organizations-wrapper-element.ts index 3c00f4844d2..0bf03cd9efc 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-organizations-wrapper-element.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-organizations-wrapper-element.ts @@ -1,39 +1,13 @@ -import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; -import { CommonCartridgeElement } from '../../interfaces'; +import { + CommonCartridgeOrganizationsWrapperElement, + CommonCartridgeOrganizationsWrapperElementProps, +} from '../abstract/common-cartridge-organizations-wrapper-element'; +import { CommonCartridgeVersion } from '../../common-cartridge.enums'; -export type CommonCartridgeOrganizationsWrapperElementPropsV130 = { - type: CommonCartridgeElementType.ORGANIZATIONS_WRAPPER; - version: CommonCartridgeVersion; - items: CommonCartridgeElement[]; -}; - -export class CommonCartridgeOrganizationsWrapperElementV130 extends CommonCartridgeElement { - constructor(private readonly props: CommonCartridgeOrganizationsWrapperElementPropsV130) { - super(props); - } +export type CommonCartridgeOrganizationsWrapperElementPropsV130 = CommonCartridgeOrganizationsWrapperElementProps; +export class CommonCartridgeOrganizationsWrapperElementV130 extends CommonCartridgeOrganizationsWrapperElement { public getSupportedVersion(): CommonCartridgeVersion { return CommonCartridgeVersion.V_1_3_0; } - - public getManifestXmlObject(): Record { - return { - organization: [ - { - $: { - identifier: 'org-1', - structure: 'rooted-hierarchy', - }, - item: [ - { - $: { - identifier: 'LearningModules', - }, - item: this.props.items.map((items) => items.getManifestXmlObject()), - }, - ], - }, - ], - }; - } } diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-resources-wrapper-element.spec.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-resources-wrapper-element.spec.ts index 2d71adcc144..17cdf2bac31 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-resources-wrapper-element.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-resources-wrapper-element.spec.ts @@ -1,7 +1,7 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { createCommonCartridgeResourcesWrapperElementPropsV130 } from '../../../testing/common-cartridge-element-props.factory'; import { createCommonCartridgeWeblinkResourcePropsV130 } from '../../../testing/common-cartridge-resource-props.factory'; -import { CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException, VersionNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeResourceFactory } from '../../resources/common-cartridge-resource-factory'; import { CommonCartridgeResourcesWrapperElementV130 } from './common-cartridge-resources-wrapper-element'; @@ -30,14 +30,14 @@ describe('CommonCartridgeResourcesWrapperElementV130', () => { it('should throw error', () => { expect(() => new CommonCartridgeResourcesWrapperElementV130(notSupportedProps)).toThrowError( - InternalServerErrorException + VersionNotSupportedLoggableException ); }); }); }); describe('getManifestXmlObject', () => { - describe('when using common cartridge version 1.3.0', () => { + describe('when creating resources wrapper xml object', () => { const setup = () => { const weblinkResourceProps = createCommonCartridgeWeblinkResourcePropsV130(); const props = createCommonCartridgeResourcesWrapperElementPropsV130([ @@ -51,7 +51,7 @@ describe('CommonCartridgeResourcesWrapperElementV130', () => { it('should return correct manifest xml object', () => { const { sut, weblinkResourceProps } = setup(); - const result = sut.getManifestXmlObject(); + const result = sut.getManifestXmlObject(CommonCartridgeElementType.RESOURCES_WRAPPER); expect(result).toStrictEqual({ resources: [ @@ -74,5 +74,23 @@ describe('CommonCartridgeResourcesWrapperElementV130', () => { }); }); }); + + describe('when using unsupported element type', () => { + const setup = () => { + const unknownElementType = 'unknown' as CommonCartridgeElementType; + const props = createCommonCartridgeResourcesWrapperElementPropsV130(); + const sut = new CommonCartridgeResourcesWrapperElementV130(props); + + return { sut, unknownElementType }; + }; + + it('should throw error', () => { + const { sut, unknownElementType } = setup(); + + expect(() => sut.getManifestXmlObject(unknownElementType)).toThrowError( + ElementTypeNotSupportedLoggableException + ); + }); + }); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-resources-wrapper-element.ts b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-resources-wrapper-element.ts index aa11d0f457a..b9726dd3c63 100644 --- a/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-resources-wrapper-element.ts +++ b/apps/server/src/modules/common-cartridge/export/elements/v1.3.0/common-cartridge-resources-wrapper-element.ts @@ -1,28 +1,13 @@ -import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; -import { CommonCartridgeElement } from '../../interfaces'; +import { CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { + CommonCartridgeResourcesWrapperElement, + CommonCartridgeResourcesWrapperElementProps, +} from '../abstract/common-cartridge-resources-wrapper-element'; -export type CommonCartridgeResourcesWrapperElementPropsV130 = { - type: CommonCartridgeElementType.RESOURCES_WRAPPER; - version: CommonCartridgeVersion; - items: CommonCartridgeElement[]; -}; - -export class CommonCartridgeResourcesWrapperElementV130 extends CommonCartridgeElement { - constructor(private readonly props: CommonCartridgeResourcesWrapperElementPropsV130) { - super(props); - } +export type CommonCartridgeResourcesWrapperElementPropsV130 = CommonCartridgeResourcesWrapperElementProps; +export class CommonCartridgeResourcesWrapperElementV130 extends CommonCartridgeResourcesWrapperElement { public getSupportedVersion(): CommonCartridgeVersion { return CommonCartridgeVersion.V_1_3_0; } - - public getManifestXmlObject(): Record { - return { - resources: [ - { - resource: this.props.items.map((items) => items.getManifestXmlObject()), - }, - ], - }; - } } diff --git a/apps/server/src/modules/common-cartridge/export/errors/element-type-not-supported.loggable-exception.spec.ts b/apps/server/src/modules/common-cartridge/export/errors/element-type-not-supported.loggable-exception.spec.ts new file mode 100644 index 00000000000..65cd0f664d9 --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/errors/element-type-not-supported.loggable-exception.spec.ts @@ -0,0 +1,23 @@ +import { ElementTypeNotSupportedLoggableException } from './element-type-not-supported.loggable-exception'; +import { CommonCartridgeErrorEnum } from './error.enums'; + +describe('ElementTypeNotSupportedLoggableException', () => { + describe('getLogMessage', () => { + describe('when getting log message', () => { + const exception = new ElementTypeNotSupportedLoggableException('notSupportedType'); + + it('should return log message', () => { + const result = exception.getLogMessage(); + + expect(result).toStrictEqual({ + type: CommonCartridgeErrorEnum.ELEMENT_TYPE_NOT_SUPPORTED, + stack: exception.stack, + data: { + type: 'notSupportedType', + message: 'Common Cartridge element type notSupportedType is not supported', + }, + }); + }); + }); + }); +}); diff --git a/apps/server/src/modules/common-cartridge/export/errors/element-type-not-supported.loggable-exception.ts b/apps/server/src/modules/common-cartridge/export/errors/element-type-not-supported.loggable-exception.ts new file mode 100644 index 00000000000..19dd0ff941d --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/errors/element-type-not-supported.loggable-exception.ts @@ -0,0 +1,24 @@ +import { InternalServerErrorException } from '@nestjs/common'; +import { ErrorLogMessage, Loggable } from '@src/core/logger'; +import { CommonCartridgeErrorEnum } from './error.enums'; + +export class ElementTypeNotSupportedLoggableException extends InternalServerErrorException implements Loggable { + constructor(private readonly type: string) { + super({ + type: CommonCartridgeErrorEnum.ELEMENT_TYPE_NOT_SUPPORTED, + }); + } + + public getLogMessage(): ErrorLogMessage { + const message: ErrorLogMessage = { + type: CommonCartridgeErrorEnum.ELEMENT_TYPE_NOT_SUPPORTED, + stack: this.stack, + data: { + type: this.type, + message: `Common Cartridge element type ${this.type} is not supported`, + }, + }; + + return message; + } +} diff --git a/apps/server/src/modules/common-cartridge/export/errors/error.enums.ts b/apps/server/src/modules/common-cartridge/export/errors/error.enums.ts new file mode 100644 index 00000000000..0f5081717c9 --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/errors/error.enums.ts @@ -0,0 +1,7 @@ +export enum CommonCartridgeErrorEnum { + VERSION_NOT_SUPPORTED = 'VERSION_NOT_SUPPORTED', + RESOURCE_TYPE_NOT_SUPPORTED = 'RESOURCE_TYPE_NOT_SUPPORTED', + ELEMENT_TYPE_NOT_SUPPORTED = 'ELEMENT_TYPE_NOT_SUPPORTED', + INTENDED_USE_NOT_SUPPORTED = 'INTENDED_USE_NOT_SUPPORTED', + MISSING_METADATA = 'MISSING_METADATA', +} diff --git a/apps/server/src/modules/common-cartridge/export/errors/index.ts b/apps/server/src/modules/common-cartridge/export/errors/index.ts new file mode 100644 index 00000000000..a3f364f2793 --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/errors/index.ts @@ -0,0 +1,5 @@ +export { ElementTypeNotSupportedLoggableException } from './element-type-not-supported.loggable-exception'; +export { IntendedUseNotSupportedLoggableException } from './intended-use-not-supported.loggable-exception'; +export { MissingMetadataLoggableException } from './missing-metadata.loggable-exception'; +export { ResourceTypeNotSupportedLoggableException } from './resource-type-not-supported.loggable-exception'; +export { VersionNotSupportedLoggableException } from './version-not-supported.loggable-exception'; diff --git a/apps/server/src/modules/common-cartridge/export/errors/intended-use-not-supported.loggable-exception.spec.ts b/apps/server/src/modules/common-cartridge/export/errors/intended-use-not-supported.loggable-exception.spec.ts new file mode 100644 index 00000000000..3bc2353cdae --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/errors/intended-use-not-supported.loggable-exception.spec.ts @@ -0,0 +1,23 @@ +import { CommonCartridgeErrorEnum } from './error.enums'; +import { IntendedUseNotSupportedLoggableException } from './intended-use-not-supported.loggable-exception'; + +describe('IntendedUseNotSupportedLoggableException', () => { + describe('getLogMessage', () => { + describe('when getting log message', () => { + const exception = new IntendedUseNotSupportedLoggableException('notSupportedIntendedUse'); + + it('should return log message', () => { + const result = exception.getLogMessage(); + + expect(result).toStrictEqual({ + type: CommonCartridgeErrorEnum.INTENDED_USE_NOT_SUPPORTED, + stack: exception.stack, + data: { + intendedUse: 'notSupportedIntendedUse', + message: 'Common Cartridge intended use notSupportedIntendedUse is not supported', + }, + }); + }); + }); + }); +}); diff --git a/apps/server/src/modules/common-cartridge/export/errors/intended-use-not-supported.loggable-exception.ts b/apps/server/src/modules/common-cartridge/export/errors/intended-use-not-supported.loggable-exception.ts new file mode 100644 index 00000000000..5259bd517f1 --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/errors/intended-use-not-supported.loggable-exception.ts @@ -0,0 +1,24 @@ +import { InternalServerErrorException } from '@nestjs/common'; +import { ErrorLogMessage, Loggable } from '@src/core/logger'; +import { CommonCartridgeErrorEnum } from './error.enums'; + +export class IntendedUseNotSupportedLoggableException extends InternalServerErrorException implements Loggable { + constructor(private readonly intendedUse: string) { + super({ + type: CommonCartridgeErrorEnum.INTENDED_USE_NOT_SUPPORTED, + }); + } + + public getLogMessage(): ErrorLogMessage { + const message: ErrorLogMessage = { + type: CommonCartridgeErrorEnum.INTENDED_USE_NOT_SUPPORTED, + stack: this.stack, + data: { + intendedUse: this.intendedUse, + message: `Common Cartridge intended use ${this.intendedUse} is not supported`, + }, + }; + + return message; + } +} diff --git a/apps/server/src/modules/common-cartridge/export/errors/missing-metadata.loggable-exception.spec.ts b/apps/server/src/modules/common-cartridge/export/errors/missing-metadata.loggable-exception.spec.ts new file mode 100644 index 00000000000..3d0b32d87d9 --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/errors/missing-metadata.loggable-exception.spec.ts @@ -0,0 +1,22 @@ +import { CommonCartridgeErrorEnum } from './error.enums'; +import { MissingMetadataLoggableException } from './missing-metadata.loggable-exception'; + +describe('MissingMetadataLoggableException', () => { + describe('getLogMessage', () => { + describe('when getting log message', () => { + const exception = new MissingMetadataLoggableException(); + + it('should return log message', () => { + const result = exception.getLogMessage(); + + expect(result).toStrictEqual({ + type: CommonCartridgeErrorEnum.MISSING_METADATA, + stack: exception.stack, + data: { + message: 'Metadata is required', + }, + }); + }); + }); + }); +}); diff --git a/apps/server/src/modules/common-cartridge/export/errors/missing-metadata.loggable-exception.ts b/apps/server/src/modules/common-cartridge/export/errors/missing-metadata.loggable-exception.ts new file mode 100644 index 00000000000..9ee3d6bcff0 --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/errors/missing-metadata.loggable-exception.ts @@ -0,0 +1,23 @@ +import { InternalServerErrorException } from '@nestjs/common'; +import { ErrorLogMessage, Loggable } from '@src/core/logger'; +import { CommonCartridgeErrorEnum } from './error.enums'; + +export class MissingMetadataLoggableException extends InternalServerErrorException implements Loggable { + constructor() { + super({ + type: CommonCartridgeErrorEnum.MISSING_METADATA, + }); + } + + public getLogMessage(): ErrorLogMessage { + const message: ErrorLogMessage = { + type: CommonCartridgeErrorEnum.MISSING_METADATA, + stack: this.stack, + data: { + message: 'Metadata is required', + }, + }; + + return message; + } +} diff --git a/apps/server/src/modules/common-cartridge/export/errors/resource-type-not-supported.loggable-exception.spec.ts b/apps/server/src/modules/common-cartridge/export/errors/resource-type-not-supported.loggable-exception.spec.ts new file mode 100644 index 00000000000..b51cb43cd0b --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/errors/resource-type-not-supported.loggable-exception.spec.ts @@ -0,0 +1,23 @@ +import { CommonCartridgeErrorEnum } from './error.enums'; +import { ResourceTypeNotSupportedLoggableException } from './resource-type-not-supported.loggable-exception'; + +describe('ResourceTypeNotSupportedLoggableException', () => { + describe('getLogMessage', () => { + describe('when getting log message', () => { + const exception = new ResourceTypeNotSupportedLoggableException('notSupportedType'); + + it('should return log message', () => { + const result = exception.getLogMessage(); + + expect(result).toStrictEqual({ + type: CommonCartridgeErrorEnum.RESOURCE_TYPE_NOT_SUPPORTED, + stack: exception.stack, + data: { + type: 'notSupportedType', + message: 'Common Cartridge resource type notSupportedType is not supported', + }, + }); + }); + }); + }); +}); diff --git a/apps/server/src/modules/common-cartridge/export/errors/resource-type-not-supported.loggable-exception.ts b/apps/server/src/modules/common-cartridge/export/errors/resource-type-not-supported.loggable-exception.ts new file mode 100644 index 00000000000..4d9d9d8a8e1 --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/errors/resource-type-not-supported.loggable-exception.ts @@ -0,0 +1,24 @@ +import { InternalServerErrorException } from '@nestjs/common'; +import { ErrorLogMessage, Loggable } from '@src/core/logger'; +import { CommonCartridgeErrorEnum } from './error.enums'; + +export class ResourceTypeNotSupportedLoggableException extends InternalServerErrorException implements Loggable { + constructor(private readonly type: string) { + super({ + type: CommonCartridgeErrorEnum.RESOURCE_TYPE_NOT_SUPPORTED, + }); + } + + public getLogMessage(): ErrorLogMessage { + const message: ErrorLogMessage = { + type: CommonCartridgeErrorEnum.RESOURCE_TYPE_NOT_SUPPORTED, + stack: this.stack, + data: { + type: this.type, + message: `Common Cartridge resource type ${this.type} is not supported`, + }, + }; + + return message; + } +} diff --git a/apps/server/src/modules/common-cartridge/export/errors/version-not-supported.loggable-exception.spec.ts b/apps/server/src/modules/common-cartridge/export/errors/version-not-supported.loggable-exception.spec.ts new file mode 100644 index 00000000000..5ec29c55fdb --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/errors/version-not-supported.loggable-exception.spec.ts @@ -0,0 +1,23 @@ +import { CommonCartridgeErrorEnum } from './error.enums'; +import { VersionNotSupportedLoggableException } from './version-not-supported.loggable-exception'; + +describe('VersionNotSupportedLoggableException', () => { + describe('getLogMessage', () => { + describe('when getting log message', () => { + const exception = new VersionNotSupportedLoggableException('notSupportedVersion'); + + it('should return log message', () => { + const result = exception.getLogMessage(); + + expect(result).toStrictEqual({ + type: CommonCartridgeErrorEnum.VERSION_NOT_SUPPORTED, + stack: exception.stack, + data: { + version: 'notSupportedVersion', + message: 'Common Cartridge version notSupportedVersion is not supported', + }, + }); + }); + }); + }); +}); diff --git a/apps/server/src/modules/common-cartridge/export/errors/version-not-supported.loggable-exception.ts b/apps/server/src/modules/common-cartridge/export/errors/version-not-supported.loggable-exception.ts new file mode 100644 index 00000000000..bb22519217e --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/errors/version-not-supported.loggable-exception.ts @@ -0,0 +1,24 @@ +import { InternalServerErrorException } from '@nestjs/common'; +import { ErrorLogMessage, Loggable } from '@src/core/logger'; +import { CommonCartridgeErrorEnum } from './error.enums'; + +export class VersionNotSupportedLoggableException extends InternalServerErrorException implements Loggable { + constructor(private readonly version: string) { + super({ + type: CommonCartridgeErrorEnum.VERSION_NOT_SUPPORTED, + }); + } + + public getLogMessage(): ErrorLogMessage { + const message: ErrorLogMessage = { + type: CommonCartridgeErrorEnum.VERSION_NOT_SUPPORTED, + stack: this.stack, + data: { + version: this.version, + message: `Common Cartridge version ${this.version} is not supported`, + }, + }; + + return message; + } +} diff --git a/apps/server/src/modules/common-cartridge/export/interfaces/common-cartridge-base.interface.ts b/apps/server/src/modules/common-cartridge/export/interfaces/common-cartridge-base.interface.ts new file mode 100644 index 00000000000..0b768edfbc7 --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/interfaces/common-cartridge-base.interface.ts @@ -0,0 +1,30 @@ +import { CommonCartridgeVersion } from '../common-cartridge.enums'; +import { VersionNotSupportedLoggableException } from '../errors'; + +type CommonCartridgeBaseProps = { + version: CommonCartridgeVersion; + identifier?: string; + title?: string; +}; + +export abstract class CommonCartridgeBase { + protected constructor(public readonly baseProps: CommonCartridgeBaseProps) { + this.checkVersion(baseProps.version); + } + + public get identifier(): string | undefined { + return this.baseProps.identifier; + } + + public get title(): string | undefined { + return this.baseProps.title; + } + + abstract getSupportedVersion(): CommonCartridgeVersion; + + private checkVersion(target: CommonCartridgeVersion): void { + if (this.getSupportedVersion() !== target) { + throw new VersionNotSupportedLoggableException(target); + } + } +} diff --git a/apps/server/src/modules/common-cartridge/export/interfaces/common-cartridge-element.interface.ts b/apps/server/src/modules/common-cartridge/export/interfaces/common-cartridge-element.interface.ts index 869aa6f576f..dae8a1ecf78 100644 --- a/apps/server/src/modules/common-cartridge/export/interfaces/common-cartridge-element.interface.ts +++ b/apps/server/src/modules/common-cartridge/export/interfaces/common-cartridge-element.interface.ts @@ -1,43 +1,14 @@ -import { CommonCartridgeVersion } from '../common-cartridge.enums'; -import { createVersionNotSupportedError } from '../utils'; - -type CommonCartridgeElementProps = { - version: CommonCartridgeVersion; - identifier?: string; - title?: string; -}; +import { CommonCartridgeElementType } from '../common-cartridge.enums'; +import { CommonCartridgeBase } from './common-cartridge-base.interface'; +import { XmlObject } from './xml-object.interface'; /** * Every element which should be listed in the Common Cartridge manifest must implement this interface. */ -export abstract class CommonCartridgeElement { - protected constructor(private readonly baseProps: CommonCartridgeElementProps) { - this.checkVersion(baseProps.version); - } - - public get identifier(): string | undefined { - return this.baseProps.identifier; - } - - public get title(): string | undefined { - return this.baseProps.title; - } - - /** - * Every element must know which versions it supports. - * @returns The supported versions for this element. - */ - abstract getSupportedVersion(): CommonCartridgeVersion; - +export abstract class CommonCartridgeElement extends CommonCartridgeBase { /** * This method is used to build the imsmanifest.xml file. * @returns The XML object representation for the imsmanifest.xml file. */ - abstract getManifestXmlObject(): Record; - - private checkVersion(target: CommonCartridgeVersion): void { - if (this.getSupportedVersion() !== target) { - throw createVersionNotSupportedError(target); - } - } + abstract getManifestXmlObject(elementType: CommonCartridgeElementType): XmlObject; } diff --git a/apps/server/src/modules/common-cartridge/export/interfaces/common-cartridge-resource.interface.ts b/apps/server/src/modules/common-cartridge/export/interfaces/common-cartridge-resource.interface.ts index dfa3adbc8b4..b75b3524d06 100644 --- a/apps/server/src/modules/common-cartridge/export/interfaces/common-cartridge-resource.interface.ts +++ b/apps/server/src/modules/common-cartridge/export/interfaces/common-cartridge-resource.interface.ts @@ -4,12 +4,6 @@ import { CommonCartridgeElement } from './common-cartridge-element.interface'; * Every resource which should be added to the Common Cartridge archive must implement this interface. */ export abstract class CommonCartridgeResource extends CommonCartridgeElement { - /** - * In later Common Cartridge versions, resources can be inlined in the imsmanifest.xml file. - * @returns true if the resource can be inlined, otherwise false. - */ - abstract canInline(): boolean; - /** * This method is used to determine the path of the resource in the Common Cartridge archive. * @returns The path of the resource in the Common Cartridge archive. diff --git a/apps/server/src/modules/common-cartridge/export/interfaces/index.ts b/apps/server/src/modules/common-cartridge/export/interfaces/index.ts index aaefb39018c..1f5d4394c4d 100644 --- a/apps/server/src/modules/common-cartridge/export/interfaces/index.ts +++ b/apps/server/src/modules/common-cartridge/export/interfaces/index.ts @@ -1,2 +1,4 @@ +export { CommonCartridgeBase } from './common-cartridge-base.interface'; export { CommonCartridgeElement } from './common-cartridge-element.interface'; export { CommonCartridgeResource } from './common-cartridge-resource.interface'; +export { XmlObject } from './xml-object.interface'; diff --git a/apps/server/src/modules/common-cartridge/export/interfaces/xml-object.interface.ts b/apps/server/src/modules/common-cartridge/export/interfaces/xml-object.interface.ts new file mode 100644 index 00000000000..583e52d208e --- /dev/null +++ b/apps/server/src/modules/common-cartridge/export/interfaces/xml-object.interface.ts @@ -0,0 +1 @@ +export interface XmlObject extends Record {} diff --git a/apps/server/src/modules/common-cartridge/export/resources/common-cartridge-resource-factory.spec.ts b/apps/server/src/modules/common-cartridge/export/resources/common-cartridge-resource-factory.spec.ts index 9bd274d4810..d1d6ab6ae76 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/common-cartridge-resource-factory.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/common-cartridge-resource-factory.spec.ts @@ -1,9 +1,9 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { createCommonCartridgeWebContentResourcePropsV110, createCommonCartridgeWebContentResourcePropsV130, } from '../../testing/common-cartridge-resource-props.factory'; import { CommonCartridgeResourceType, CommonCartridgeVersion } from '../common-cartridge.enums'; +import { VersionNotSupportedLoggableException } from '../errors'; import { CommonCartridgeResourceFactory } from './common-cartridge-resource-factory'; import { CommonCartridgeWebContentResourcePropsV110, CommonCartridgeWebContentResourceV110 } from './v1.1.0'; import { CommonCartridgeWebContentResourceV130 } from './v1.3.0'; @@ -35,14 +35,14 @@ describe('CommonCartridgeResourceVersion', () => { CommonCartridgeVersion.V_1_4_0, ]; - it('should throw InternalServerErrorException', () => { + it('should throw VersionNotSupportedLoggableException', () => { notSupportedVersions.forEach((version) => { expect(() => CommonCartridgeResourceFactory.createResource({ version, type: CommonCartridgeResourceType.WEB_CONTENT, } as CommonCartridgeWebContentResourcePropsV110) - ).toThrow(InternalServerErrorException); + ).toThrow(VersionNotSupportedLoggableException); }); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/resources/common-cartridge-resource-factory.ts b/apps/server/src/modules/common-cartridge/export/resources/common-cartridge-resource-factory.ts index 673672c129e..1ef3cc1428d 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/common-cartridge-resource-factory.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/common-cartridge-resource-factory.ts @@ -1,6 +1,7 @@ import { CommonCartridgeVersion } from '../common-cartridge.enums'; +import { VersionNotSupportedLoggableException } from '../errors'; import { CommonCartridgeResource } from '../interfaces'; -import { OmitVersionAndFolder, createVersionNotSupportedError } from '../utils'; +import { OmitVersionAndFolder } from '../utils'; import { CommonCartridgeManifestResourcePropsV110, CommonCartridgeResourceFactoryV110, @@ -20,7 +21,7 @@ export type CommonCartridgeResourceProps = | OmitVersionAndFolder | OmitVersionAndFolder; -type CommonCartridgeResourcePropsInternal = +export type CommonCartridgeResourcePropsInternal = | CommonCartridgeManifestResourcePropsV110 | CommonCartridgeWebContentResourcePropsV110 | CommonCartridgeWebLinkResourcePropsV110 @@ -38,7 +39,7 @@ export class CommonCartridgeResourceFactory { case CommonCartridgeVersion.V_1_3_0: return CommonCartridgeResourceFactoryV130.createResource(props); default: - throw createVersionNotSupportedError(version); + throw new VersionNotSupportedLoggableException(version); } } } diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-manifest-resource.spec.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-manifest-resource.spec.ts index aa63bb9237f..129738f323f 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-manifest-resource.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-manifest-resource.spec.ts @@ -8,27 +8,14 @@ import { CommonCartridgeVersion, } from '../../common-cartridge.enums'; import { CommonCartridgeElementFactory } from '../../elements/common-cartridge-element-factory'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; +import * as utils from '../../utils'; import { CommonCartridgeResourceFactory } from '../common-cartridge-resource-factory'; import { CommonCartridgeManifestResourceV110 } from './common-cartridge-manifest-resource'; describe('CommonCartridgeManifestResourceV110', () => { - describe('canInline', () => { - describe('when using Common Cartridge version 1.1.0', () => { - const setup = () => { - const props = createCommonCartridgeManifestResourcePropsV110(); - const sut = new CommonCartridgeManifestResourceV110(props); - - return { sut }; - }; - - it('should return false', () => { - const { sut } = setup(); - - const result = sut.canInline(); - - expect(result).toBe(false); - }); - }); + beforeEach(() => { + jest.clearAllMocks(); }); describe('getFilePath', () => { @@ -100,6 +87,11 @@ describe('CommonCartridgeManifestResourceV110', () => { resources: [resource1, resource2], }); + // we need this, otherwise the identifier will be random and we have to updated + // the manifest.xml file which we will compare with the expected content in the test + const mockValues = ['o1', 'o2']; + jest.spyOn(utils, 'createIdentifier').mockImplementation(() => mockValues.shift() ?? ''); + return { sut }; }; @@ -144,4 +136,46 @@ describe('CommonCartridgeManifestResourceV110', () => { }); }); }); + + describe('getManifestXmlObject', () => { + describe('when creating manifest xml object', () => { + const setup = () => { + const props = createCommonCartridgeManifestResourcePropsV110(); + const sut = new CommonCartridgeManifestResourceV110(props); + + return { sut }; + }; + + it('should return manifest xml object', () => { + const { sut } = setup(); + + const result = sut.getManifestXmlObject(CommonCartridgeElementType.MANIFEST); + + expect(result).toStrictEqual({ + manifest: { + $: expect.any(Object) as unknown, + metadata: expect.any(Object) as unknown, + organizations: expect.any(Object) as unknown, + resources: expect.any(Object) as unknown, + }, + }); + }); + }); + + describe('when using unsupported element type', () => { + const setup = () => { + const unknownElementType = 'unknown' as CommonCartridgeElementType; + const props = createCommonCartridgeManifestResourcePropsV110(); + const sut = new CommonCartridgeManifestResourceV110(props); + + return { sut, unknownElementType }; + }; + + it('should throw error', () => { + const { sut, unknownElementType } = setup(); + + expect(() => sut.getManifestXmlObject(unknownElementType)).toThrow(ElementTypeNotSupportedLoggableException); + }); + }); + }); }); diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-manifest-resource.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-manifest-resource.ts index ae578c34e4a..1f597492d03 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-manifest-resource.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-manifest-resource.ts @@ -4,7 +4,8 @@ import { CommonCartridgeVersion, } from '../../common-cartridge.enums'; import { CommonCartridgeElementFactory } from '../../elements/common-cartridge-element-factory'; -import { CommonCartridgeElement, CommonCartridgeResource } from '../../interfaces'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; +import { CommonCartridgeElement, CommonCartridgeResource, XmlObject } from '../../interfaces'; import { buildXmlString } from '../../utils'; export type CommonCartridgeManifestResourcePropsV110 = { @@ -21,8 +22,17 @@ export class CommonCartridgeManifestResourceV110 extends CommonCartridgeResource super(props); } - public canInline(): boolean { - return false; + public getSupportedVersion(): CommonCartridgeVersion { + return CommonCartridgeVersion.V_1_1_0; + } + + public getManifestXmlObject(elementType: CommonCartridgeElementType): XmlObject { + switch (elementType) { + case CommonCartridgeElementType.MANIFEST: + return this.getManifestXmlObjectInternal(); + default: + throw new ElementTypeNotSupportedLoggableException(elementType); + } } public getFilePath(): string { @@ -30,14 +40,10 @@ export class CommonCartridgeManifestResourceV110 extends CommonCartridgeResource } public getFileContent(): string { - return buildXmlString(this.getManifestXmlObject()); - } - - public getSupportedVersion(): CommonCartridgeVersion { - return CommonCartridgeVersion.V_1_1_0; + return buildXmlString(this.getManifestXmlObjectInternal()); } - public getManifestXmlObject(): Record { + private getManifestXmlObjectInternal(): XmlObject { return { manifest: { $: { @@ -51,17 +57,17 @@ export class CommonCartridgeManifestResourceV110 extends CommonCartridgeResource 'http://ltsc.ieee.org/xsd/imsccv1p1/LOM/manifest https://www.imsglobal.org/profile/cc/ccv1p1/LOM/ccv1p1_lommanifest_v1p0.xsd ' + 'http://ltsc.ieee.org/xsd/imsccv1p1/LOM/resource https://www.imsglobal.org/profile/cc/ccv1p1/LOM/ccv1p1_lomresource_v1p0.xsd', }, - metadata: this.props.metadata.getManifestXmlObject(), + metadata: this.props.metadata.getManifestXmlObject(CommonCartridgeElementType.METADATA), organizations: CommonCartridgeElementFactory.createElement({ type: CommonCartridgeElementType.ORGANIZATIONS_WRAPPER, version: this.props.version, items: this.props.organizations, - }).getManifestXmlObject(), + }).getManifestXmlObject(CommonCartridgeElementType.ORGANIZATIONS_WRAPPER), ...CommonCartridgeElementFactory.createElement({ type: CommonCartridgeElementType.RESOURCES_WRAPPER, version: this.props.version, items: this.props.resources, - }).getManifestXmlObject(), + }).getManifestXmlObject(CommonCartridgeElementType.RESOURCES_WRAPPER), }, }; } diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-resource-factory.spec.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-resource-factory.spec.ts index fd93be44d2d..3e214a828ee 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-resource-factory.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-resource-factory.spec.ts @@ -1,9 +1,9 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { createCommonCartridgeManifestResourcePropsV110, createCommonCartridgeWebContentResourcePropsV110, createCommonCartridgeWeblinkResourcePropsV110, } from '../../../testing/common-cartridge-resource-props.factory'; +import { ResourceTypeNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeManifestResourceV110 } from './common-cartridge-manifest-resource'; import { CommonCartridgeResourceFactoryV110 } from './common-cartridge-resource-factory'; import { CommonCartridgeWebContentResourceV110 } from './common-cartridge-web-content-resource'; @@ -41,10 +41,10 @@ describe('CommonCartridgeResourceFactoryV110', () => { }); describe('when resource type is not supported', () => { - it('should throw error', () => { + it('should throw ResourceTypeNotSupportedLoggableException', () => { expect(() => CommonCartridgeResourceFactoryV110.createResource({} as CommonCartridgeWebLinkResourcePropsV110) - ).toThrow(InternalServerErrorException); + ).toThrow(ResourceTypeNotSupportedLoggableException); }); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-resource-factory.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-resource-factory.ts index 4e13ec77587..ffb94273dce 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-resource-factory.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-resource-factory.ts @@ -1,6 +1,6 @@ import { CommonCartridgeResourceType } from '../../common-cartridge.enums'; +import { ResourceTypeNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeResource } from '../../interfaces'; -import { createResourceTypeNotSupportedError } from '../../utils'; import { CommonCartridgeManifestResourcePropsV110, CommonCartridgeManifestResourceV110, @@ -31,7 +31,7 @@ export class CommonCartridgeResourceFactoryV110 { case CommonCartridgeResourceType.WEB_LINK: return new CommonCartridgeWebLinkResourceV110(props); default: - throw createResourceTypeNotSupportedError(type); + throw new ResourceTypeNotSupportedLoggableException(type); } } } diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-web-content-resource.spec.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-web-content-resource.spec.ts index 169986b451e..40ae8a4f2e1 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-web-content-resource.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-web-content-resource.spec.ts @@ -1,28 +1,9 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { createCommonCartridgeWebContentResourcePropsV110 } from '../../../testing/common-cartridge-resource-props.factory'; -import { CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException, VersionNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeWebContentResourceV110 } from './common-cartridge-web-content-resource'; describe('CommonCartridgeWebContentResourceV110', () => { - describe('canInline', () => { - describe('when using Common Cartridge version 1.1.0', () => { - const setup = () => { - const props = createCommonCartridgeWebContentResourcePropsV110(); - const sut = new CommonCartridgeWebContentResourceV110(props); - - return { sut }; - }; - - it('should return false', () => { - const { sut } = setup(); - - const result = sut.canInline(); - - expect(result).toBe(false); - }); - }); - }); - describe('getFilePath', () => { describe('when using Common Cartridge version 1.1.0', () => { const setup = () => { @@ -85,14 +66,37 @@ describe('CommonCartridgeWebContentResourceV110', () => { it('should throw error', () => { expect(() => new CommonCartridgeWebContentResourceV110(notSupportedProps)).toThrow( - InternalServerErrorException + VersionNotSupportedLoggableException ); }); }); }); describe('getManifestXmlObject', () => { - describe('when using Common Cartridge version 1.1.0', () => { + describe('when creating organization xml object', () => { + const setup = () => { + const props = createCommonCartridgeWebContentResourcePropsV110(); + const sut = new CommonCartridgeWebContentResourceV110(props); + + return { sut, props }; + }; + + it('should return organization manifest fragment', () => { + const { sut, props } = setup(); + + const result = sut.getManifestXmlObject(CommonCartridgeElementType.ORGANIZATION); + + expect(result).toEqual({ + $: { + identifier: expect.any(String), + identifierref: props.identifier, + }, + title: props.title, + }); + }); + }); + + describe('when creating resource xml object', () => { const setup = () => { const props = createCommonCartridgeWebContentResourcePropsV110(); const sut = new CommonCartridgeWebContentResourceV110(props); @@ -100,10 +104,10 @@ describe('CommonCartridgeWebContentResourceV110', () => { return { sut, props }; }; - it('should return the correct XML object', () => { + it('should return resource manifest fragment', () => { const { sut, props } = setup(); - const result = sut.getManifestXmlObject(); + const result = sut.getManifestXmlObject(CommonCartridgeElementType.RESOURCE); expect(result).toEqual({ $: { @@ -119,5 +123,21 @@ describe('CommonCartridgeWebContentResourceV110', () => { }); }); }); + + describe('when using unsupported element type', () => { + const setup = () => { + const unknownElementType = 'unknown' as CommonCartridgeElementType; + const props = createCommonCartridgeWebContentResourcePropsV110(); + const sut = new CommonCartridgeWebContentResourceV110(props); + + return { sut, unknownElementType }; + }; + + it('should throw error', () => { + const { sut, unknownElementType } = setup(); + + expect(() => sut.getManifestXmlObject(unknownElementType)).toThrow(ElementTypeNotSupportedLoggableException); + }); + }); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-web-content-resource.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-web-content-resource.ts index f684709e246..e097a707960 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-web-content-resource.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-web-content-resource.ts @@ -1,10 +1,13 @@ import { + CommonCartridgeElementType, CommonCartridgeIntendedUseType, CommonCartridgeResourceType, CommonCartridgeVersion, } from '../../common-cartridge.enums'; -import { CommonCartridgeResource } from '../../interfaces'; -import { checkIntendedUse } from '../../utils'; +import { CommonCartridgeGuard } from '../../common-cartridge.guard'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; +import { CommonCartridgeResource, XmlObject } from '../../interfaces'; +import { createIdentifier } from '../../utils'; export type CommonCartridgeWebContentResourcePropsV110 = { type: CommonCartridgeResourceType.WEB_CONTENT; @@ -25,11 +28,25 @@ export class CommonCartridgeWebContentResourceV110 extends CommonCartridgeResour constructor(private readonly props: CommonCartridgeWebContentResourcePropsV110) { super(props); - checkIntendedUse(props.intendedUse, CommonCartridgeWebContentResourceV110.SUPPORTED_INTENDED_USES); + CommonCartridgeGuard.checkIntendedUse( + props.intendedUse, + CommonCartridgeWebContentResourceV110.SUPPORTED_INTENDED_USES + ); } - public canInline(): boolean { - return false; + public getSupportedVersion(): CommonCartridgeVersion { + return CommonCartridgeVersion.V_1_1_0; + } + + public getManifestXmlObject(elementType: CommonCartridgeElementType): XmlObject { + switch (elementType) { + case CommonCartridgeElementType.RESOURCE: + return this.getManifestResourceXmlObject(); + case CommonCartridgeElementType.ORGANIZATION: + return this.getManifestOrganizationXmlObject(); + default: + throw new ElementTypeNotSupportedLoggableException(elementType); + } } public getFilePath(): string { @@ -40,11 +57,17 @@ export class CommonCartridgeWebContentResourceV110 extends CommonCartridgeResour return this.props.html; } - public getSupportedVersion(): CommonCartridgeVersion { - return CommonCartridgeVersion.V_1_1_0; + private getManifestOrganizationXmlObject(): XmlObject { + return { + $: { + identifier: createIdentifier(), + identifierref: this.props.identifier, + }, + title: this.props.title, + }; } - public getManifestXmlObject(): Record { + private getManifestResourceXmlObject(): XmlObject { return { $: { identifier: this.props.identifier, diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-web-link-resource.spec.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-web-link-resource.spec.ts index 79881d285f4..b697a51f871 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-web-link-resource.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-web-link-resource.spec.ts @@ -1,29 +1,11 @@ import { InternalServerErrorException } from '@nestjs/common'; import { readFile } from 'fs/promises'; import { createCommonCartridgeWeblinkResourcePropsV110 } from '../../../testing/common-cartridge-resource-props.factory'; -import { CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeWebLinkResourceV110 } from './common-cartridge-web-link-resource'; describe('CommonCartridgeWebLinkResourceV110', () => { - describe('canInline', () => { - describe('when using Common Cartridge version 1.3.0', () => { - const setup = () => { - const props = createCommonCartridgeWeblinkResourcePropsV110(); - const sut = new CommonCartridgeWebLinkResourceV110(props); - - return { sut }; - }; - - it('should return false', () => { - const { sut } = setup(); - - const result = sut.canInline(); - - expect(result).toBe(false); - }); - }); - }); - describe('getFilePath', () => { describe('when using Common Cartridge version 1.1.0', () => { const setup = () => { @@ -99,7 +81,7 @@ describe('CommonCartridgeWebLinkResourceV110', () => { }); describe('getManifestXmlObject', () => { - describe('when using Common Cartridge version 1.1.0', () => { + describe('when creating organization xml object', () => { const setup = () => { const props = createCommonCartridgeWeblinkResourcePropsV110(); const sut = new CommonCartridgeWebLinkResourceV110(props); @@ -107,10 +89,33 @@ describe('CommonCartridgeWebLinkResourceV110', () => { return { sut, props }; }; - it('should return the manifest XML object', () => { + it('should return resource manifest fragment', () => { const { sut, props } = setup(); - const result = sut.getManifestXmlObject(); + const result = sut.getManifestXmlObject(CommonCartridgeElementType.ORGANIZATION); + + expect(result).toEqual({ + $: { + identifier: expect.any(String), + identifierref: props.identifier, + }, + title: props.title, + }); + }); + }); + + describe('when creating resource xml object', () => { + const setup = () => { + const props = createCommonCartridgeWeblinkResourcePropsV110(); + const sut = new CommonCartridgeWebLinkResourceV110(props); + + return { sut, props }; + }; + + it('should return resource manifest fragment', () => { + const { sut, props } = setup(); + + const result = sut.getManifestXmlObject(CommonCartridgeElementType.RESOURCE); expect(result).toEqual({ $: { @@ -125,5 +130,21 @@ describe('CommonCartridgeWebLinkResourceV110', () => { }); }); }); + + describe('when using unsupported element type', () => { + const setup = () => { + const unknownElementType = 'unknown' as CommonCartridgeElementType; + const props = createCommonCartridgeWeblinkResourcePropsV110(); + const sut = new CommonCartridgeWebLinkResourceV110(props); + + return { sut, unknownElementType }; + }; + + it('should throw error', () => { + const { sut, unknownElementType } = setup(); + + expect(() => sut.getManifestXmlObject(unknownElementType)).toThrow(ElementTypeNotSupportedLoggableException); + }); + }); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-web-link-resource.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-web-link-resource.ts index 4da6c641215..b6fd594de7d 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-web-link-resource.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.1.0/common-cartridge-web-link-resource.ts @@ -1,6 +1,11 @@ -import { CommonCartridgeResourceType, CommonCartridgeVersion } from '../../common-cartridge.enums'; -import { CommonCartridgeResource } from '../../interfaces'; -import { buildXmlString } from '../../utils'; +import { + CommonCartridgeElementType, + CommonCartridgeResourceType, + CommonCartridgeVersion, +} from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; +import { CommonCartridgeResource, XmlObject } from '../../interfaces'; +import { buildXmlString, createIdentifier } from '../../utils'; export type CommonCartridgeWebLinkResourcePropsV110 = { type: CommonCartridgeResourceType.WEB_LINK; @@ -18,8 +23,19 @@ export class CommonCartridgeWebLinkResourceV110 extends CommonCartridgeResource super(props); } - public canInline(): boolean { - return false; + public getSupportedVersion(): CommonCartridgeVersion { + return CommonCartridgeVersion.V_1_1_0; + } + + public getManifestXmlObject(elementType: CommonCartridgeElementType): XmlObject { + switch (elementType) { + case CommonCartridgeElementType.RESOURCE: + return this.getManifestResourceXmlObject(); + case CommonCartridgeElementType.ORGANIZATION: + return this.getManifestOrganizationXmlObject(); + default: + throw new ElementTypeNotSupportedLoggableException(elementType); + } } public getFilePath(): string { @@ -47,14 +63,20 @@ export class CommonCartridgeWebLinkResourceV110 extends CommonCartridgeResource }); } - public getSupportedVersion(): CommonCartridgeVersion { - return CommonCartridgeVersion.V_1_1_0; + private getManifestOrganizationXmlObject(): XmlObject { + return { + $: { + identifier: createIdentifier(), + identifierref: this.identifier, + }, + title: this.title, + }; } - public getManifestXmlObject(): Record { + private getManifestResourceXmlObject(): XmlObject { return { $: { - identifier: this.props.identifier, + identifier: this.identifier, type: 'imswl_xmlv1p1', }, file: { diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-manifest-resource.spec.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-manifest-resource.spec.ts index f49caa016c9..eb0bd8e5696 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-manifest-resource.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-manifest-resource.spec.ts @@ -9,27 +9,14 @@ import { } from '../../common-cartridge.enums'; import { CommonCartridgeElementFactory } from '../../elements/common-cartridge-element-factory'; import { CommonCartridgeElementFactoryV130 } from '../../elements/v1.3.0'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; +import * as utils from '../../utils'; import { CommonCartridgeResourceFactory } from '../common-cartridge-resource-factory'; import { CommonCartridgeManifestResourceV130 } from './common-cartridge-manifest-resource'; describe('CommonCartridgeManifestResourceV130', () => { - describe('canInline', () => { - describe('when using Common Cartridge version 1.3.0', () => { - const setup = () => { - const props = createCommonCartridgeManifestResourcePropsV130(); - const sut = new CommonCartridgeManifestResourceV130(props); - - return { sut }; - }; - - it('should return false', () => { - const { sut } = setup(); - - const result = sut.canInline(); - - expect(result).toBe(false); - }); - }); + beforeEach(() => { + jest.clearAllMocks(); }); describe('getFilePath', () => { @@ -101,6 +88,11 @@ describe('CommonCartridgeManifestResourceV130', () => { resources: [resource1, resource2], }); + // we need this, otherwise the identifier will be random and we have to updated + // the manifest.xml file which we will compare with the expected content in the test + const mockValues = ['o1', 'o2']; + jest.spyOn(utils, 'createIdentifier').mockImplementation(() => mockValues.shift() ?? ''); + return { sut }; }; @@ -145,4 +137,46 @@ describe('CommonCartridgeManifestResourceV130', () => { }); }); }); + + describe('getManifestXmlObject', () => { + describe('when creating manifest xml object', () => { + const setup = () => { + const props = createCommonCartridgeManifestResourcePropsV130(); + const sut = new CommonCartridgeManifestResourceV130(props); + + return { sut }; + }; + + it('should return manifest xml object', () => { + const { sut } = setup(); + + const result = sut.getManifestXmlObject(CommonCartridgeElementType.MANIFEST); + + expect(result).toStrictEqual({ + manifest: { + $: expect.any(Object) as unknown, + metadata: expect.any(Object) as unknown, + organizations: expect.any(Object) as unknown, + resources: expect.any(Object) as unknown, + }, + }); + }); + }); + + describe('when element type is not supported', () => { + const setup = () => { + const unknownElementType = 'unknown' as CommonCartridgeElementType; + const props = createCommonCartridgeManifestResourcePropsV130(); + const sut = new CommonCartridgeManifestResourceV130(props); + + return { sut, unknownElementType }; + }; + + it('should throw error', () => { + const { sut, unknownElementType } = setup(); + + expect(() => sut.getManifestXmlObject(unknownElementType)).toThrow(ElementTypeNotSupportedLoggableException); + }); + }); + }); }); diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-manifest-resource.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-manifest-resource.ts index 3da27cb30b8..63f543baf07 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-manifest-resource.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-manifest-resource.ts @@ -7,7 +7,8 @@ import { CommonCartridgeOrganizationsWrapperElementV130, CommonCartridgeResourcesWrapperElementV130, } from '../../elements/v1.3.0'; -import { CommonCartridgeElement, CommonCartridgeResource } from '../../interfaces'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; +import { CommonCartridgeElement, CommonCartridgeResource, XmlObject } from '../../interfaces'; import { buildXmlString } from '../../utils'; export type CommonCartridgeManifestResourcePropsV130 = { @@ -24,23 +25,28 @@ export class CommonCartridgeManifestResourceV130 extends CommonCartridgeResource super(props); } - public canInline(): boolean { - return false; - } - public getFilePath(): string { return 'imsmanifest.xml'; } public getFileContent(): string { - return buildXmlString(this.getManifestXmlObject()); + return buildXmlString(this.getManifestXmlObject(CommonCartridgeElementType.MANIFEST)); } public getSupportedVersion(): CommonCartridgeVersion { return CommonCartridgeVersion.V_1_3_0; } - public getManifestXmlObject(): Record { + public getManifestXmlObject(elementType: CommonCartridgeElementType): XmlObject { + switch (elementType) { + case CommonCartridgeElementType.MANIFEST: + return this.getManifestXmlObjectInternal(); + default: + throw new ElementTypeNotSupportedLoggableException(elementType); + } + } + + public getManifestXmlObjectInternal(): XmlObject { return { manifest: { $: { @@ -54,17 +60,17 @@ export class CommonCartridgeManifestResourceV130 extends CommonCartridgeResource 'http://ltsc.ieee.org/xsd/imsccv1p3/LOM/manifest https://www.imsglobal.org/profile/cc/ccv1p3/LOM/ccv1p3_lommanifest_v1p0.xsd ' + 'http://ltsc.ieee.org/xsd/imsccv1p3/LOM/resource https://www.imsglobal.org/profile/cc/ccv1p3/LOM/ccv1p3_lomresource_v1p0.xsd', }, - metadata: this.props.metadata.getManifestXmlObject(), + metadata: this.props.metadata.getManifestXmlObject(CommonCartridgeElementType.METADATA), organizations: new CommonCartridgeOrganizationsWrapperElementV130({ type: CommonCartridgeElementType.ORGANIZATIONS_WRAPPER, version: this.props.version, items: this.props.organizations, - }).getManifestXmlObject(), + }).getManifestXmlObject(CommonCartridgeElementType.ORGANIZATIONS_WRAPPER), ...new CommonCartridgeResourcesWrapperElementV130({ type: CommonCartridgeElementType.RESOURCES_WRAPPER, version: this.props.version, items: this.props.resources, - }).getManifestXmlObject(), + }).getManifestXmlObject(CommonCartridgeElementType.RESOURCES_WRAPPER), }, }; } diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-resource-factory.spec.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-resource-factory.spec.ts index ad630ea25d8..799e435ee00 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-resource-factory.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-resource-factory.spec.ts @@ -1,9 +1,9 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { createCommonCartridgeManifestResourcePropsV130, createCommonCartridgeWebContentResourcePropsV130, createCommonCartridgeWeblinkResourcePropsV130, } from '../../../testing/common-cartridge-resource-props.factory'; +import { ResourceTypeNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeManifestResourceV130 } from './common-cartridge-manifest-resource'; import { CommonCartridgeResourceFactoryV130 } from './common-cartridge-resource-factory'; import { CommonCartridgeWebContentResourceV130 } from './common-cartridge-web-content-resource'; @@ -41,10 +41,10 @@ describe('CommonCartridgeResourceFactoryV130', () => { }); describe('when resource type is not supported', () => { - it('should throw error', () => { + it('should throw ResourceTypeNotSupportedLoggableException', () => { expect(() => CommonCartridgeResourceFactoryV130.createResource({} as CommonCartridgeWebLinkResourcePropsV130) - ).toThrow(InternalServerErrorException); + ).toThrow(ResourceTypeNotSupportedLoggableException); }); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-resource-factory.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-resource-factory.ts index 9be5fad11c1..a842c1a4a98 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-resource-factory.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-resource-factory.ts @@ -1,6 +1,6 @@ import { CommonCartridgeResourceType } from '../../common-cartridge.enums'; +import { ResourceTypeNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeResource } from '../../interfaces'; -import { createResourceTypeNotSupportedError } from '../../utils'; import { CommonCartridgeManifestResourcePropsV130, CommonCartridgeManifestResourceV130, @@ -31,7 +31,7 @@ export class CommonCartridgeResourceFactoryV130 { case CommonCartridgeResourceType.WEB_LINK: return new CommonCartridgeWebLinkResourceV130(props); default: - throw createResourceTypeNotSupportedError(type); + throw new ResourceTypeNotSupportedLoggableException(type); } } } diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-web-content-resource.spec.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-web-content-resource.spec.ts index e1b3334fd7d..44ab6b65002 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-web-content-resource.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-web-content-resource.spec.ts @@ -1,28 +1,9 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { createCommonCartridgeWebContentResourcePropsV130 } from '../../../testing/common-cartridge-resource-props.factory'; -import { CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException, VersionNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeWebContentResourceV130 } from './common-cartridge-web-content-resource'; describe('CommonCartridgeWebContentResourceV130', () => { - describe('canInline', () => { - describe('when using Common Cartridge version 1.3.0', () => { - const setup = () => { - const props = createCommonCartridgeWebContentResourcePropsV130(); - const sut = new CommonCartridgeWebContentResourceV130(props); - - return { sut }; - }; - - it('should return false', () => { - const { sut } = setup(); - - const result = sut.canInline(); - - expect(result).toBe(false); - }); - }); - }); - describe('getFilePath', () => { describe('when using Common Cartridge version 1.3.0', () => { const setup = () => { @@ -85,14 +66,37 @@ describe('CommonCartridgeWebContentResourceV130', () => { it('should throw error', () => { expect(() => new CommonCartridgeWebContentResourceV130(notSupportedProps)).toThrow( - InternalServerErrorException + VersionNotSupportedLoggableException ); }); }); }); describe('getManifestXmlObject', () => { - describe('when using Common Cartridge version 1.3.0', () => { + describe('when creating organization xml object', () => { + const setup = () => { + const props = createCommonCartridgeWebContentResourcePropsV130(); + const sut = new CommonCartridgeWebContentResourceV130(props); + + return { sut, props }; + }; + + it('should return organization manifest fragment', () => { + const { sut, props } = setup(); + + const result = sut.getManifestXmlObject(CommonCartridgeElementType.ORGANIZATION); + + expect(result).toEqual({ + $: { + identifier: expect.any(String), + identifierref: props.identifier, + }, + title: props.title, + }); + }); + }); + + describe('when creating resource xml object', () => { const setup = () => { const props = createCommonCartridgeWebContentResourcePropsV130(); const sut = new CommonCartridgeWebContentResourceV130(props); @@ -100,10 +104,10 @@ describe('CommonCartridgeWebContentResourceV130', () => { return { sut, props }; }; - it('should return the manifest XML object', () => { + it('should return resource manifest fragment', () => { const { sut, props } = setup(); - const result = sut.getManifestXmlObject(); + const result = sut.getManifestXmlObject(CommonCartridgeElementType.RESOURCE); expect(result).toEqual({ $: { @@ -119,5 +123,21 @@ describe('CommonCartridgeWebContentResourceV130', () => { }); }); }); + + describe('when using unsupported element type', () => { + const setup = () => { + const unknownElementType = 'unknown' as CommonCartridgeElementType; + const props = createCommonCartridgeWebContentResourcePropsV130(); + const sut = new CommonCartridgeWebContentResourceV130(props); + + return { sut, unknownElementType }; + }; + + it('should throw error', () => { + const { sut, unknownElementType } = setup(); + + expect(() => sut.getManifestXmlObject(unknownElementType)).toThrow(ElementTypeNotSupportedLoggableException); + }); + }); }); }); diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-web-content-resource.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-web-content-resource.ts index eb168087a52..21aa09a0a2d 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-web-content-resource.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-web-content-resource.ts @@ -1,10 +1,13 @@ import { + CommonCartridgeElementType, CommonCartridgeIntendedUseType, CommonCartridgeResourceType, CommonCartridgeVersion, } from '../../common-cartridge.enums'; -import { CommonCartridgeResource } from '../../interfaces'; -import { checkIntendedUse } from '../../utils'; +import { CommonCartridgeGuard } from '../../common-cartridge.guard'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; +import { CommonCartridgeResource, XmlObject } from '../../interfaces'; +import { createIdentifier } from '../../utils'; export type CommonCartridgeWebContentResourcePropsV130 = { type: CommonCartridgeResourceType.WEB_CONTENT; @@ -26,11 +29,25 @@ export class CommonCartridgeWebContentResourceV130 extends CommonCartridgeResour constructor(private readonly props: CommonCartridgeWebContentResourcePropsV130) { super(props); - checkIntendedUse(props.intendedUse, CommonCartridgeWebContentResourceV130.SUPPORTED_INTENDED_USES); + CommonCartridgeGuard.checkIntendedUse( + props.intendedUse, + CommonCartridgeWebContentResourceV130.SUPPORTED_INTENDED_USES + ); } - public canInline(): boolean { - return false; + public getSupportedVersion(): CommonCartridgeVersion { + return CommonCartridgeVersion.V_1_3_0; + } + + public getManifestXmlObject(elementType: CommonCartridgeElementType): XmlObject { + switch (elementType) { + case CommonCartridgeElementType.RESOURCE: + return this.getManifestResourceXmlObject(); + case CommonCartridgeElementType.ORGANIZATION: + return this.getManifestOrganizationXmlObject(); + default: + throw new ElementTypeNotSupportedLoggableException(elementType); + } } public getFilePath(): string { @@ -41,11 +58,17 @@ export class CommonCartridgeWebContentResourceV130 extends CommonCartridgeResour return this.props.html; } - public getSupportedVersion(): CommonCartridgeVersion { - return CommonCartridgeVersion.V_1_3_0; + private getManifestOrganizationXmlObject(): XmlObject { + return { + $: { + identifier: createIdentifier(), + identifierref: this.props.identifier, + }, + title: this.props.title, + }; } - public getManifestXmlObject(): Record { + private getManifestResourceXmlObject(): XmlObject { return { $: { identifier: this.props.identifier, diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-web-link-resource.spec.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-web-link-resource.spec.ts index d6aee5e394f..83985e8f717 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-web-link-resource.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-web-link-resource.spec.ts @@ -1,29 +1,10 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { readFile } from 'node:fs/promises'; import { createCommonCartridgeWeblinkResourcePropsV130 } from '../../../testing/common-cartridge-resource-props.factory'; -import { CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { CommonCartridgeElementType, CommonCartridgeVersion } from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException, VersionNotSupportedLoggableException } from '../../errors'; import { CommonCartridgeWebLinkResourceV130 } from './common-cartridge-web-link-resource'; describe('CommonCartridgeWebLinkResourceV130', () => { - describe('canInline', () => { - describe('when using Common Cartridge version 1.3.0', () => { - const setup = () => { - const props = createCommonCartridgeWeblinkResourcePropsV130(); - const sut = new CommonCartridgeWebLinkResourceV130(props); - - return { sut }; - }; - - it('should return false', () => { - const { sut } = setup(); - - const result = sut.canInline(); - - expect(result).toBe(false); - }); - }); - }); - describe('getFilePath', () => { describe('when using Common Cartridge version 1.3.0', () => { const setup = () => { @@ -94,13 +75,38 @@ describe('CommonCartridgeWebLinkResourceV130', () => { notSupportedProps.version = CommonCartridgeVersion.V_1_1_0; it('should throw error', () => { - expect(() => new CommonCartridgeWebLinkResourceV130(notSupportedProps)).toThrow(InternalServerErrorException); + expect(() => new CommonCartridgeWebLinkResourceV130(notSupportedProps)).toThrow( + VersionNotSupportedLoggableException + ); }); }); }); describe('getManifestXmlObject', () => { - describe('when using Common Cartridge version 1.3.0', () => { + describe('when creating organization xml object', () => { + const setup = () => { + const props = createCommonCartridgeWeblinkResourcePropsV130(); + const sut = new CommonCartridgeWebLinkResourceV130(props); + + return { sut, props }; + }; + + it('should return organization manifest fragment', () => { + const { sut, props } = setup(); + + const result = sut.getManifestXmlObject(CommonCartridgeElementType.ORGANIZATION); + + expect(result).toEqual({ + $: { + identifier: expect.any(String), + identifierref: props.identifier, + }, + title: props.title, + }); + }); + }); + + describe('when creating resource xml object', () => { const setup = () => { const props = createCommonCartridgeWeblinkResourcePropsV130(); const sut = new CommonCartridgeWebLinkResourceV130(props); @@ -108,10 +114,10 @@ describe('CommonCartridgeWebLinkResourceV130', () => { return { sut, props }; }; - it('should return the manifest XML object', () => { + it('should return resource manifest fragment', () => { const { sut, props } = setup(); - const result = sut.getManifestXmlObject(); + const result = sut.getManifestXmlObject(CommonCartridgeElementType.RESOURCE); expect(result).toEqual({ $: { @@ -127,4 +133,20 @@ describe('CommonCartridgeWebLinkResourceV130', () => { }); }); }); + + describe('when using unsupported element type', () => { + const setup = () => { + const unknownElementType = 'unknown' as CommonCartridgeElementType; + const props = createCommonCartridgeWeblinkResourcePropsV130(); + const sut = new CommonCartridgeWebLinkResourceV130(props); + + return { sut, unknownElementType }; + }; + + it('should throw error', () => { + const { sut, unknownElementType } = setup(); + + expect(() => sut.getManifestXmlObject(unknownElementType)).toThrow(ElementTypeNotSupportedLoggableException); + }); + }); }); diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-web-link-resource.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-web-link-resource.ts index 1cfd8a3df5b..eb5d66ad8eb 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-web-link-resource.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/common-cartridge-web-link-resource.ts @@ -1,6 +1,11 @@ -import { CommonCartridgeResourceType, CommonCartridgeVersion } from '../../common-cartridge.enums'; -import { CommonCartridgeResource } from '../../interfaces'; -import { buildXmlString } from '../../utils'; +import { + CommonCartridgeElementType, + CommonCartridgeResourceType, + CommonCartridgeVersion, +} from '../../common-cartridge.enums'; +import { ElementTypeNotSupportedLoggableException } from '../../errors'; +import { CommonCartridgeResource, XmlObject } from '../../interfaces'; +import { buildXmlString, createIdentifier } from '../../utils'; export type CommonCartridgeWebLinkResourcePropsV130 = { type: CommonCartridgeResourceType.WEB_LINK; @@ -18,8 +23,19 @@ export class CommonCartridgeWebLinkResourceV130 extends CommonCartridgeResource super(props); } - public canInline(): boolean { - return false; + public getSupportedVersion(): CommonCartridgeVersion { + return CommonCartridgeVersion.V_1_3_0; + } + + public getManifestXmlObject(elementType: CommonCartridgeElementType): XmlObject { + switch (elementType) { + case CommonCartridgeElementType.RESOURCE: + return this.getManifestResourceXmlObject(); + case CommonCartridgeElementType.ORGANIZATION: + return this.getManifestOrganizationXmlObject(); + default: + throw new ElementTypeNotSupportedLoggableException(elementType); + } } public getFilePath(): string { @@ -47,11 +63,17 @@ export class CommonCartridgeWebLinkResourceV130 extends CommonCartridgeResource }); } - public getSupportedVersion(): CommonCartridgeVersion { - return CommonCartridgeVersion.V_1_3_0; + private getManifestOrganizationXmlObject(): XmlObject { + return { + $: { + identifier: createIdentifier(), + identifierref: this.props.identifier, + }, + title: this.props.title, + }; } - public getManifestXmlObject(): Record { + private getManifestResourceXmlObject(): XmlObject { return { $: { identifier: this.props.identifier, diff --git a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/index.ts b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/index.ts index dca0baca323..90e0b5121ea 100644 --- a/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/index.ts +++ b/apps/server/src/modules/common-cartridge/export/resources/v1.3.0/index.ts @@ -1,7 +1,7 @@ export { CommonCartridgeManifestResourcePropsV130 } from './common-cartridge-manifest-resource'; export { CommonCartridgeResourceFactoryV130 } from './common-cartridge-resource-factory'; export { - CommonCartridgeWebContentResourcePropsV130, CommonCartridgeWebContentResourceV130, + CommonCartridgeWebContentResourcePropsV130, } from './common-cartridge-web-content-resource'; export { CommonCartridgeWebLinkResourcePropsV130 } from './common-cartridge-web-link-resource'; diff --git a/apps/server/src/modules/common-cartridge/export/utils.spec.ts b/apps/server/src/modules/common-cartridge/export/utils.spec.ts index 3dae9ffa1c8..2a5b5b7de60 100644 --- a/apps/server/src/modules/common-cartridge/export/utils.spec.ts +++ b/apps/server/src/modules/common-cartridge/export/utils.spec.ts @@ -1,14 +1,5 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { ObjectId } from 'bson'; -import { CommonCartridgeVersion } from './common-cartridge.enums'; -import { - buildXmlString, - checkIntendedUse, - createElementTypeNotSupportedError, - createIdentifier, - createResourceTypeNotSupportedError, - createVersionNotSupportedError, -} from './utils'; +import { buildXmlString, createIdentifier } from './utils'; describe('CommonCartridgeUtils', () => { describe('buildXmlString', () => { @@ -19,17 +10,6 @@ describe('CommonCartridgeUtils', () => { }); }); - describe('createVersionNotSupportedError', () => { - describe('when creating error', () => { - it('should return error with message', () => { - const error = createVersionNotSupportedError(CommonCartridgeVersion.V_1_0_0); - - expect(error).toBeInstanceOf(InternalServerErrorException); - expect(error.message).toBe('Common Cartridge version 1.0.0 is not supported'); - }); - }); - }); - describe('createIdentifier', () => { describe('when creating identifier', () => { it('should return identifier with prefix', () => { @@ -43,42 +23,4 @@ describe('CommonCartridgeUtils', () => { }); }); }); - - describe('createResourceTypeNotSupportedError', () => { - describe('when creating error', () => { - it('should return error with message', () => { - const resourceType = 'unsupported'; - - const error = createResourceTypeNotSupportedError(resourceType); - - expect(error).toBeInstanceOf(InternalServerErrorException); - expect(error.message).toBe(`Common Cartridge resource type ${resourceType} is not supported`); - }); - }); - }); - - describe('createElementTypeNotSupportedError', () => { - describe('when creating error', () => { - it('should return error with message', () => { - const elementType = 'unsupported'; - - const error = createElementTypeNotSupportedError(elementType); - - expect(error).toBeInstanceOf(InternalServerErrorException); - expect(error.message).toBe(`Common Cartridge element type ${elementType} is not supported`); - }); - }); - }); - - describe('checkIntendedUse', () => { - describe('when intended use is not supported', () => { - it('should throw error', () => { - const supportedIntendedUses = ['use1', 'use2']; - - expect(() => checkIntendedUse('use3', supportedIntendedUses)).toThrowError( - 'Intended use use3 is not supported' - ); - }); - }); - }); }); diff --git a/apps/server/src/modules/common-cartridge/export/utils.ts b/apps/server/src/modules/common-cartridge/export/utils.ts index 0b110f783c5..0e22175216e 100644 --- a/apps/server/src/modules/common-cartridge/export/utils.ts +++ b/apps/server/src/modules/common-cartridge/export/utils.ts @@ -1,4 +1,3 @@ -import { InternalServerErrorException } from '@nestjs/common'; import { ObjectId } from 'bson'; import { Builder } from 'xml2js'; @@ -17,19 +16,6 @@ export function buildXmlString(obj: unknown): string { return xmlBuilder.buildObject(obj); } -export function createVersionNotSupportedError(version: string): Error { - return new InternalServerErrorException(`Common Cartridge version ${version} is not supported`); -} - -export function createResourceTypeNotSupportedError(type: string): Error { - return new InternalServerErrorException(`Common Cartridge resource type ${type} is not supported`); -} - -export function createElementTypeNotSupportedError(type: string): Error { - // AI next 1 line - return new InternalServerErrorException(`Common Cartridge element type ${type} is not supported`); -} - export function createIdentifier(identifier?: string | ObjectId): string { if (!identifier) { return `i${new ObjectId().toString()}`; @@ -37,9 +23,3 @@ export function createIdentifier(identifier?: string | ObjectId): string { return `i${identifier.toString()}`; } - -export function checkIntendedUse(intendedUse: string, supportedIntendedUses: string[]): void | never { - if (!supportedIntendedUses.includes(intendedUse)) { - throw new Error(`Intended use ${intendedUse} is not supported`); - } -} diff --git a/apps/server/src/modules/common-cartridge/index.ts b/apps/server/src/modules/common-cartridge/index.ts index 82b46a983b3..2f177a38fb6 100644 --- a/apps/server/src/modules/common-cartridge/index.ts +++ b/apps/server/src/modules/common-cartridge/index.ts @@ -1,11 +1,9 @@ export { CommonCartridgeFileBuilder, CommonCartridgeFileBuilderProps, + CommonCartridgeOrganizationProps, } from './export/builders/common-cartridge-file-builder'; -export { - CommonCartridgeOrganizationBuilder, - CommonCartridgeOrganizationBuilderOptions, -} from './export/builders/common-cartridge-organization-builder'; +export { CommonCartridgeOrganizationNode } from './export/builders/common-cartridge-organization-node'; export { CommonCartridgeElementType, CommonCartridgeIntendedUseType, @@ -19,8 +17,10 @@ export { CommonCartridgeFileParser } from './import/common-cartridge-file-parser export { CommonCartridgeResourceTypeV1P1 } from './import/common-cartridge-import.enums'; export { CommonCartridgeFileParserOptions, + CommonCartridgeOrganizationProps as CommonCartridgeImportOrganizationProps, CommonCartridgeResourceProps as CommonCartridgeImportResourceProps, - CommonCartridgeOrganizationProps, + CommonCartridgeWebContentResourceProps as CommonCartridgeImportWebContentResourceProps, + CommonCartridgeWebLinkResourceProps as CommonCartridgeImportWebLinkResourceProps, DEFAULT_FILE_PARSER_OPTIONS, } from './import/common-cartridge-import.types'; export { CommonCartridgeImportUtils } from './import/utils/common-cartridge-import-utils'; diff --git a/apps/server/src/modules/common-cartridge/testing/common-cartridge-element-props.factory.ts b/apps/server/src/modules/common-cartridge/testing/common-cartridge-element-props.factory.ts index 000ad586840..f6d05b9710b 100644 --- a/apps/server/src/modules/common-cartridge/testing/common-cartridge-element-props.factory.ts +++ b/apps/server/src/modules/common-cartridge/testing/common-cartridge-element-props.factory.ts @@ -1,5 +1,11 @@ import { faker } from '@faker-js/faker'; -import { CommonCartridgeElementType, CommonCartridgeVersion } from '@modules/common-cartridge'; +import { + CommonCartridgeElementProps, + CommonCartridgeElementType, + CommonCartridgeOrganizationProps, + CommonCartridgeVersion, +} from '@modules/common-cartridge'; +import { CommonCartridgeOrganizationNodeProps } from '../export/builders/common-cartridge-organization-node'; import { CommonCartridgeMetadataElementPropsV110 } from '../export/elements/v1.1.0/common-cartridge-metadata-element'; import { CommonCartridgeOrganizationElementPropsV110 } from '../export/elements/v1.1.0/common-cartridge-organization-element'; import { CommonCartridgeOrganizationsWrapperElementPropsV110 } from '../export/elements/v1.1.0/common-cartridge-organizations-wrapper-element'; @@ -94,3 +100,28 @@ export function createCommonCartridgeResourcesWrapperElementPropsV130( items: items || [], }; } + +export function createCommonCartridgeMetadataElementProps(): CommonCartridgeElementProps { + return { + type: CommonCartridgeElementType.METADATA, + title: faker.lorem.words(), + creationDate: new Date(), + copyrightOwners: ['John Doe', 'Jane Doe'], + }; +} + +export function createCommonCartridgeOrganizationProps(): CommonCartridgeOrganizationProps { + return { + title: faker.lorem.words(), + identifier: faker.string.uuid(), + }; +} + +export function createCommonCartridgeOrganizationNodeProps(): CommonCartridgeOrganizationNodeProps { + return { + type: CommonCartridgeElementType.ORGANIZATION, + identifier: faker.string.uuid(), + title: faker.lorem.words(), + version: CommonCartridgeVersion.V_1_1_0, + }; +} diff --git a/apps/server/src/modules/common-cartridge/testing/common-cartridge-resource-props.factory.ts b/apps/server/src/modules/common-cartridge/testing/common-cartridge-resource-props.factory.ts index cbe4f31f0c6..136f0e8ac6f 100644 --- a/apps/server/src/modules/common-cartridge/testing/common-cartridge-resource-props.factory.ts +++ b/apps/server/src/modules/common-cartridge/testing/common-cartridge-resource-props.factory.ts @@ -1,16 +1,21 @@ import { faker } from '@faker-js/faker'; import { CommonCartridgeIntendedUseType, + CommonCartridgeResourceProps, CommonCartridgeResourceType, CommonCartridgeVersion, } from '@modules/common-cartridge'; -import { CommonCartridgeElement } from '../export/interfaces/common-cartridge-element.interface'; +import { CommonCartridgeElementFactory } from '../export/elements/common-cartridge-element-factory'; import { CommonCartridgeManifestResourcePropsV110 } from '../export/resources/v1.1.0/common-cartridge-manifest-resource'; import { CommonCartridgeWebContentResourcePropsV110 } from '../export/resources/v1.1.0/common-cartridge-web-content-resource'; import { CommonCartridgeWebLinkResourcePropsV110 } from '../export/resources/v1.1.0/common-cartridge-web-link-resource'; import { CommonCartridgeManifestResourcePropsV130 } from '../export/resources/v1.3.0/common-cartridge-manifest-resource'; import { CommonCartridgeWebContentResourcePropsV130 } from '../export/resources/v1.3.0/common-cartridge-web-content-resource'; import { CommonCartridgeWebLinkResourcePropsV130 } from '../export/resources/v1.3.0/common-cartridge-web-link-resource'; +import { + createCommonCartridgeMetadataElementPropsV110, + createCommonCartridgeMetadataElementPropsV130, +} from './common-cartridge-element-props.factory'; export function createCommonCartridgeWeblinkResourcePropsV110(): CommonCartridgeWebLinkResourcePropsV110 { return { @@ -63,7 +68,7 @@ export function createCommonCartridgeManifestResourcePropsV110(): CommonCartridg type: CommonCartridgeResourceType.MANIFEST, version: CommonCartridgeVersion.V_1_1_0, identifier: faker.string.uuid(), - metadata: {} as CommonCartridgeElement, + metadata: CommonCartridgeElementFactory.createElement(createCommonCartridgeMetadataElementPropsV110()), organizations: [], resources: [], }; @@ -74,8 +79,27 @@ export function createCommonCartridgeManifestResourcePropsV130(): CommonCartridg type: CommonCartridgeResourceType.MANIFEST, version: CommonCartridgeVersion.V_1_3_0, identifier: faker.string.uuid(), - metadata: {} as CommonCartridgeElement, + metadata: CommonCartridgeElementFactory.createElement(createCommonCartridgeMetadataElementPropsV130()), organizations: [], resources: [], }; } + +export function createCommonCartridgeWebLinkResourceProps(): CommonCartridgeResourceProps { + return { + type: CommonCartridgeResourceType.WEB_LINK, + title: faker.lorem.words(), + identifier: faker.string.uuid(), + url: faker.internet.url(), + }; +} + +export function createCommonCartridgeWebContentResourceProps(): CommonCartridgeResourceProps { + return { + type: CommonCartridgeResourceType.WEB_CONTENT, + title: faker.lorem.words(), + identifier: faker.string.uuid(), + html: faker.lorem.paragraph(), + intendedUse: CommonCartridgeIntendedUseType.UNSPECIFIED, + }; +} diff --git a/apps/server/src/modules/learnroom/learnroom.module.ts b/apps/server/src/modules/learnroom/learnroom.module.ts index 3627b94b129..5f112d07dff 100644 --- a/apps/server/src/modules/learnroom/learnroom.module.ts +++ b/apps/server/src/modules/learnroom/learnroom.module.ts @@ -18,8 +18,8 @@ import { import { LoggerModule } from '@src/core/logger'; import { BoardNodeRepo } from '../board/repo'; import { COURSE_REPO } from './domain'; +import { CommonCartridgeExportMapper } from './mapper/common-cartridge-export.mapper'; import { CommonCartridgeImportMapper } from './mapper/common-cartridge-import.mapper'; -import { CommonCartridgeMapper } from './mapper/common-cartridge.mapper'; import { ColumnBoardNodeRepo } from './repo'; import { CourseMikroOrmRepo } from './repo/mikro-orm/course.repo'; import { @@ -57,7 +57,7 @@ import { CommonCartridgeFileValidatorPipe } from './utils'; CommonCartridgeExportService, CommonCartridgeFileValidatorPipe, CommonCartridgeImportService, - CommonCartridgeMapper, + CommonCartridgeExportMapper, CommonCartridgeImportMapper, CourseCopyService, CourseGroupRepo, diff --git a/apps/server/src/modules/learnroom/mapper/common-cartridge.mapper.spec.ts b/apps/server/src/modules/learnroom/mapper/common-cartridge-export.mapper.spec.ts similarity index 96% rename from apps/server/src/modules/learnroom/mapper/common-cartridge.mapper.spec.ts rename to apps/server/src/modules/learnroom/mapper/common-cartridge-export.mapper.spec.ts index 2fe6f0967a1..0e4bdc92e82 100644 --- a/apps/server/src/modules/learnroom/mapper/common-cartridge.mapper.spec.ts +++ b/apps/server/src/modules/learnroom/mapper/common-cartridge-export.mapper.spec.ts @@ -5,7 +5,7 @@ import { CommonCartridgeElementType, CommonCartridgeFileBuilderProps, CommonCartridgeIntendedUseType, - CommonCartridgeOrganizationBuilderOptions, + CommonCartridgeOrganizationProps, CommonCartridgeResourceProps, CommonCartridgeResourceType, CommonCartridgeVersion, @@ -18,25 +18,25 @@ import { ComponentProperties, ComponentType } from '@shared/domain/entity'; import { courseFactory, lessonFactory, setupEntities, taskFactory, userFactory } from '@shared/testing'; import { linkElementFactory, richTextElementFactory } from '@modules/board/testing'; import { LearnroomConfig } from '../learnroom.config'; -import { CommonCartridgeMapper } from './common-cartridge.mapper'; +import { CommonCartridgeExportMapper } from './common-cartridge-export.mapper'; -describe('CommonCartridgeMapper', () => { +describe('CommonCartridgeExportMapper', () => { let module: TestingModule; - let sut: CommonCartridgeMapper; + let sut: CommonCartridgeExportMapper; let configServiceMock: DeepMocked>; beforeAll(async () => { await setupEntities(); module = await Test.createTestingModule({ providers: [ - CommonCartridgeMapper, + CommonCartridgeExportMapper, { provide: ConfigService, useValue: createMock>(), }, ], }).compile(); - sut = module.get(CommonCartridgeMapper); + sut = module.get(CommonCartridgeExportMapper); configServiceMock = module.get(ConfigService); }); @@ -88,7 +88,7 @@ describe('CommonCartridgeMapper', () => { const { lesson } = setup(); const organizationProps = sut.mapLessonToOrganization(lesson); - expect(organizationProps).toStrictEqual>({ + expect(organizationProps).toStrictEqual>({ identifier: createIdentifier(lesson.id), title: lesson.name, }); @@ -117,7 +117,7 @@ describe('CommonCartridgeMapper', () => { const { componentProps } = setup(); const organizationProps = sut.mapContentToOrganization(componentProps); - expect(organizationProps).toStrictEqual>({ + expect(organizationProps).toStrictEqual>({ identifier: expect.any(String), title: componentProps.title, }); @@ -188,7 +188,7 @@ describe('CommonCartridgeMapper', () => { const { task } = setup(); const organizationProps = sut.mapTaskToOrganization(task); - expect(organizationProps).toStrictEqual>({ + expect(organizationProps).toStrictEqual>({ identifier: expect.any(String), title: task.name, }); @@ -415,10 +415,7 @@ describe('CommonCartridgeMapper', () => { expect(resourceProps).toStrictEqual({ type: CommonCartridgeResourceType.WEB_CONTENT, identifier: expect.any(String), - title: richTextElement.text - .slice(0, 50) - .replace(/<[^>]*>?/gm, '') - .concat('...'), + title: richTextElement.text.slice(0, 50).replace(/<[^>]*>?/gm, ''), html: `

${richTextElement.text}

`, intendedUse: CommonCartridgeIntendedUseType.UNSPECIFIED, }); diff --git a/apps/server/src/modules/learnroom/mapper/common-cartridge.mapper.ts b/apps/server/src/modules/learnroom/mapper/common-cartridge-export.mapper.ts similarity index 88% rename from apps/server/src/modules/learnroom/mapper/common-cartridge.mapper.ts rename to apps/server/src/modules/learnroom/mapper/common-cartridge-export.mapper.ts index 3d684905903..989d230c3eb 100644 --- a/apps/server/src/modules/learnroom/mapper/common-cartridge.mapper.ts +++ b/apps/server/src/modules/learnroom/mapper/common-cartridge-export.mapper.ts @@ -2,21 +2,21 @@ import { LinkElement, RichTextElement } from '@modules/board/domain'; import { CommonCartridgeElementProps, CommonCartridgeElementType, - CommonCartridgeFileBuilderProps, CommonCartridgeIntendedUseType, - CommonCartridgeOrganizationBuilderOptions, CommonCartridgeResourceProps, CommonCartridgeResourceType, CommonCartridgeVersion, createIdentifier, } from '@modules/common-cartridge'; +import { CommonCartridgeOrganizationProps } from '@modules/common-cartridge/export/builders/common-cartridge-file-builder'; import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { ComponentProperties, ComponentType, Course, LessonEntity, Task } from '@shared/domain/entity'; +import sanitizeHtml from 'sanitize-html'; import { LearnroomConfig } from '../learnroom.config'; @Injectable() -export class CommonCartridgeMapper { +export class CommonCartridgeExportMapper { constructor(private readonly configService: ConfigService) {} public mapCourseToMetadata(course: Course): CommonCartridgeElementProps { @@ -28,21 +28,21 @@ export class CommonCartridgeMapper { }; } - public mapLessonToOrganization(lesson: LessonEntity): CommonCartridgeOrganizationBuilderOptions { + public mapLessonToOrganization(lesson: LessonEntity): CommonCartridgeOrganizationProps { return { identifier: createIdentifier(lesson.id), title: lesson.name, }; } - public mapContentToOrganization(content: ComponentProperties): CommonCartridgeOrganizationBuilderOptions { + public mapContentToOrganization(content: ComponentProperties): CommonCartridgeOrganizationProps { return { identifier: createIdentifier(content._id), title: content.title, }; } - public mapTaskToOrganization(task: Task): CommonCartridgeOrganizationBuilderOptions { + public mapTaskToOrganization(task: Task): CommonCartridgeOrganizationProps { return { identifier: createIdentifier(), title: task.name, @@ -112,7 +112,10 @@ export class CommonCartridgeMapper { } } - public mapCourseToManifest(version: CommonCartridgeVersion, course: Course): CommonCartridgeFileBuilderProps { + public mapCourseToManifest( + version: CommonCartridgeVersion, + course: Course + ): { version: CommonCartridgeVersion; identifier: string } { return { version, identifier: createIdentifier(course.id), @@ -139,10 +142,11 @@ export class CommonCartridgeMapper { } private getTextTitle(text: string): string { - const title = text - .slice(0, 50) - .replace(/<[^>]*>?/gm, '') - .concat('...'); + const title = sanitizeHtml(text, { + allowedTags: [], + allowedAttributes: {}, + }).slice(0, 50); + return title; } } diff --git a/apps/server/src/modules/learnroom/mapper/common-cartridge-import.mapper.spec.ts b/apps/server/src/modules/learnroom/mapper/common-cartridge-import.mapper.spec.ts index c9a4f0d544f..a2515c4d367 100644 --- a/apps/server/src/modules/learnroom/mapper/common-cartridge-import.mapper.spec.ts +++ b/apps/server/src/modules/learnroom/mapper/common-cartridge-import.mapper.spec.ts @@ -1,12 +1,11 @@ import { faker } from '@faker-js/faker'; -import { Test, TestingModule } from '@nestjs/testing'; -import { ContentElementType } from '@modules/board/domain'; -import { LinkContentBody, RichTextContentBody } from '@modules/board/controller/dto'; +import { ContentElementType, LinkContentBody, RichTextContentBody } from '@modules/board'; import { + CommonCartridgeImportOrganizationProps, CommonCartridgeImportResourceProps, - CommonCartridgeOrganizationProps, CommonCartridgeResourceTypeV1P1, } from '@modules/common-cartridge'; +import { Test, TestingModule } from '@nestjs/testing'; import { InputFormat } from '@shared/domain/types'; import { CommonCartridgeImportMapper } from './common-cartridge-import.mapper'; @@ -15,7 +14,7 @@ describe('CommonCartridgeImportMapper', () => { let sut: CommonCartridgeImportMapper; const setupOrganization = () => { - const organization: CommonCartridgeOrganizationProps = { + const organization: CommonCartridgeImportOrganizationProps = { path: faker.string.uuid(), pathDepth: faker.number.int({ min: 0, max: 3 }), identifier: faker.string.uuid(), diff --git a/apps/server/src/modules/learnroom/mapper/common-cartridge-import.mapper.ts b/apps/server/src/modules/learnroom/mapper/common-cartridge-import.mapper.ts index e199bf466a9..826186cd4e8 100644 --- a/apps/server/src/modules/learnroom/mapper/common-cartridge-import.mapper.ts +++ b/apps/server/src/modules/learnroom/mapper/common-cartridge-import.mapper.ts @@ -1,13 +1,13 @@ +import { AnyElementContentBody, ContentElementType, LinkContentBody, RichTextContentBody } from '@modules/board'; +import { + CommonCartridgeImportResourceProps, + CommonCartridgeImportWebContentResourceProps, + CommonCartridgeImportWebLinkResourceProps, + CommonCartridgeOrganizationProps, + CommonCartridgeResourceTypeV1P1, +} from '@modules/common-cartridge'; import { Injectable } from '@nestjs/common'; -import { ContentElementType } from '@modules/board/domain'; import { InputFormat } from '@shared/domain/types'; -import { AnyElementContentBody, LinkContentBody, RichTextContentBody } from '@modules/board/controller/dto'; -import { CommonCartridgeOrganizationProps, CommonCartridgeResourceTypeV1P1 } from '@modules/common-cartridge'; -import { - CommonCartridgeResourceProps, - CommonCartridgeWebContentResourceProps, - CommonCartridgeWebLinkResourceProps, -} from '@src/modules/common-cartridge/import/common-cartridge-import.types'; @Injectable() export class CommonCartridgeImportMapper { @@ -45,7 +45,7 @@ export class CommonCartridgeImportMapper { } } - public mapResourceToContentElementBody(resource: CommonCartridgeResourceProps): AnyElementContentBody { + public mapResourceToContentElementBody(resource: CommonCartridgeImportResourceProps): AnyElementContentBody { switch (resource.type) { case CommonCartridgeResourceTypeV1P1.WEB_LINK: return this.createLinkContentElementBody(resource); @@ -56,7 +56,7 @@ export class CommonCartridgeImportMapper { } } - private createLinkContentElementBody(resource: CommonCartridgeWebLinkResourceProps): AnyElementContentBody { + private createLinkContentElementBody(resource: CommonCartridgeImportWebLinkResourceProps): AnyElementContentBody { const body = new LinkContentBody(); body.title = resource.title; @@ -65,7 +65,7 @@ export class CommonCartridgeImportMapper { return body; } - private createWebContentElementBody(resource: CommonCartridgeWebContentResourceProps): AnyElementContentBody { + private createWebContentElementBody(resource: CommonCartridgeImportWebContentResourceProps): AnyElementContentBody { const body = new RichTextContentBody(); body.text = resource.html; 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 605d9ad3cdc..4cd4ff7fab0 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,5 +1,13 @@ import { faker } from '@faker-js/faker'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { ColumnBoardService } from '@modules/board'; +import { + cardFactory, + columnBoardFactory, + columnFactory, + linkElementFactory, + richTextElementFactory, +} from '@modules/board/testing'; import { CommonCartridgeVersion } from '@modules/common-cartridge'; import { CommonCartridgeExportService, CourseService, LearnroomConfig } from '@modules/learnroom'; import { LessonService } from '@modules/lesson'; @@ -8,16 +16,8 @@ import { ConfigService } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; import { ComponentType } from '@shared/domain/entity'; import { courseFactory, lessonFactory, setupEntities, taskFactory } from '@shared/testing'; -import { ColumnBoardService } from '@src/modules/board'; -import { - cardFactory, - columnBoardFactory, - columnFactory, - linkElementFactory, - richTextElementFactory, -} from '@src/modules/board/testing'; import AdmZip from 'adm-zip'; -import { CommonCartridgeMapper } from '../mapper/common-cartridge.mapper'; +import { CommonCartridgeExportMapper } from '../mapper/common-cartridge-export.mapper'; describe('CommonCartridgeExportService', () => { let module: TestingModule; @@ -107,7 +107,7 @@ describe('CommonCartridgeExportService', () => { module = await Test.createTestingModule({ providers: [ CommonCartridgeExportService, - CommonCartridgeMapper, + CommonCartridgeExportMapper, { provide: CourseService, useValue: createMock(), 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 4c146bcfeb6..3300d60af1c 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 @@ -3,24 +3,24 @@ import { BoardExternalReferenceType, Card, Column, + ColumnBoardService, isCard, isColumn, isLinkElement, isRichTextElement, -} from '@modules/board/domain'; +} from '@modules/board'; import { CommonCartridgeFileBuilder, - CommonCartridgeOrganizationBuilder, + CommonCartridgeOrganizationNode, CommonCartridgeVersion, + createIdentifier, } from '@modules/common-cartridge'; import { LessonService } from '@modules/lesson'; import { TaskService } from '@modules/task'; import { Injectable } from '@nestjs/common'; import { ComponentProperties } from '@shared/domain/entity'; import { EntityId } from '@shared/domain/types'; -import { ColumnBoardService } from '@src/modules/board'; -import { createIdentifier } from '@src/modules/common-cartridge/export/utils'; -import { CommonCartridgeMapper } from '../mapper/common-cartridge.mapper'; +import { CommonCartridgeExportMapper } from '../mapper/common-cartridge-export.mapper'; import { CourseService } from './course.service'; @Injectable() @@ -30,7 +30,7 @@ export class CommonCartridgeExportService { private readonly lessonService: LessonService, private readonly taskService: TaskService, private readonly columnBoardService: ColumnBoardService, - private readonly commonCartridgeMapper: CommonCartridgeMapper + private readonly mapper: CommonCartridgeExportMapper ) {} public async exportCourse( @@ -42,9 +42,9 @@ export class CommonCartridgeExportService { exportedColumnBoards: string[] ): Promise { const course = await this.courseService.findById(courseId); - const builder = new CommonCartridgeFileBuilder(this.commonCartridgeMapper.mapCourseToManifest(version, course)); + const builder = new CommonCartridgeFileBuilder(this.mapper.mapCourseToManifest(version, course)); - builder.addMetadata(this.commonCartridgeMapper.mapCourseToMetadata(course)); + builder.addMetadata(this.mapper.mapCourseToMetadata(course)); await this.addLessons(builder, courseId, version, exportedTopics); await this.addTasks(builder, courseId, userId, version, exportedTasks); @@ -66,14 +66,14 @@ export class CommonCartridgeExportService { return; } - const organizationBuilder = builder.addOrganization(this.commonCartridgeMapper.mapLessonToOrganization(lesson)); + const lessonOrganization = builder.createOrganization(this.mapper.mapLessonToOrganization(lesson)); lesson.contents.forEach((content) => { - this.addComponentToOrganization(organizationBuilder, content); + this.addComponentToOrganization(content, lessonOrganization); }); lesson.getLessonLinkedTasks().forEach((task) => { - organizationBuilder.addResource(this.commonCartridgeMapper.mapTaskToResource(task, version)); + lessonOrganization.addResource(this.mapper.mapTaskToResource(task, version)); }); }); } @@ -91,8 +91,8 @@ export class CommonCartridgeExportService { return; } - const organization = builder.addOrganization({ - title: '', + const tasksOrganization = builder.createOrganization({ + title: 'Aufgaben', identifier: createIdentifier(), }); @@ -101,7 +101,7 @@ export class CommonCartridgeExportService { return; } - organization.addResource(this.commonCartridgeMapper.mapTaskToResource(task, version)); + tasksOrganization.addResource(this.mapper.mapTaskToResource(task, version)); }); } @@ -118,21 +118,21 @@ export class CommonCartridgeExportService { ).filter((cb) => exportedColumnBoards.includes(cb.id)); for (const columnBoard of columnBoards) { - const organization = builder.addOrganization({ + const columnBoardOrganization = builder.createOrganization({ title: columnBoard.title, identifier: createIdentifier(columnBoard.id), }); columnBoard.children .filter((child) => isColumn(child)) - .forEach((column) => this.addColumnToOrganization(column as Column, organization)); + .forEach((column) => this.addColumnToOrganization(column as Column, columnBoardOrganization)); } } - private addColumnToOrganization(column: Column, organizationBuilder: CommonCartridgeOrganizationBuilder): void { + private addColumnToOrganization(column: Column, columnBoardOrganization: CommonCartridgeOrganizationNode): void { const { id } = column; - const columnOrganization = organizationBuilder.addSubOrganization({ - title: column.title ?? '', + const columnOrganization = columnBoardOrganization.createChild({ + title: column.title || '', identifier: createIdentifier(id), }); @@ -141,49 +141,43 @@ export class CommonCartridgeExportService { .forEach((card) => this.addCardToOrganization(card as Card, columnOrganization)); } - private addCardToOrganization(card: Card, organizationBuilder: CommonCartridgeOrganizationBuilder): void { - const { id } = card; - const cardOrganization = organizationBuilder.addSubOrganization({ - title: card.title ?? '', - identifier: createIdentifier(id), + private addCardToOrganization(card: Card, columnOrganization: CommonCartridgeOrganizationNode): void { + const cardOrganization = columnOrganization.createChild({ + title: card.title || '', + identifier: createIdentifier(card.id), }); card.children.forEach((child) => this.addCardElementToOrganization(child, cardOrganization)); } - private addCardElementToOrganization( - element: AnyBoardNode, - organizationBuilder: CommonCartridgeOrganizationBuilder - ): void { + private addCardElementToOrganization(element: AnyBoardNode, cardOrganization: CommonCartridgeOrganizationNode): void { if (isRichTextElement(element)) { - const resource = this.commonCartridgeMapper.mapRichTextElementToResource(element); + const resource = this.mapper.mapRichTextElementToResource(element); - organizationBuilder.addResource(resource); + cardOrganization.addResource(resource); } if (isLinkElement(element)) { - const resource = this.commonCartridgeMapper.mapLinkElementToResource(element); + const resource = this.mapper.mapLinkElementToResource(element); - organizationBuilder.addResource(resource); + cardOrganization.addResource(resource); } } private addComponentToOrganization( - organizationBuilder: CommonCartridgeOrganizationBuilder, - component: ComponentProperties + component: ComponentProperties, + lessonOrganization: CommonCartridgeOrganizationNode ): void { - const resources = this.commonCartridgeMapper.mapContentToResources(component); + const resources = this.mapper.mapContentToResources(component); if (Array.isArray(resources)) { - const subOrganizationBuilder = organizationBuilder.addSubOrganization( - this.commonCartridgeMapper.mapContentToOrganization(component) - ); + const componentOrganization = lessonOrganization.createChild(this.mapper.mapContentToOrganization(component)); resources.forEach((resource) => { - subOrganizationBuilder.addResource(resource); + componentOrganization.addResource(resource); }); } else { - organizationBuilder.addResource(resources); + lessonOrganization.addResource(resources); } } } diff --git a/apps/server/src/modules/learnroom/service/common-cartridge-import.service.ts b/apps/server/src/modules/learnroom/service/common-cartridge-import.service.ts index 1cded6df376..af55a6eb2a9 100644 --- a/apps/server/src/modules/learnroom/service/common-cartridge-import.service.ts +++ b/apps/server/src/modules/learnroom/service/common-cartridge-import.service.ts @@ -2,19 +2,19 @@ import { BoardExternalReferenceType, BoardLayout, BoardNodeFactory, + BoardNodeService, Card, - ContentElementType, Column, ColumnBoard, -} from '@modules/board/domain'; -import { BoardNodeService } from '@modules/board/service'; -import { Injectable } from '@nestjs/common'; -import { Course, User } from '@shared/domain/entity'; + ContentElementType, +} from '@modules/board'; import { CommonCartridgeFileParser, - CommonCartridgeOrganizationProps, + CommonCartridgeImportOrganizationProps, DEFAULT_FILE_PARSER_OPTIONS, -} from '@src/modules/common-cartridge'; +} from '@modules/common-cartridge'; +import { Injectable } from '@nestjs/common'; +import { Course, User } from '@shared/domain/entity'; import { CommonCartridgeImportMapper } from '../mapper/common-cartridge-import.mapper'; import { CourseService } from './course.service'; @@ -47,8 +47,8 @@ export class CommonCartridgeImportService { private async createColumnBoard( parser: CommonCartridgeFileParser, course: Course, - boardProps: CommonCartridgeOrganizationProps, - organizations: CommonCartridgeOrganizationProps[] + boardProps: CommonCartridgeImportOrganizationProps, + organizations: CommonCartridgeImportOrganizationProps[] ): Promise { const columnBoard = this.boardNodeFactory.buildColumnBoard({ context: { @@ -66,8 +66,8 @@ export class CommonCartridgeImportService { private async createColumns( parser: CommonCartridgeFileParser, columnBoard: ColumnBoard, - boardProps: CommonCartridgeOrganizationProps, - organizations: CommonCartridgeOrganizationProps[] + boardProps: CommonCartridgeImportOrganizationProps, + organizations: CommonCartridgeImportOrganizationProps[] ): Promise { const columnsWithResource = organizations.filter( (organization) => @@ -91,7 +91,7 @@ export class CommonCartridgeImportService { private async createColumnWithResource( parser: CommonCartridgeFileParser, columnBoard: ColumnBoard, - columnProps: CommonCartridgeOrganizationProps + columnProps: CommonCartridgeImportOrganizationProps ): Promise { const column = this.boardNodeFactory.buildColumn(); const { title } = this.mapper.mapOrganizationToColumn(columnProps); @@ -103,8 +103,8 @@ export class CommonCartridgeImportService { private async createColumn( parser: CommonCartridgeFileParser, columnBoard: ColumnBoard, - columnProps: CommonCartridgeOrganizationProps, - organizations: CommonCartridgeOrganizationProps[] + columnProps: CommonCartridgeImportOrganizationProps, + organizations: CommonCartridgeImportOrganizationProps[] ): Promise { const column = this.boardNodeFactory.buildColumn(); const { title } = this.mapper.mapOrganizationToColumn(columnProps); @@ -131,7 +131,7 @@ export class CommonCartridgeImportService { private async createCardWithElement( parser: CommonCartridgeFileParser, column: Column, - cardProps: CommonCartridgeOrganizationProps, + cardProps: CommonCartridgeImportOrganizationProps, withTitle = true ): Promise { const card = this.boardNodeFactory.buildCard(); @@ -154,8 +154,8 @@ export class CommonCartridgeImportService { private async createCard( parser: CommonCartridgeFileParser, column: Column, - cardProps: CommonCartridgeOrganizationProps, - organizations: CommonCartridgeOrganizationProps[] + cardProps: CommonCartridgeImportOrganizationProps, + organizations: CommonCartridgeImportOrganizationProps[] ) { const card = this.boardNodeFactory.buildCard(); const { title, height } = this.mapper.mapOrganizationToCard(cardProps, true); @@ -175,7 +175,7 @@ export class CommonCartridgeImportService { private async createCardElement( parser: CommonCartridgeFileParser, card: Card, - cardElementProps: CommonCartridgeOrganizationProps + cardElementProps: CommonCartridgeImportOrganizationProps ) { if (cardElementProps.isResource) { const resource = parser.getResource(cardElementProps); diff --git a/apps/server/src/modules/learnroom/service/course-copy.service.spec.ts b/apps/server/src/modules/learnroom/service/course-copy.service.spec.ts index cbd8bee44d6..91f85457879 100644 --- a/apps/server/src/modules/learnroom/service/course-copy.service.spec.ts +++ b/apps/server/src/modules/learnroom/service/course-copy.service.spec.ts @@ -4,7 +4,7 @@ import { LessonCopyService } from '@modules/lesson/service'; import { ToolContextType } from '@modules/tool/common/enum'; import { ContextExternalTool } from '@modules/tool/context-external-tool/domain'; import { ContextExternalToolService } from '@modules/tool/context-external-tool/service'; -import { ToolFeatures } from '@modules/tool/tool-config'; +import { IToolFeatures, ToolFeatures } from '@modules/tool/tool-config'; import { Test, TestingModule } from '@nestjs/testing'; import { Course } from '@shared/domain/entity'; import { CourseRepo, LegacyBoardRepo, UserRepo } from '@shared/repo'; @@ -16,7 +16,6 @@ import { setupEntities, userFactory, } from '@shared/testing'; -import { IToolFeatures } from '@src/modules/tool/tool-config'; import { contextExternalToolFactory } from '../../tool/context-external-tool/testing'; import { BoardCopyService } from './board-copy.service'; import { CourseCopyService } from './course-copy.service'; diff --git a/apps/server/src/modules/learnroom/uc/course-import.uc.spec.ts b/apps/server/src/modules/learnroom/uc/course-import.uc.spec.ts index eedae71a09f..fe50b3f5926 100644 --- a/apps/server/src/modules/learnroom/uc/course-import.uc.spec.ts +++ b/apps/server/src/modules/learnroom/uc/course-import.uc.spec.ts @@ -1,12 +1,12 @@ import { faker } from '@faker-js/faker'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { MikroORM } from '@mikro-orm/core'; +import { AuthorizationService } from '@modules/authorization'; import { NotFoundException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain/interface'; import { courseFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationService } from '@src/modules/authorization'; import { LearnroomConfig } from '../learnroom.config'; import { CommonCartridgeImportService } from '../service'; import { CourseImportUc } from './course-import.uc'; diff --git a/apps/server/src/modules/learnroom/uc/course-import.uc.ts b/apps/server/src/modules/learnroom/uc/course-import.uc.ts index 4eccabe8132..33b0e9a3a63 100644 --- a/apps/server/src/modules/learnroom/uc/course-import.uc.ts +++ b/apps/server/src/modules/learnroom/uc/course-import.uc.ts @@ -1,8 +1,8 @@ +import { AuthorizationService } from '@modules/authorization'; import { Injectable, NotFoundException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { Permission } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; -import { AuthorizationService } from '@src/modules/authorization'; import { LearnroomConfig } from '../learnroom.config'; import { CommonCartridgeImportService } from '../service'; diff --git a/apps/server/src/modules/learnroom/uc/course.uc.spec.ts b/apps/server/src/modules/learnroom/uc/course.uc.spec.ts index 29b0a5be59b..3c2a082fd4a 100644 --- a/apps/server/src/modules/learnroom/uc/course.uc.spec.ts +++ b/apps/server/src/modules/learnroom/uc/course.uc.spec.ts @@ -1,12 +1,12 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { AuthorizationService } from '@modules/authorization'; +import { RoleDto, RoleService } from '@modules/role'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission, RoleName, SortOrder } from '@shared/domain/interface'; import { CourseRepo } from '@shared/repo'; import { courseFactory, setupEntities, UserAndAccountTestFactory } from '@shared/testing'; -import { AuthorizationService } from '@src/modules/authorization'; -import { RoleDto, RoleService } from '@src/modules/role'; -import { CourseUc } from './course.uc'; import { CourseService } from '../service'; +import { CourseUc } from './course.uc'; describe('CourseUc', () => { let module: TestingModule; diff --git a/apps/server/src/modules/learnroom/uc/course.uc.ts b/apps/server/src/modules/learnroom/uc/course.uc.ts index a60676803d9..ed7abf98a1d 100644 --- a/apps/server/src/modules/learnroom/uc/course.uc.ts +++ b/apps/server/src/modules/learnroom/uc/course.uc.ts @@ -1,11 +1,11 @@ +import { AuthorizationService } from '@modules/authorization'; +import { RoleService } from '@modules/role'; import { Injectable } from '@nestjs/common'; import { PaginationParams } from '@shared/controller/'; import { Course } from '@shared/domain/entity'; import { SortOrder } from '@shared/domain/interface'; import { Counted, EntityId } from '@shared/domain/types'; import { CourseRepo } from '@shared/repo'; -import { AuthorizationService } from '@src/modules/authorization'; -import { RoleService } from '@src/modules/role'; import { RoleNameMapper } from '../mapper/rolename.mapper'; import { CourseService } from '../service';