From 5995057240670ff0227bebe991f76520baf83353 Mon Sep 17 00:00:00 2001 From: Joachim Van Herwegen Date: Wed, 6 Jan 2021 14:07:29 +0100 Subject: [PATCH] fix: Don't get normalized metadata for root containers --- src/storage/DataAccessorBasedStore.ts | 3 ++- .../storage/DataAccessorBasedStore.test.ts | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/storage/DataAccessorBasedStore.ts b/src/storage/DataAccessorBasedStore.ts index baa0c7709..76e9040a8 100644 --- a/src/storage/DataAccessorBasedStore.ts +++ b/src/storage/DataAccessorBasedStore.ts @@ -185,7 +185,8 @@ export class DataAccessorBasedStore implements ResourceStore { try { return await this.accessor.getMetadata(identifier); } catch (error: unknown) { - if (error instanceof NotFoundHttpError) { + // Trimming the trailing slash of a root container is undefined as there is no parent container + if (error instanceof NotFoundHttpError && !this.identifierStrategy.isRootContainer(identifier)) { return this.accessor.getMetadata( { path: hasSlash ? trimTrailingSlashes(identifier.path) : ensureTrailingSlash(identifier.path) }, ); diff --git a/test/unit/storage/DataAccessorBasedStore.test.ts b/test/unit/storage/DataAccessorBasedStore.test.ts index f4848c313..a32eff880 100644 --- a/test/unit/storage/DataAccessorBasedStore.test.ts +++ b/test/unit/storage/DataAccessorBasedStore.test.ts @@ -241,6 +241,27 @@ describe('A DataAccessorBasedStore', (): void => { .rejects.toThrow(`${resourceID.path} conflicts with existing path ${resourceID.path}/`); }); + // As discussed in #475, trimming the trailing slash of a root container in getNormalizedMetadata + // can result in undefined behaviour since there is no parent container. + it('will not trim the slash of root containers since there is no parent.', async(): Promise => { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete accessor.data[root]; + + const mock = jest.spyOn(accessor, 'getMetadata'); + + const resourceID = { path: `${root}` }; + representation.metadata.removeAll(RDF.type); + representation.metadata.contentType = 'text/turtle'; + representation.data = guardedStreamFrom([ `<${`${root}`}> a .` ]); + + await expect(store.setRepresentation(resourceID, representation)) + .resolves.toBeUndefined(); + expect(mock).toHaveBeenCalledTimes(1); + expect(mock).toHaveBeenLastCalledWith(resourceID); + + mock.mockRestore(); + }); + it('will error if the target has a different resource type.', async(): Promise => { const resourceID = { path: `${root}resource` }; accessor.data[resourceID.path] = representation;