feat: add template based data generator

This commit is contained in:
Joachim Van Herwegen
2020-11-27 13:44:41 +01:00
parent 9653deec7f
commit f387b36dc2
14 changed files with 285 additions and 6 deletions

View File

@@ -0,0 +1,11 @@
import { HandlebarsTemplateEngine } from '../../../../src/pods/generate/HandlebarsTemplateEngine';
describe('A HandlebarsTemplateEngine', (): void => {
const engine = new HandlebarsTemplateEngine();
it('fills in Handlebars templates.', async(): Promise<void> => {
const template = '<{{webId}}> a <http://xmlns.com/foaf/0.1/Person>.';
const options = { webId: 'http://alice/#profile' };
expect(engine.apply(template, options)).toBe('<http://alice/#profile> a <http://xmlns.com/foaf/0.1/Person>.');
});
});

View File

@@ -0,0 +1,90 @@
import type { ResourceIdentifier } from '../../../../src/ldp/representation/ResourceIdentifier';
import { TemplatedResourcesGenerator } from '../../../../src/pods/generate/TemplatedResourcesGenerator';
import type { TemplateEngine } from '../../../../src/pods/generate/TemplateEngine';
import type {
FileIdentifierMapper,
FileIdentifierMapperFactory,
ResourceLink,
} from '../../../../src/storage/mapping/FileIdentifierMapper';
import { ensureTrailingSlash, trimTrailingSlashes } from '../../../../src/util/PathUtil';
import { readableToString } from '../../../../src/util/StreamUtil';
import { mockFs } from '../../../util/Util';
import Dict = NodeJS.Dict;
jest.mock('fs');
class DummyFactory implements FileIdentifierMapperFactory {
public async create(base: string, rootFilePath: string): Promise<FileIdentifierMapper> {
const trimBase = trimTrailingSlashes(base);
const trimRoot = trimTrailingSlashes(rootFilePath);
return {
async mapFilePathToUrl(filePath: string, isContainer: boolean): Promise<ResourceLink> {
const path = `${trimBase}${filePath.slice(trimRoot.length)}`;
return {
identifier: { path: isContainer ? ensureTrailingSlash(path) : path },
filePath,
contentType: isContainer ? undefined : 'text/turtle',
};
},
} as any;
}
}
class DummyEngine implements TemplateEngine {
public apply(template: string, options: Dict<string>): string {
const keys = Object.keys(options);
return `${template}${keys.map((key): string => `{${key}:${options[key]}}`).join('')}`;
}
}
const genToArray = async<T>(iterable: AsyncIterable<T>): Promise<T[]> => {
const arr: T[] = [];
for await (const result of iterable) {
arr.push(result);
}
return arr;
};
describe('A TemplatedResourcesGenerator', (): void => {
const rootFilePath = 'templates';
const generator = new TemplatedResourcesGenerator(rootFilePath, new DummyFactory(), new DummyEngine());
let cache: { data: any };
const template = '<{{webId}}> a <http://xmlns.com/foaf/0.1/Person>.';
const location = { path: 'http://test.com/alice/' };
const webId = 'http://alice/#profile';
beforeEach(async(): Promise<void> => {
cache = mockFs(rootFilePath);
});
it('fills in a template with the given options.', async(): Promise<void> => {
cache.data = { template };
const result = await genToArray(generator.generate(location, { webId }));
const identifiers = result.map((res): ResourceIdentifier => res.identifier);
const id = { path: `${location.path}template` };
expect(identifiers).toEqual([ location, id ]);
const { representation } = result[1];
expect(representation.binary).toBe(true);
expect(representation.metadata.contentType).toBe('text/turtle');
await expect(readableToString(representation.data)).resolves
.toEqual(`<{{webId}}> a <http://xmlns.com/foaf/0.1/Person>.{webId:${webId}}`);
});
it('creates the necessary containers and ignores non-files.', async(): Promise<void> => {
cache.data = { container: { container: { template }}, 2: 5 };
const result = await genToArray(generator.generate(location, { webId }));
const identifiers = result.map((res): ResourceIdentifier => res.identifier);
const id = { path: `${location.path}container/container/template` };
expect(identifiers).toEqual([
location,
{ path: `${location.path}container/` },
{ path: `${location.path}container/container/` },
id,
]);
const { representation } = result[3];
await expect(readableToString(representation.data)).resolves
.toEqual(`<{{webId}}> a <http://xmlns.com/foaf/0.1/Person>.{webId:${webId}}`);
});
});

View File

@@ -1,5 +1,8 @@
import fs from 'fs';
import { ExtensionBasedMapper } from '../../../../src/storage/mapping/ExtensionBasedMapper';
import {
ExtensionBasedMapper,
ExtensionBasedMapperFactory,
} from '../../../../src/storage/mapping/ExtensionBasedMapper';
import { BadRequestHttpError } from '../../../../src/util/errors/BadRequestHttpError';
import { NotFoundHttpError } from '../../../../src/util/errors/NotFoundHttpError';
import { trimTrailingSlashes } from '../../../../src/util/PathUtil';
@@ -135,4 +138,12 @@ describe('An ExtensionBasedMapper', (): void => {
});
});
});
describe('An ExtensionBasedMapperFactory', (): void => {
const factory = new ExtensionBasedMapperFactory();
it('creates an ExtensionBasedMapper.', async(): Promise<void> => {
await expect(factory.create('base', 'filePath')).resolves.toBeInstanceOf(ExtensionBasedMapper);
});
});
});

View File

@@ -160,6 +160,10 @@ export const mockFs = (rootFilepath?: string, time?: Date): { data: any } => {
}
folder[name] = {};
},
readFile(path: string): string {
const { folder, name } = getFolder(path);
return folder[name];
},
},
};