From 7011b766b4d6a39a4edcfde19856d9a4b933fda6 Mon Sep 17 00:00:00 2001 From: Joachim Van Herwegen Date: Mon, 7 Dec 2020 16:48:30 +0100 Subject: [PATCH] feat: Create new resources when patching --- src/storage/patch/SparqlUpdatePatchHandler.ts | 25 +++++++++++++------ .../patch/SparqlUpdatePatchHandler.test.ts | 19 ++++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/storage/patch/SparqlUpdatePatchHandler.ts b/src/storage/patch/SparqlUpdatePatchHandler.ts index 470dfe131..3fecf133a 100644 --- a/src/storage/patch/SparqlUpdatePatchHandler.ts +++ b/src/storage/patch/SparqlUpdatePatchHandler.ts @@ -10,6 +10,7 @@ import { RepresentationMetadata } from '../../ldp/representation/RepresentationM import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier'; import { getLoggerFor } from '../../logging/LogUtil'; import { INTERNAL_QUADS } from '../../util/ContentTypes'; +import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError'; import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError'; import { guardStream } from '../../util/GuardedStream'; import type { ResourceLocker } from '../../util/locking/ResourceLocker'; @@ -82,15 +83,23 @@ export class SparqlUpdatePatchHandler extends PatchHandler { */ private async applyPatch(identifier: ResourceIdentifier, deletes: Algebra.Pattern[], inserts: Algebra.Pattern[]): Promise { - // Read the quads of the current representation - const quads = await this.source.getRepresentation(identifier, { type: [{ value: INTERNAL_QUADS, weight: 1 }]}); const store = new Store(); - const importEmitter = store.import(quads.data); - await new Promise((resolve, reject): void => { - importEmitter.on('end', resolve); - importEmitter.on('error', reject); - }); - this.logger.debug(`${store.size} quads in ${identifier.path}.`); + try { + // Read the quads of the current representation + const quads = await this.source.getRepresentation(identifier, { type: [{ value: INTERNAL_QUADS, weight: 1 }]}); + const importEmitter = store.import(quads.data); + await new Promise((resolve, reject): void => { + importEmitter.on('end', resolve); + importEmitter.on('error', reject); + }); + this.logger.debug(`${store.size} quads in ${identifier.path}.`); + } catch (error: unknown) { + // In case the resource does not exist yet we want to create it + if (!(error instanceof NotFoundHttpError)) { + throw error; + } + this.logger.debug(`Patching new resource ${identifier.path}.`); + } // Apply the patch store.removeQuads(deletes); diff --git a/test/unit/storage/patch/SparqlUpdatePatchHandler.test.ts b/test/unit/storage/patch/SparqlUpdatePatchHandler.test.ts index 8180edf44..aaa0f65f8 100644 --- a/test/unit/storage/patch/SparqlUpdatePatchHandler.test.ts +++ b/test/unit/storage/patch/SparqlUpdatePatchHandler.test.ts @@ -8,6 +8,7 @@ import { RepresentationMetadata } from '../../../../src/ldp/representation/Repre import { SparqlUpdatePatchHandler } from '../../../../src/storage/patch/SparqlUpdatePatchHandler'; import type { ResourceStore } from '../../../../src/storage/ResourceStore'; import { INTERNAL_QUADS } from '../../../../src/util/ContentTypes'; +import { NotFoundHttpError } from '../../../../src/util/errors/NotFoundHttpError'; import { NotImplementedHttpError } from '../../../../src/util/errors/NotImplementedHttpError'; import type { Lock } from '../../../../src/util/locking/Lock'; import type { ResourceLocker } from '../../../../src/util/locking/ResourceLocker'; @@ -215,4 +216,22 @@ describe('A SparqlUpdatePatchHandler', (): void => { await expect(handler.handle(input)).rejects.toThrow('error'); expect(order).toEqual([ 'acquire', 'getRepresentation', 'release' ]); }); + + it('creates a new resource if it does not exist yet.', async(): Promise => { + // There is no initial data + startQuads = []; + source.getRepresentation = jest.fn((): any => { + order.push('getRepresentation'); + throw new NotFoundHttpError(); + }); + + await handler.handle({ identifier: { path: 'path' }, + patch: { algebra: translate( + 'INSERT DATA { . }', + { quads: true }, + ) } as SparqlUpdatePatch }); + expect(await basicChecks(startQuads.concat( + [ quad(namedNode('http://test.com/s1'), namedNode('http://test.com/p1'), namedNode('http://test.com/o1')) ], + ))).toBe(true); + }); });