Skip to content

Commit

Permalink
EW-770 Common Cartridge import of web content into column board 1.1.0 (
Browse files Browse the repository at this point in the history
…#4945)

* EW-770 expanded common cartridge import to include web content into column board 1.1.0
  • Loading branch information
Fshmit authored and bergatco committed May 6, 2024
1 parent da554f7 commit c21d2dc
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 13 deletions.
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();

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;
}
}

0 comments on commit c21d2dc

Please sign in to comment.