From 7f3eab0b20e6f9a92c8abf642e82cd55440142fe Mon Sep 17 00:00:00 2001 From: Joachim Van Herwegen Date: Fri, 3 Sep 2021 15:39:57 +0200 Subject: [PATCH] fix: Prevent parent containers from storing generated metadata --- src/storage/DataAccessorBasedStore.ts | 31 +++++++++++++------ .../storage/DataAccessorBasedStore.test.ts | 11 ++++++- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/storage/DataAccessorBasedStore.ts b/src/storage/DataAccessorBasedStore.ts index fd7b3b1ba..bd1479312 100644 --- a/src/storage/DataAccessorBasedStore.ts +++ b/src/storage/DataAccessorBasedStore.ts @@ -268,9 +268,7 @@ export class DataAccessorBasedStore implements ResourceStore { deleted.push(container); // Update modified date of parent - const parentMetadata = await this.accessor.getMetadata(container); - updateModifiedDate(parentMetadata); - await this.accessor.writeContainer(container, parentMetadata); + await this.updateContainerModifiedDate(container); } await this.accessor.deleteResource(identifier); @@ -386,15 +384,11 @@ export class DataAccessorBasedStore implements ResourceStore { } // Parent container is also modified - const parentMetadata = await this.accessor.getMetadata(container); - updateModifiedDate(parentMetadata); - await this.accessor.writeContainer(container, parentMetadata); + await this.updateContainerModifiedDate(container); } // Remove all generated metadata to prevent it from being stored permanently - representation.metadata.removeQuads( - representation.metadata.quads(null, null, null, SOLID_META.terms.ResponseMetadata), - ); + this.removeResponseMetadata(representation.metadata); await (isContainer ? this.accessor.writeContainer(identifier, representation.metadata) : @@ -438,6 +432,25 @@ export class DataAccessorBasedStore implements ResourceStore { representation.metadata.addQuads(quads); } + /** + * Removes all generated data from metadata to prevent it from being stored permanently. + */ + protected removeResponseMetadata(metadata: RepresentationMetadata): void { + metadata.removeQuads( + metadata.quads(null, null, null, SOLID_META.terms.ResponseMetadata), + ); + } + + /** + * Updates the last modified date of the given container + */ + protected async updateContainerModifiedDate(container: ResourceIdentifier): Promise { + const parentMetadata = await this.accessor.getMetadata(container); + updateModifiedDate(parentMetadata); + this.removeResponseMetadata(parentMetadata); + await this.accessor.writeContainer(container, parentMetadata); + } + /** * Generates a new URI for a resource in the given container, potentially using the given slug. * diff --git a/test/unit/storage/DataAccessorBasedStore.test.ts b/test/unit/storage/DataAccessorBasedStore.test.ts index 8aba035b5..d88308502 100644 --- a/test/unit/storage/DataAccessorBasedStore.test.ts +++ b/test/unit/storage/DataAccessorBasedStore.test.ts @@ -26,6 +26,8 @@ import { guardedStreamFrom } from '../../../src/util/StreamUtil'; import { CONTENT_TYPE, SOLID_HTTP, LDP, PIM, RDF, SOLID_META, DC } from '../../../src/util/Vocabularies'; const { namedNode, quad } = DataFactory; +const GENERATED_PREDICATE = namedNode('generated'); + class SimpleDataAccessor implements DataAccessor { public readonly data: Record = {}; @@ -54,7 +56,9 @@ class SimpleDataAccessor implements DataAccessor { public async getMetadata(identifier: ResourceIdentifier): Promise { this.checkExists(identifier); - return this.data[identifier.path].metadata; + const metadata = new RepresentationMetadata(this.data[identifier.path].metadata); + metadata.add(GENERATED_PREDICATE, 'data', SOLID_META.terms.ResponseMetadata); + return metadata; } public async* getChildren(identifier: ResourceIdentifier): AsyncIterableIterator { @@ -180,6 +184,8 @@ describe('A DataAccessorBasedStore', (): void => { containerMetadata.identifier = namedNode(resourceID.path); accessor.data[resourceID.path] = { metadata: containerMetadata } as Representation; const metaMirror = new RepresentationMetadata(containerMetadata); + // Generated metadata will have its graph removed + metaMirror.add(GENERATED_PREDICATE, 'data'); await auxiliaryStrategy.addMetadata(metaMirror); const result = await store.getRepresentation(resourceID); expect(result).toMatchObject({ binary: false }); @@ -407,6 +413,7 @@ describe('A DataAccessorBasedStore', (): void => { await expect(arrayifyStream(accessor.data[resourceID.path].data)).resolves.toEqual([ resourceData ]); expect(accessor.data[resourceID.path].metadata.get(DC.terms.modified)?.value).toBe(now.toISOString()); expect(accessor.data[root].metadata.get(DC.terms.modified)?.value).toBe(now.toISOString()); + expect(accessor.data[root].metadata.get(GENERATED_PREDICATE)).toBeUndefined(); }); it('can write containers.', async(): Promise => { @@ -424,6 +431,7 @@ describe('A DataAccessorBasedStore', (): void => { expect(accessor.data[resourceID.path].metadata.contentType).toBeUndefined(); expect(accessor.data[resourceID.path].metadata.get(DC.terms.modified)?.value).toBe(now.toISOString()); expect(accessor.data[root].metadata.get(DC.terms.modified)?.value).toBe(now.toISOString()); + expect(accessor.data[root].metadata.get(GENERATED_PREDICATE)).toBeUndefined(); }); it('can overwrite resources which does not update parent metadata.', async(): Promise => { @@ -625,6 +633,7 @@ describe('A DataAccessorBasedStore', (): void => { ]); expect(accessor.data[`${root}resource`]).toBeUndefined(); expect(accessor.data[root].metadata.get(DC.terms.modified)?.value).toBe(now.toISOString()); + expect(accessor.data[root].metadata.get(GENERATED_PREDICATE)).toBeUndefined(); }); it('will delete root non-storage containers.', async(): Promise => {