feat: Added cloneRepresentation function to ResourceUtil

* feat: added cloneRepresentation function to ResourceUtil

* fix: adapted to review

* fix: adapted to review

Co-authored-by: Arne Vandoorslaer <arne@digita.ai>
This commit is contained in:
Arthur Joppart 2021-03-04 08:43:53 +01:00 committed by GitHub
parent 4385b461b5
commit ee88bf14de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 65 additions and 17 deletions

View File

@ -1,8 +1,7 @@
import arrayifyStream from 'arrayify-stream';
import type { RepresentationConverter } from '../../storage/conversion/RepresentationConverter';
import { INTERNAL_QUADS } from '../../util/ContentTypes';
import { guardedStreamFrom } from '../../util/StreamUtil';
import { BasicRepresentation } from '../representation/BasicRepresentation';
import { cloneRepresentation } from '../../util/ResourceUtil';
import type { Representation } from '../representation/Representation';
import { Validator } from './Validator';
@ -23,23 +22,22 @@ export class RdfValidator extends Validator {
if (representation.metadata.contentType === INTERNAL_QUADS) {
return;
}
// eslint-disable-next-line unicorn/expiring-todo-comments
// TODO: Everything below should be part of a utility cloneRepresentation function.
const identifier = { path: representation.metadata.identifier.value };
// Read data in memory first so it does not get lost
const data = await arrayifyStream(representation.data);
const preferences = { type: { [INTERNAL_QUADS]: 1 }};
// Creating new representation since converter might edit metadata
const tempRepresentation = new BasicRepresentation(data, identifier, representation.metadata.contentType);
const result = await this.converter.handleSafe({ identifier, representation: tempRepresentation, preferences });
let result;
try {
// Creating new representation since converter might edit metadata
const tempRepresentation = await cloneRepresentation(representation);
result = await this.converter.handleSafe({
identifier,
representation: tempRepresentation,
preferences,
});
} catch (error: unknown) {
representation.data.destroy();
throw error;
}
// Drain stream to make sure data was parsed correctly
await arrayifyStream(result.data);
// Stream has been drained so need to create new stream
representation.data = guardedStreamFrom(data);
}
}

View File

@ -1,7 +1,12 @@
import arrayifyStream from 'arrayify-stream';
import { DataFactory } from 'n3';
import type { NamedNode, Quad } from 'rdf-js';
import { BasicRepresentation } from '../ldp/representation/BasicRepresentation';
import type { Representation } from '../ldp/representation/Representation';
import { RepresentationMetadata } from '../ldp/representation/RepresentationMetadata';
import { pushQuad } from './QuadUtil';
import { guardedStreamFrom } from './StreamUtil';
import { LDP, RDF } from './Vocabularies';
/**
@ -33,3 +38,21 @@ export function generateContainmentQuads(containerURI: NamedNode, childURIs: str
return new RepresentationMetadata(containerURI,
{ [LDP.contains]: childURIs.map(DataFactory.namedNode) }).quads();
}
/**
* Helper function to clone a representation, the original representation can still be used.
* This function loads the entire stream in memory.
* @param representation - The representation to clone.
*
* @returns The cloned representation.
*/
export async function cloneRepresentation(representation: Representation): Promise<BasicRepresentation> {
const data = await arrayifyStream(representation.data);
const result = new BasicRepresentation(
data,
new RepresentationMetadata(representation.metadata),
representation.binary,
);
representation.data = guardedStreamFrom(data);
return result;
}

View File

@ -39,6 +39,6 @@ describe('An RdfValidator', (): void => {
const representation = new BasicRepresentation('data', 'content-type');
await expect(validator.handle(representation)).rejects.toThrow('bad data!');
// Make sure the data on the readable has not been reset
expect(representation.data.readableEnded).toBe(true);
expect(representation.data.destroyed).toBe(true);
});
});

View File

@ -0,0 +1,27 @@
import { BasicRepresentation } from '../../../src/ldp/representation/BasicRepresentation';
import type { Representation } from '../../../src/ldp/representation/Representation';
import * as resourceUtils from '../../../src/util/ResourceUtil';
import 'jest-rdf';
describe('ResourceUtil', (): void => {
let representation: Representation;
beforeEach(async(): Promise<void> => {
representation = new BasicRepresentation('data', 'metadata');
});
describe('cloneRepresentation', (): void => {
it('returns a clone of the passed representation.', async(): Promise<void> => {
const res = await resourceUtils.cloneRepresentation(representation);
expect(res.binary).toBe(representation.binary);
expect(res.metadata.identifier).toBe(representation.metadata.identifier);
expect(res.metadata.contentType).toBe(representation.metadata.contentType);
});
it('ensures that original representation does not update when the clone is updated.', async(): Promise<void> => {
const res = await resourceUtils.cloneRepresentation(representation);
res.metadata.contentType = 'typetype';
expect(representation.metadata.contentType).not.toBe(res.metadata.contentType);
});
});
});