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-770 Common Cartridge import of web content into column board 1.1.0 #4945

Merged
merged 51 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
537b0f7
add resource get
alweber-cap Apr 3, 2024
6f3b9d9
EW-771 parsing web link from xml resource file
psachmann Apr 8, 2024
1f484a1
EW-771 working on parsing and mapping
psachmann Apr 8, 2024
635ab7c
EW-771 importing links from cc that are not inlined
psachmann Apr 9, 2024
892fc07
EW-771 adding cc import utils
psachmann Apr 10, 2024
2054ce5
EW-771 working on code structure
psachmann Apr 10, 2024
9a2a6dc
EW-771 removing unnecessary changes
psachmann Apr 11, 2024
5d9e153
EW-771 working on tests and coverage
psachmann Apr 11, 2024
f32e408
EW-771 renaming import types
psachmann Apr 11, 2024
eb54de1
EW-771 renaming of types
psachmann Apr 11, 2024
2292e8c
EW-771 adding tests for file parser
psachmann Apr 11, 2024
5e8303a
EW-771 working on tests and coverage
psachmann Apr 11, 2024
bd0aacb
Merge branch 'main' into EW-771
psachmann Apr 11, 2024
3850f3a
EW-771 working on tests and coverage
psachmann Apr 11, 2024
8c87be7
EW-771 adding tests for cc import service
psachmann Apr 11, 2024
d79a08a
EW-771 adding tests
psachmann Apr 11, 2024
a61ad7b
Merge branch 'main' into EW-771
psachmann Apr 12, 2024
6a81394
EW-771 adding tests
psachmann Apr 12, 2024
5410529
EW-771 improving test coverage
psachmann Apr 12, 2024
6ddeb5a
EW-771 removing unused enum
psachmann Apr 12, 2024
de199f5
Merge branch 'main' into EW-771
psachmann Apr 15, 2024
5cacd7f
Merge branch 'main' into EW-771
psachmann Apr 15, 2024
2c7fcc1
Merge branch 'main' into EW-771
psachmann Apr 16, 2024
0c88956
EW-771 optimizing resource parsing
psachmann Apr 16, 2024
9b02916
EW-770 expanded spaltenboard export to web content
Fshmit Apr 16, 2024
950810e
EW-771 removing adm zip and jsdom from cc file pipe
psachmann Apr 17, 2024
4e53dda
EW-771 fixing unit test
psachmann Apr 17, 2024
cc48dfe
Merge branch 'main' into EW-771
psachmann Apr 17, 2024
5991fd0
Merge branch 'EW-771' into EW-770
Fshmit Apr 17, 2024
d7bccd4
EW-770 changed query selector
Fshmit Apr 17, 2024
f4e25ca
EW-770 trying a new selector to read body
Fshmit Apr 17, 2024
9a08126
EW-770 expanded a test of import mapper
Fshmit Apr 18, 2024
07e9be5
Merge branch 'main' into EW-770
Fshmit Apr 18, 2024
f1fb566
EW-770 expanded test of resource factory of cc
Fshmit Apr 18, 2024
d9a945d
EW-770 increased test coverage of resource factory
Fshmit Apr 18, 2024
5f4c1a1
EW-770 merge main branch
Fshmit Apr 19, 2024
53a1658
Merge branch 'main' into EW-770
Fshmit Apr 19, 2024
dca0202
Merge branch 'main' into EW-770
Fshmit Apr 19, 2024
25c3d21
Merge branch 'main' into EW-770
Fshmit Apr 19, 2024
c32c2d1
EW-770 code review
Fshmit Apr 22, 2024
32a0700
Merge branch 'EW-770' of https://github.com/hpi-schul-cloud/schulclou…
Fshmit Apr 22, 2024
6d83325
Merge branch 'main' into EW-770
Fshmit Apr 22, 2024
13170e8
EW-770 deleted undefined case
Fshmit Apr 22, 2024
c9746e4
Merge branch 'EW-770' of https://github.com/hpi-schul-cloud/schulclou…
Fshmit Apr 22, 2024
a88ae34
Merge branch 'main' into EW-770
Fshmit Apr 23, 2024
8be9c56
EW-770 modified resource factory
Fshmit Apr 23, 2024
305e318
EW-770 modified test sturcture
Fshmit Apr 24, 2024
83f5055
Merge branch 'main' into EW-770
Fshmit Apr 24, 2024
67ee6e2
Merge branch 'main' into EW-770
psachmann Apr 24, 2024
299cc36
Merge branch 'main' into EW-770
Fshmit Apr 25, 2024
ea306bf
EW-770 fixed a test of resource factory
Fshmit Apr 25, 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
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ import { CommonCartridgeResourceFactory } from './common-cartridge-resource-fact
describe('CommonCartridgeResourceFactory', () => {
let sut: CommonCartridgeResourceFactory;
let admZipMock: DeepMocked<AdmZip>;
let webLinkXml: string | undefined;

const setupWebLinkXml = async () => {
let webLinkXml: string | undefined;

// caching the web link xml to avoid reading the file multiple times from disk
if (!webLinkXml) {
webLinkXml = await readFile(
Expand All @@ -25,6 +24,18 @@ describe('CommonCartridgeResourceFactory', () => {

return webLinkXml;
};
const setupWebContentHtml = () => {
const webContentHtml = `<html>
<head>
<title>Title</title>
</head>
<body>
<p>Content</p>
</body>
</html>`;

return webContentHtml;
};
const setupOrganizationProps = () => {
const organizationProps: CommonCartridgeOrganizationProps = {
identifier: faker.string.uuid(),
Expand All @@ -46,14 +57,18 @@ describe('CommonCartridgeResourceFactory', () => {
sut = new CommonCartridgeResourceFactory(admZipMock);
});

beforeEach(() => {
jest.clearAllMocks();
});

it('should be defined', () => {
expect(sut).toBeDefined();
});

describe('create', () => {
describe('when creating a web link resource', () => {
const setup = async () => {
const webLinkXml = await setupWebLinkXml();
webLinkXml = await setupWebLinkXml();
const organizationProps = setupOrganizationProps();

organizationProps.resourceType = CommonCartridgeResourceTypeV1P1.WEB_LINK;
Expand All @@ -70,7 +85,7 @@ describe('CommonCartridgeResourceFactory', () => {

expect(result).toStrictEqual<CommonCartridgeWebLinkResourceProps>({
type: CommonCartridgeResourceTypeV1P1.WEB_LINK,
title: 'Title',
title: organizationProps.title,
url: 'http://www.example.tld',
});
});
Expand Down Expand Up @@ -132,5 +147,71 @@ describe('CommonCartridgeResourceFactory', () => {
expect(result).toBeUndefined();
});
});

describe('when web content is provided', () => {
const setup = () => {
const webContentHtml = setupWebContentHtml();
const organizationProps = setupOrganizationProps();

organizationProps.resourceType = CommonCartridgeResourceTypeV1P1.WEB_CONTENT;
admZipMock.getEntry.mockReturnValue({} as AdmZip.IZipEntry);
admZipMock.readAsText.mockReturnValue(webContentHtml);

return { organizationProps };
};

it('should create a web content resource', () => {
const { organizationProps } = setup();

const result = sut.create(organizationProps);

expect(result).toStrictEqual({
type: CommonCartridgeResourceTypeV1P1.WEB_CONTENT,
title: organizationProps.title,
html: 'Content',
});
});
});

describe('when web content is not provided', () => {
const setup = () => {
const organizationProps = setupOrganizationProps();

organizationProps.resourceType = CommonCartridgeResourceTypeV1P1.WEB_CONTENT;
admZipMock.getEntry.mockReturnValue({} as AdmZip.IZipEntry);
admZipMock.readAsText.mockReturnValue('');

return { organizationProps };
};
it('should return an empty value', () => {
const { organizationProps } = setup();

const result = sut.create(organizationProps);

expect(result).toStrictEqual({
type: CommonCartridgeResourceTypeV1P1.WEB_CONTENT,
title: organizationProps.title,
html: '',
});
});
});

describe('when html is not valid', () => {
const setup = () => {
const organizationProps = setupOrganizationProps();
organizationProps.resourceType = CommonCartridgeResourceTypeV1P1.WEB_CONTENT;
const tryCreateDocumentMock = jest.fn().mockReturnValue(undefined);
Reflect.set(sut, 'tryCreateDocument', tryCreateDocumentMock);

return { organizationProps };
};
it('should return undefined', () => {
const { organizationProps } = setup();

psachmann marked this conversation as resolved.
Show resolved Hide resolved
const result = sut.create(organizationProps);

expect(result).toBeUndefined();
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { CommonCartridgeResourceTypeV1P1 } from './common-cartridge-import.enums
import {
CommonCartridgeOrganizationProps,
CommonCartridgeResourceProps,
CommonCartridgeWebContentResourceProps,
CommonCartridgeWebLinkResourceProps,
} from './common-cartridge-import.types';

Expand All @@ -16,10 +17,13 @@ export class CommonCartridgeResourceFactory {
}

const content = this.archive.readAsText(organization.resourcePath);
const { title } = organization;

switch (organization.resourceType) {
case CommonCartridgeResourceTypeV1P1.WEB_LINK:
return this.createWebLinkResource(content);
return this.createWebLinkResource(content, title);
case CommonCartridgeResourceTypeV1P1.WEB_CONTENT:
return this.createWebContentResource(content, title);
default:
return undefined;
}
Expand All @@ -32,15 +36,14 @@ export class CommonCartridgeResourceFactory {
return isValidOrganization;
}

private createWebLinkResource(content: string): CommonCartridgeWebLinkResourceProps | undefined {
const document = this.tryCreateDocument(content);
private createWebLinkResource(content: string, title: string): CommonCartridgeWebLinkResourceProps | undefined {
const document = this.tryCreateDocument(content, 'text/xml');

if (!document) {
return undefined;
}

const title = document.querySelector('webLink > title')?.textContent || '';
const url = document.querySelector('webLink > url')?.getAttribute('href') || '';
const url = document.querySelector('webLink > url')?.getAttribute('href') ?? '';

return {
type: CommonCartridgeResourceTypeV1P1.WEB_LINK,
Expand All @@ -49,9 +52,25 @@ export class CommonCartridgeResourceFactory {
};
}

private tryCreateDocument(content: string): Document | undefined {
private createWebContentResource(content: string, title: string): CommonCartridgeWebContentResourceProps | undefined {
const document = this.tryCreateDocument(content, 'text/html');

if (!document) {
return undefined;
}

const html = document.body.textContent?.trim() ?? '';

return {
type: CommonCartridgeResourceTypeV1P1.WEB_CONTENT,
title,
html,
};
}

private tryCreateDocument(content: string, contentType: 'text/html' | 'text/xml'): Document | undefined {
try {
const parser = new JSDOM(content, { contentType: 'text/xml' }).window.document;
const parser = new JSDOM(content, { contentType }).window.document;

return parser;
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { faker } from '@faker-js/faker';
import { Test, TestingModule } from '@nestjs/testing';
import { CardInitProps, ColumnInitProps, ContentElementType } from '@shared/domain/domainobject';
import { LinkContentBody } from '@src/modules/board/controller/dto';
import { LinkContentBody, RichTextContentBody } from '@src/modules/board/controller/dto';
import {
CommonCartridgeImportResourceProps,
CommonCartridgeOrganizationProps,
CommonCartridgeResourceTypeV1P1,
} from '@src/modules/common-cartridge';
import { InputFormat } from '@shared/domain/types';
import { CommonCartridgeImportMapper } from './common-cartridge-import.mapper';

describe('CommonCartridgeImportMapper', () => {
Expand Down Expand Up @@ -92,6 +93,12 @@ describe('CommonCartridgeImportMapper', () => {

expect(result).toEqual(ContentElementType.LINK);
});

it('should return rich text', () => {
const result = sut.mapResourceTypeToContentElementType(CommonCartridgeResourceTypeV1P1.WEB_CONTENT);

expect(result).toEqual(ContentElementType.RICH_TEXT);
});
});
});

Expand All @@ -112,6 +119,22 @@ describe('CommonCartridgeImportMapper', () => {
url: resource.url,
});
});

it('should return rich text content element body', () => {
const resource: CommonCartridgeImportResourceProps = {
type: CommonCartridgeResourceTypeV1P1.WEB_CONTENT,
title: faker.lorem.words(3),
html: faker.lorem.paragraph(),
};

const result = sut.mapResourceToContentElementBody(resource);

expect(result).toBeInstanceOf(RichTextContentBody);
expect(result).toEqual<RichTextContentBody>({
text: resource.html,
inputFormat: InputFormat.RICH_TEXT_CK5_SIMPLE,
});
});
});

describe('when unknown resource type is provided', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Injectable } from '@nestjs/common';
import { CardInitProps, ColumnInitProps, ContentElementType } from '@shared/domain/domainobject';
import { AnyElementContentBody, LinkContentBody } from '@src/modules/board/controller/dto';
import { InputFormat } from '@shared/domain/types';
import { AnyElementContentBody, LinkContentBody, RichTextContentBody } from '@src/modules/board/controller/dto';
import { CommonCartridgeOrganizationProps, CommonCartridgeResourceTypeV1P1 } from '@src/modules/common-cartridge';
import {
CommonCartridgeResourceProps,
CommonCartridgeWebContentResourceProps,
CommonCartridgeWebLinkResourceProps,
} from '@src/modules/common-cartridge/import/common-cartridge-import.types';

Expand All @@ -28,6 +30,8 @@ export class CommonCartridgeImportMapper {
switch (resourceType) {
case CommonCartridgeResourceTypeV1P1.WEB_LINK:
return ContentElementType.LINK;
case CommonCartridgeResourceTypeV1P1.WEB_CONTENT:
return ContentElementType.RICH_TEXT;
default:
return undefined;
}
Expand All @@ -37,6 +41,8 @@ export class CommonCartridgeImportMapper {
switch (resource.type) {
case CommonCartridgeResourceTypeV1P1.WEB_LINK:
return this.createLinkContentElementBody(resource);
case CommonCartridgeResourceTypeV1P1.WEB_CONTENT:
return this.createWebContentElementBody(resource);
default:
throw new Error('Resource type not supported');
}
Expand All @@ -50,4 +56,13 @@ export class CommonCartridgeImportMapper {

return body;
}

private createWebContentElementBody(resource: CommonCartridgeWebContentResourceProps): AnyElementContentBody {
const body = new RichTextContentBody();

body.text = resource.html;
body.inputFormat = InputFormat.RICH_TEXT_CK5_SIMPLE;

return body;
}
}
Loading