diff --git a/.changeset/selfish-pens-mix.md b/.changeset/selfish-pens-mix.md new file mode 100644 index 0000000..45f97bd --- /dev/null +++ b/.changeset/selfish-pens-mix.md @@ -0,0 +1,5 @@ +--- +"rdf-transform-graph-imports": patch +--- + +Failures to fetch remote import would break the stream diff --git a/index.ts b/index.ts index f297071..93c1d15 100644 --- a/index.ts +++ b/index.ts @@ -31,8 +31,8 @@ function transform(env: Environment, { basePath }: Options = {}) { }) for (const importTarget of imports) { - const importStream = fetchImport(env, importTarget) - .pipe(transform(env, { basePath: importTarget })) + const fetchStream = await fetchImport(env, importTarget) + const importStream = fetchStream.pipe(transform(env, { basePath: importTarget })) for await (const importedQuad of importStream) { this.push(importedQuad) diff --git a/lib/fetchImport.ts b/lib/fetchImport.ts index 36d638b..1621abe 100644 --- a/lib/fetchImport.ts +++ b/lib/fetchImport.ts @@ -1,23 +1,15 @@ -import { Readable, PassThrough } from 'readable-stream' +import { Readable } from 'readable-stream' import Environment from './env.js' -export default function (env: Environment, importTarget: string | URL) { +export default async function (env: Environment, importTarget: string | URL) { if (typeof importTarget === 'string') { return env.fromFile(importTarget) } - const remoteStream = new PassThrough({ objectMode: true }) - - env.fetch(importTarget.toString()) - .then(response => { - return response.quadStream() as unknown as Promise - }) - .then(quadStream => { - quadStream.pipe(remoteStream) - }) - .catch(error => { - remoteStream.emit('error', error) - }) + const response = await env.fetch(importTarget.toString()) + if (!response.ok) { + throw new Error(`Failed to fetch: ${response.statusText}`) + } - return remoteStream + return response.quadStream() as unknown as Promise } diff --git a/package-lock.json b/package-lock.json index dc7cd0a..a4602a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rdf-transform-graph-imports", - "version": "0.0.0", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rdf-transform-graph-imports", - "version": "0.0.0", + "version": "0.1.0", "license": "MIT", "dependencies": { "is-uri": "^1.2.6", @@ -21,6 +21,7 @@ "@tpluscode/eslint-config": "^0.4.4", "@types/absolute-url": "^2.0.0", "@types/chai": "^4.3.9", + "@types/chai-as-promised": "^7.1.8", "@types/express": "^4.17.20", "@types/is-uri": "^1.0.2", "@types/mocha": "^10.0.3", @@ -33,6 +34,7 @@ "absolute-url": "^2.0.0", "c8": "^8.0.1", "chai": "^4.3.10", + "chai-as-promised": "^7.1.1", "eslint-import-resolver-typescript": "^3.6.1", "express": "^4.18.2", "husky": "^8.0.3", @@ -2448,6 +2450,15 @@ "integrity": "sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==", "dev": true }, + "node_modules/@types/chai-as-promised": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz", + "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==", + "dev": true, + "dependencies": { + "@types/chai": "*" + } + }, "node_modules/@types/clownface": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/clownface/-/clownface-2.0.3.tgz", @@ -3886,6 +3897,18 @@ "node": ">=4" } }, + "node_modules/chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "dependencies": { + "check-error": "^1.0.2" + }, + "peerDependencies": { + "chai": ">= 2.1.2 < 5" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", diff --git a/package.json b/package.json index df94742..294ddce 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "@tpluscode/eslint-config": "^0.4.4", "@types/absolute-url": "^2.0.0", "@types/chai": "^4.3.9", + "@types/chai-as-promised": "^7.1.8", "@types/express": "^4.17.20", "@types/is-uri": "^1.0.2", "@types/mocha": "^10.0.3", @@ -60,6 +61,7 @@ "absolute-url": "^2.0.0", "c8": "^8.0.1", "chai": "^4.3.10", + "chai-as-promised": "^7.1.1", "eslint-import-resolver-typescript": "^3.6.1", "express": "^4.18.2", "husky": "^8.0.3", @@ -77,6 +79,9 @@ }, "mocha": { "extension": "ts", - "loader": "ts-node/esm" + "loader": "ts-node/esm", + "require": [ + "test/mocha-setup.cjs" + ] } } diff --git a/test/index.test.ts b/test/index.test.ts index e47995c..9bda954 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -59,6 +59,16 @@ describe('rdf-merge-stream', () => { // then expect(await turtle(merged)).toMatchSnapshot() }) + + it('fails when remote resource is not found', async () => { + // given + const response = await rdf.fetch('http://localhost:6666/invalid-import') + const root = await response.quadStream() as unknown as Readable + + // then + await expect(rdf.dataset().import(root.pipe(transform(rdf)))) + .to.be.eventually.rejectedWith('Failed to fetch: Not Found') + }) }) async function turtle(merged: Dataset) { diff --git a/test/mocha-setup.cjs b/test/mocha-setup.cjs new file mode 100644 index 0000000..8187a33 --- /dev/null +++ b/test/mocha-setup.cjs @@ -0,0 +1,4 @@ +const chai = require('chai') +const chaiAsPromised = require('chai-as-promised') + +chai.use(chaiAsPromised); diff --git a/test/server/index.ts b/test/server/index.ts index a42b816..5691cc9 100644 --- a/test/server/index.ts +++ b/test/server/index.ts @@ -1,5 +1,6 @@ import * as url from 'url' import * as path from 'path' +import * as fs from 'fs' import express from 'express' import * as absoluteUrl from 'absolute-url' import rdfHandler from '@rdfjs/express-handler' @@ -14,7 +15,12 @@ export function start() { .get('/*', (req, res) => { res.setHeader('Content-Type', 'text/turtle') - const quads = env.fromFile(path.join(__dirname, `${req.path}.ttl`), { + const ttlPath = path.join(__dirname, `${req.path}.ttl`) + if (!fs.existsSync(ttlPath)) { + return res.sendStatus(404) + } + + const quads = env.fromFile(ttlPath, { baseUri: req.absoluteUrl(), }) diff --git a/test/server/resources/invalid-import.ttl b/test/server/resources/invalid-import.ttl new file mode 100644 index 0000000..92d4e1a --- /dev/null +++ b/test/server/resources/invalid-import.ttl @@ -0,0 +1,7 @@ +PREFIX code: +PREFIX schema: +PREFIX sh: + +[ + code:imports ; +] .