Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EW-793 - Refactor Common Cartridge Export #4918

Merged
merged 72 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
b9b1525
EW-793 Add loggable exceptions
SimoneRadtke-Cap Apr 11, 2024
711cd09
EW-793 Rename folder and adjust regarding imports
SimoneRadtke-Cap Apr 12, 2024
a56b918
EW-793 Add common cartridge guard for intended use
SimoneRadtke-Cap Apr 12, 2024
c9744f4
Merge branch 'main' into EW-793
SimoneRadtke-Cap Apr 12, 2024
6ca75be
EW-793 Work in progress
SimoneRadtke-Cap Apr 16, 2024
2ee5a63
EW-793 Working in progress
SimoneRadtke-Cap Apr 16, 2024
9a6d51a
EW-793 work in progress
psachmann Apr 17, 2024
2315fc1
EW-793 restructuring export mechanism
psachmann Apr 18, 2024
cb75216
EW-793 removing unused code
psachmann Apr 18, 2024
04c95c3
Merge branch 'main' into EW-793
psachmann Apr 18, 2024
1765505
EW-793 removing compilation errors
psachmann Apr 18, 2024
7c47e0d
EW-793 Renaming functions, classes and variables
SimoneRadtke-Cap Apr 19, 2024
34b058a
EW-793 Renaming
SimoneRadtke-Cap Apr 19, 2024
be48244
EW-793 Add loggable exception for missing metadata and rename folder
SimoneRadtke-Cap Apr 22, 2024
d4458ec
EW-793 Adjust mapper test and index file
SimoneRadtke-Cap Apr 22, 2024
b341f01
Merge branch 'main' into EW-793
SimoneRadtke-Cap Apr 22, 2024
c4c4c5f
Merge branch 'main' into EW-793
SimoneRadtke-Cap Apr 22, 2024
b8dfa78
Merge branch 'main' into EW-793
SimoneRadtke-Cap Apr 23, 2024
63c3dfb
EW-793 Add tests for new builders and expand test factories
SimoneRadtke-Cap Apr 26, 2024
8d26db6
Merge branch 'main' into EW-793
SimoneRadtke-Cap Apr 26, 2024
5580ea5
EW-793 Adjust import
SimoneRadtke-Cap Apr 26, 2024
d915785
EW-793 Adjust import for CommonCartridgeImportOrganizationProps
SimoneRadtke-Cap Apr 26, 2024
7fc8fab
EW-793 Add test for new loggable exception
SimoneRadtke-Cap Apr 26, 2024
9b4d7a6
EW-793 Add xml object interface
SimoneRadtke-Cap Apr 26, 2024
e5563f2
EW-793 Add public keyword to loggable methods
SimoneRadtke-Cap Apr 26, 2024
9ba35fa
EW-793 Add new guard to org element
SimoneRadtke-Cap Apr 26, 2024
8827adb
EW-793 Work in progress
SimoneRadtke-Cap Apr 26, 2024
853e557
Merge branch 'main' into EW-793
SimoneRadtke-Cap Apr 26, 2024
b4e7cdd
EW.793 Work in progress
SimoneRadtke-Cap Apr 29, 2024
54cf470
Merge branch 'main' into EW-793
SimoneRadtke-Cap May 6, 2024
db411ea
EW-793 introducing some interfaces
psachmann May 14, 2024
910754b
EW-793 working on
psachmann May 14, 2024
87c3ae7
EW-793 changing interfaces and make them more consistent
psachmann May 15, 2024
9f5b932
EW-793 updating tests
psachmann May 16, 2024
f401c54
Merge branch 'main' into EW-793
psachmann May 16, 2024
19dfea3
EW-793 working on tests
psachmann May 16, 2024
73f179e
EW-793 adding unit tests
psachmann May 17, 2024
a193bb2
EW-793 fixing linter warnings
psachmann May 17, 2024
f8db7b7
Merge branch 'main' into EW-793
SimoneRadtke-Cap May 21, 2024
f583c4c
Merge branch 'main' into EW-793
SimoneRadtke-Cap May 24, 2024
ba0d2b0
EW-793 Try to reduce code duplications
SimoneRadtke-Cap May 24, 2024
1ff4491
EW-793 Revert try
SimoneRadtke-Cap May 24, 2024
eedfeec
EW-793 Extend test factories to reduce code duplications
SimoneRadtke-Cap May 27, 2024
4f03d4b
Merge branch 'main' into EW-793
SimoneRadtke-Cap May 29, 2024
23bbc0a
EW-793 Delete node-props-factory and move method to another factory
SimoneRadtke-Cap May 29, 2024
cd83ed2
fix code duplication
alweber-cap May 31, 2024
a98e6ba
Merge branch 'main' into EW-793
alweber-cap May 31, 2024
7190c3d
EW-793 Add abstract class for resources wrapper and revert changes in…
SimoneRadtke-Cap Jun 3, 2024
c3e4826
EW-793 Add comments to abstract classes
SimoneRadtke-Cap Jun 3, 2024
38b0558
Merge branch 'main' into EW-793
SimoneRadtke-Cap Jun 3, 2024
9829632
Merge branch 'main' into EW-793
SimoneRadtke-Cap Jun 5, 2024
02bffc4
EW-793 Change three dots in card title
SimoneRadtke-Cap Jun 5, 2024
985a88f
EW-793 Prevent script injection
SimoneRadtke-Cap Jun 6, 2024
ced2d24
Merge branch 'main' into EW-793
SimoneRadtke-Cap Jun 6, 2024
f26acae
EW-793 Trial to prevent script injection
SimoneRadtke-Cap Jun 6, 2024
d20f8d2
Merge branch 'main' into EW-793
SimoneRadtke-Cap Jun 6, 2024
9f78a7b
EW-793 Adjust test for cc export mapper
SimoneRadtke-Cap Jun 7, 2024
b7cd26f
EW-793 Use sanitize-html library
SimoneRadtke-Cap Jun 7, 2024
da5d7b6
EW-793 Work in progress
SimoneRadtke-Cap Jun 7, 2024
4b67afa
Merge branch 'main' into EW-793
SimoneRadtke-Cap Jun 7, 2024
c66d724
EW-793 Delete comments
SimoneRadtke-Cap Jun 7, 2024
0a5a5e4
EW-793 Adjust cc-import-service after merge conflict
SimoneRadtke-Cap Jun 7, 2024
3595026
Merge branch 'main' into EW-793
SimoneRadtke-Cap Jun 11, 2024
bf4101c
Merge branch 'main' into EW-793
SimoneRadtke-Cap Jun 12, 2024
377db55
Merge branch 'main' into EW-793
SimoneRadtke-Cap Jun 12, 2024
d89c010
Merge branch 'main' into EW-793
SimoneRadtke-Cap Jun 12, 2024
c1fd447
Merge branch 'main' into EW-793
SimoneRadtke-Cap Jun 13, 2024
f3cb026
Merge branch 'main' into EW-793
SimoneRadtke-Cap Jun 14, 2024
13f5013
EW-793 changing import path
psachmann Jun 21, 2024
525ccee
Merge branch 'main' into EW-793
psachmann Jun 21, 2024
cdba08c
EW-793 updating imports
psachmann Jun 21, 2024
ef8bdcc
Merge branch 'main' into EW-793
psachmann Jun 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 19 additions & 10 deletions apps/server/src/modules/board/index.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
});
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -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<CommonCartridgeOrganizationNodeProps, 'version' | 'type'>;

private readonly organizationBuilders = new Array<CommonCartridgeOrganizationBuilder>();
export class CommonCartridgeFileBuilder {
private readonly resourcesBuilder: CommonCartridgeResourceCollectionBuilder =
new CommonCartridgeResourceCollectionBuilder();

private readonly resources = new Array<CommonCartridgeResource>();
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<CommonCartridgeOrganizationBuilderOptions>
): 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<Buffer> {
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();
}
}
Loading
Loading