From 17d774fc18e92b9e99beb374b4289c4b8cd5a3a4 Mon Sep 17 00:00:00 2001 From: Ruben Verborgh Date: Sat, 31 Oct 2020 22:22:22 +0100 Subject: [PATCH] chore: Add SPARQL endpoint logging. --- src/storage/accessors/SparqlDataAccessor.ts | 22 +++++++++- .../accessors/SparqlDataAccessor.test.ts | 44 ++++++++++++++++--- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/storage/accessors/SparqlDataAccessor.ts b/src/storage/accessors/SparqlDataAccessor.ts index d0ca683c4..bc8437b7f 100644 --- a/src/storage/accessors/SparqlDataAccessor.ts +++ b/src/storage/accessors/SparqlDataAccessor.ts @@ -17,6 +17,7 @@ import { import type { Representation } from '../../ldp/representation/Representation'; import { RepresentationMetadata } from '../../ldp/representation/RepresentationMetadata'; import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier'; +import { getLoggerFor } from '../../logging/LogUtil'; import { INTERNAL_QUADS } from '../../util/ContentTypes'; import { ConflictHttpError } from '../../util/errors/ConflictHttpError'; import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError'; @@ -44,6 +45,7 @@ const { defaultGraph, namedNode, quad, variable } = DataFactory; * so those don't get overwritten. */ export class SparqlDataAccessor implements DataAccessor { + protected readonly logger = getLoggerFor(this); private readonly endpoint: string; private readonly base: string; private readonly containerManager: ContainerManager; @@ -298,7 +300,15 @@ export class SparqlDataAccessor implements DataAccessor { */ private async sendSparqlConstruct(sparqlQuery: ConstructQuery): Promise { const query = this.generator.stringify(sparqlQuery); - return await this.fetcher.fetchTriples(this.endpoint, query); + this.logger.info(`Sending SPARQL CONSTRUCT query to ${this.endpoint}: ${query}`); + try { + return await this.fetcher.fetchTriples(this.endpoint, query); + } catch (error: unknown) { + if (error instanceof Error) { + this.logger.error(`SPARQL endpoint ${this.endpoint} error: ${error.message}`); + } + throw error; + } } /** @@ -307,6 +317,14 @@ export class SparqlDataAccessor implements DataAccessor { */ private async sendSparqlUpdate(sparqlQuery: Update): Promise { const query = this.generator.stringify(sparqlQuery); - return await this.fetcher.fetchUpdate(this.endpoint, query); + this.logger.info(`Sending SPARQL UPDATE query to ${this.endpoint}: ${query}`); + try { + return await this.fetcher.fetchUpdate(this.endpoint, query); + } catch (error: unknown) { + if (error instanceof Error) { + this.logger.error(`SPARQL endpoint ${this.endpoint} error: ${error.message}`); + } + throw error; + } } } diff --git a/test/unit/storage/accessors/SparqlDataAccessor.test.ts b/test/unit/storage/accessors/SparqlDataAccessor.test.ts index 8e7d21b97..df9c2b941 100644 --- a/test/unit/storage/accessors/SparqlDataAccessor.test.ts +++ b/test/unit/storage/accessors/SparqlDataAccessor.test.ts @@ -35,14 +35,25 @@ describe('A SparqlDataAccessor', (): void => { let fetchTriples: jest.Mock>; let fetchUpdate: jest.Mock>; let triples: Quad[]; + let fetchError: any; + let updateError: any; beforeEach(async(): Promise => { metadata = new RepresentationMetadata(); triples = [ quad(namedNode('this'), namedNode('a'), namedNode('triple')) ]; - // Makes it so the `SparqlEndpointFetcher` will always return the contents of the `bindings` array - fetchTriples = jest.fn(async(): Promise => streamifyArray(triples)); - fetchUpdate = jest.fn(async(): Promise => undefined); + // Makes it so the `SparqlEndpointFetcher` will always return the contents of the `triples` array + fetchTriples = jest.fn(async(): Promise => { + if (fetchError) { + throw fetchError; + } + return streamifyArray(triples); + }); + fetchUpdate = jest.fn(async(): Promise => { + if (updateError) { + throw updateError; + } + }); (SparqlEndpointFetcher as any).mockImplementation((): any => ({ fetchTriples, fetchUpdate, @@ -128,8 +139,8 @@ describe('A SparqlDataAccessor', (): void => { }); it('throws 404 if no metadata was found.', async(): Promise => { - // Clear bindings array - triples.splice(0, triples.length); + // Clear triples array + triples = []; await expect(accessor.getMetadata({ path: 'http://identifier' })).rejects.toThrow(NotFoundHttpError); expect(fetchTriples).toHaveBeenCalledTimes(1); @@ -205,4 +216,27 @@ describe('A SparqlDataAccessor', (): void => { await expect(accessor.writeDocument({ path: 'http://test.com/container/resource' }, data, metadata)) .rejects.toThrow(new UnsupportedHttpError('Only triples in the default graph are supported.')); }); + + it('errors when the SPARQL endpoint fails during reading.', async(): Promise => { + fetchError = 'error'; + await expect(accessor.getMetadata({ path: 'http://identifier' })).rejects.toBe(fetchError); + + fetchError = new Error(); + await expect(accessor.getMetadata({ path: 'http://identifier' })).rejects.toThrow(fetchError); + + fetchError = undefined; + }); + + it('errors when the SPARQL endpoint fails during writing.', async(): Promise => { + const path = 'http://test.com/container/'; + metadata = new RepresentationMetadata(path); + + updateError = 'error'; + await expect(accessor.writeContainer({ path }, metadata)).rejects.toBe(updateError); + + updateError = new Error(); + await expect(accessor.writeContainer({ path }, metadata)).rejects.toThrow(updateError); + + updateError = undefined; + }); });