fetch-sparql-endpoint
Advanced tools
Comparing version 4.1.1 to 4.2.0
#!/usr/bin/env node | ||
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const fs_1 = require("fs"); | ||
const minimist = require("minimist"); | ||
const node_fs_1 = require("node:fs"); | ||
const n3_1 = require("n3"); | ||
const rdf_string_1 = require("rdf-string"); | ||
const index_1 = require("../index"); | ||
// tslint:disable-next-line:no-var-requires | ||
const n3 = require('n3'); | ||
process.argv.splice(0, 2); | ||
const args = minimist(process.argv); | ||
if (args._.length === 0 || args._.length > 2 || args.h || args.help) { | ||
// Print command usage | ||
process.stderr.write(`fetch-sparql-endpoint Sends a query to a SPARQL endpoint | ||
Usage: | ||
fetch-sparql-endpoint https://dbpedia.org/sparql [-q] 'SELECT * WHERE { ?s ?p ?o } 100' | ||
fetch-sparql-endpoint https://dbpedia.org/sparql -f query.sparql | ||
cat query.sparql | fetch-sparql-endpoint https://dbpedia.org/sparql | ||
Options: | ||
-q evaluate the given SPARQL query string | ||
-f evaluate the SPARQL query in the given file | ||
-g send query via HTTP GET instead of POST | ||
--help print this help message | ||
`); | ||
process.exit(1); | ||
const streamToString = require("stream-to-string"); | ||
const yargs_1 = require("yargs"); | ||
const helpers_1 = require("yargs/helpers"); | ||
const __1 = require(".."); | ||
function getQuery(query, file) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (query) { | ||
return query; | ||
} | ||
if (file) { | ||
return (0, node_fs_1.readFileSync)(file, { encoding: 'utf-8' }); | ||
} | ||
return streamToString(process.stdin); | ||
}); | ||
} | ||
async function getQuery() { | ||
if (args._.length > 1) { | ||
return args._[1]; | ||
} | ||
else if (args.q) { | ||
return args.q; | ||
} | ||
else if (args.f) { | ||
return (0, fs_1.readFileSync)(args.f, { encoding: 'utf8' }); | ||
} | ||
else { | ||
// tslint:disable-next-line:no-var-requires | ||
return await require('stream-to-string')(process.stdin); | ||
} | ||
function querySelect(endpoint, fetcher, query) { | ||
return new Promise((resolve, reject) => { | ||
fetcher.fetchBindings(endpoint, query).then(bindingsStream => bindingsStream | ||
.on('data', (bindings) => { | ||
process.stdout.write(`${JSON.stringify(Object.fromEntries(Object.entries(bindings).map(([key, value]) => [ | ||
key, | ||
(0, rdf_string_1.termToString)(value), | ||
])))}\n`); | ||
}) | ||
.on('error', reject) | ||
.on('end', resolve)).catch(reject); | ||
}); | ||
} | ||
const endpoint = args._[0]; | ||
getQuery().then((query) => { | ||
const fetcher = new index_1.SparqlEndpointFetcher({ | ||
method: args.g ? 'GET' : 'POST', | ||
function queryAsk(endpoint, fetcher, query) { | ||
return new Promise((resolve, reject) => { | ||
fetcher.fetchAsk(endpoint, query) | ||
.then((answer) => { | ||
process.stdout.write(`${answer}\n`); | ||
resolve(); | ||
}) | ||
.catch(reject); | ||
}); | ||
const queryType = fetcher.getQueryType(query); | ||
switch (queryType) { | ||
case 'SELECT': | ||
querySelect(fetcher, query); | ||
break; | ||
case 'ASK': | ||
queryAsk(fetcher, query); | ||
break; | ||
case 'CONSTRUCT': | ||
queryConstruct(fetcher, query); | ||
break; | ||
case 'UNKNOWN': | ||
if (fetcher.getUpdateTypes(query) !== 'UNKNOWN') { | ||
update(fetcher, query); | ||
} | ||
break; | ||
} | ||
}); | ||
function querySelect(fetcher, query) { | ||
fetcher.fetchBindings(endpoint, query) | ||
.then((bindingsStream) => { | ||
bindingsStream.on('data', (bindings) => { | ||
for (const variable of Object.keys(bindings)) { | ||
bindings[variable] = (0, rdf_string_1.termToString)(bindings[variable]); | ||
} | ||
process.stdout.write(JSON.stringify(bindings) + '\n'); | ||
}); | ||
bindingsStream.on('error', (error) => process.stderr.write(error.toString())); | ||
}) | ||
.catch((error) => { | ||
process.stderr.write(error.message + '\n'); | ||
process.exit(1); | ||
}); | ||
} | ||
function queryAsk(fetcher, query) { | ||
fetcher.fetchAsk(endpoint, query) | ||
.then((answer) => { | ||
process.stdout.write(answer + '\n'); | ||
}) | ||
.catch((error) => { | ||
process.stderr.write(error.message + '\n'); | ||
process.exit(1); | ||
function queryConstruct(endpoint, fetcher, query) { | ||
return new Promise((resolve, reject) => { | ||
fetcher.fetchTriples(endpoint, query) | ||
.then(tripleStream => tripleStream | ||
.on('error', reject) | ||
.pipe(new n3_1.StreamWriter(__1.SparqlEndpointFetcher.CONTENTTYPE_TURTLE)) | ||
.pipe(process.stdout) | ||
.on('end', resolve)).catch(reject); | ||
}); | ||
} | ||
function queryConstruct(fetcher, query) { | ||
fetcher.fetchTriples(endpoint, query) | ||
.then((tripleStream) => { | ||
tripleStream | ||
.on('error', (error) => process.stderr.write(error.toString())) | ||
.pipe(new n3.StreamWriter(index_1.SparqlEndpointFetcher.CONTENTTYPE_TURTLE)) | ||
.pipe(process.stdout); | ||
}) | ||
.catch((error) => { | ||
process.stderr.write(error.message + '\n'); | ||
process.exit(1); | ||
function update(endpoint, fetcher, query) { | ||
return new Promise((resolve, reject) => { | ||
fetcher.fetchUpdate(endpoint, query).then(() => { | ||
process.stdout.write('OK\n'); | ||
resolve(); | ||
}).catch(reject); | ||
}); | ||
} | ||
function update(fetcher, query) { | ||
fetcher.fetchUpdate(endpoint, query) | ||
.then(() => { | ||
process.stdout.write('OK\n'); | ||
}) | ||
.catch((error) => { | ||
process.stderr.write(error.message + '\n'); | ||
process.exit(1); | ||
function run(argv) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const args = yield (0, yargs_1.default)((0, helpers_1.hideBin)(argv), 'Sends a query to a SPARQL endpoint') | ||
.example('$0 --endpoint https://dbpedia.org/sparql --query \'SELECT * WHERE { ?s ?p ?o } LIMIT 100\'', 'Fetch 100 triples from the DBPedia SPARQL endpoint') | ||
.example('$0 --endpoint https://dbpedia.org/sparql --file query.rq', 'Run the SPARQL query from query.rq against the DBPedia SPARQL endpoint') | ||
.example('cat query.rq | $0 --endpoint https://dbpedia.org/sparql', 'Run the SPARQL query from query.rq against the DBPedia SPARQL endpoint') | ||
.options({ | ||
endpoint: { type: 'string', describe: 'Send the query to this SPARQL endpoint', demandOption: true }, | ||
query: { type: 'string', describe: 'Evaluate the given SPARQL query string' }, | ||
file: { type: 'string', describe: 'Evaluate the SPARQL query in the given file' }, | ||
get: { type: 'boolean', describe: 'Send query via HTTP GET instead of POST', default: false }, | ||
timeout: { type: 'number', describe: 'The timeout value in seconds to finish the query' }, | ||
auth: { choices: ['basic'], describe: 'The type of authentication to use' }, | ||
}) | ||
.check((arg) => { | ||
if (arg.auth === 'basic' && (!process.env.SPARQL_USERNAME || !process.env.SPARQL_PASSWORD)) { | ||
throw new Error('Basic authentication requires the SPARQL_USERNAME and SPARQL_PASSWORD environment variables.'); | ||
} | ||
return true; | ||
}) | ||
.version() | ||
.help('help') | ||
.parse(); | ||
const queryString = yield getQuery(args.query, args.file); | ||
let defaultHeaders; | ||
if (args.auth === 'basic') { | ||
defaultHeaders = new Headers({ | ||
authorization: `Basic ${Buffer.from(`${process.env.SPARQL_USERNAME}:${process.env.SPARQL_PASSWORD}`, 'binary').toString('base64')}`, | ||
}); | ||
} | ||
const fetcher = new __1.SparqlEndpointFetcher({ | ||
method: args.get ? 'GET' : 'POST', | ||
timeout: args.timeout ? args.timeout * 1000 : undefined, | ||
defaultHeaders, | ||
}); | ||
const queryType = fetcher.getQueryType(queryString); | ||
switch (queryType) { | ||
case 'SELECT': | ||
yield querySelect(args.endpoint, fetcher, queryString); | ||
break; | ||
case 'ASK': | ||
yield queryAsk(args.endpoint, fetcher, queryString); | ||
break; | ||
case 'CONSTRUCT': | ||
yield queryConstruct(args.endpoint, fetcher, queryString); | ||
break; | ||
case 'UNKNOWN': | ||
if (fetcher.getUpdateTypes(queryString) !== 'UNKNOWN') { | ||
yield update(args.endpoint, fetcher, queryString); | ||
} | ||
break; | ||
} | ||
}); | ||
} | ||
run(process.argv).then().catch((error) => process.stderr.write(`${error.name}: ${error.message}\n${error.stack}`)); | ||
//# sourceMappingURL=fetch-sparql-endpoint.js.map |
@@ -1,1 +0,1 @@ | ||
export * from "./lib/SparqlEndpointFetcher"; | ||
export * from './lib/SparqlEndpointFetcher'; |
/// <reference types="node" /> | ||
/// <reference types="node" /> | ||
import "cross-fetch/polyfill"; | ||
import * as RDF from "@rdfjs/types"; | ||
import { InsertDeleteOperation, ManagementOperation } from "sparqljs"; | ||
import { ISettings, SparqlJsonParser } from "sparqljson-parse"; | ||
import { SparqlXmlParser } from "sparqlxml-parse"; | ||
import { Readable } from "stream"; | ||
import type * as RDF from '@rdfjs/types'; | ||
import type { Readable } from 'readable-stream'; | ||
import { type InsertDeleteOperation, type ManagementOperation } from 'sparqljs'; | ||
import { type ISettings as ISparqlJsonParserArgs, SparqlJsonParser } from 'sparqljson-parse'; | ||
import { type ISettings as ISparqlXmlParserArgs, SparqlXmlParser } from 'sparqlxml-parse'; | ||
/** | ||
@@ -14,16 +12,14 @@ * A SparqlEndpointFetcher can send queries to SPARQL endpoints, | ||
export declare class SparqlEndpointFetcher { | ||
static CONTENTTYPE_SPARQL_JSON: string; | ||
static CONTENTTYPE_SPARQL_XML: string; | ||
static CONTENTTYPE_SPARQL: string; | ||
static CONTENTTYPE_TURTLE: string; | ||
readonly method: 'POST' | 'GET'; | ||
readonly additionalUrlParams: URLSearchParams; | ||
readonly defaultHeaders: Headers; | ||
static readonly CONTENTTYPE_SPARQL_JSON = "application/sparql-results+json"; | ||
static readonly CONTENTTYPE_SPARQL_XML = "application/sparql-results+xml"; | ||
static readonly CONTENTTYPE_TURTLE = "text/turtle"; | ||
static readonly CONTENTTYPE_SPARQL: string; | ||
protected readonly method: 'GET' | 'POST'; | ||
protected readonly timeout?: number; | ||
protected readonly additionalUrlParams: URLSearchParams; | ||
protected readonly defaultHeaders: Headers; | ||
readonly fetchCb?: (input: Request | string, init?: RequestInit) => Promise<Response>; | ||
readonly sparqlParsers: { | ||
[contentType: string]: ISparqlResultsParser; | ||
}; | ||
readonly sparqlJsonParser: SparqlJsonParser; | ||
readonly sparqlXmlParser: SparqlXmlParser; | ||
readonly timeout: number; | ||
protected readonly sparqlParsers: Record<string, ISparqlResultsParser>; | ||
protected readonly sparqlJsonParser: SparqlJsonParser; | ||
protected readonly sparqlXmlParser: SparqlXmlParser; | ||
constructor(args?: ISparqlEndpointFetcherArgs); | ||
@@ -36,5 +32,5 @@ /** | ||
* @param {string} query A query. | ||
* @return {"SELECT" | "ASK" | "CONSTRUCT" | "UNKNOWN"} The query type. | ||
* @return {'SELECT' | 'ASK' | 'CONSTRUCT' | 'UNKNOWN'} The query type. | ||
*/ | ||
getQueryType(query: string): "SELECT" | "ASK" | "CONSTRUCT" | "UNKNOWN"; | ||
getQueryType(query: string): 'SELECT' | 'ASK' | 'CONSTRUCT' | 'UNKNOWN'; | ||
/** | ||
@@ -99,3 +95,3 @@ * Get the query type of the given update query. | ||
} | ||
export interface ISparqlEndpointFetcherArgs extends ISettings { | ||
export interface ISparqlEndpointFetcherArgs extends ISparqlJsonParserArgs, ISparqlXmlParserArgs { | ||
/** | ||
@@ -114,11 +110,9 @@ * A custom HTTP method for issuing (non-update) queries, defaults to POST. | ||
} | ||
export interface IBindings { | ||
[key: string]: RDF.Term; | ||
} | ||
export interface ISparqlResultsParser { | ||
parseResultsStream(sparqlResponseStream: NodeJS.ReadableStream): NodeJS.ReadableStream; | ||
parseBooleanStream(sparqlResponseStream: NodeJS.ReadableStream): Promise<boolean>; | ||
parseResultsStream: (sparqlResponseStream: NodeJS.ReadableStream) => NodeJS.ReadableStream; | ||
parseBooleanStream: (sparqlResponseStream: NodeJS.ReadableStream) => Promise<boolean>; | ||
} | ||
export type IBindings = Record<string, RDF.Term>; | ||
export type IUpdateTypes = { | ||
[K in ManagementOperation['type'] | InsertDeleteOperation['updateType']]?: boolean; | ||
}; |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.SparqlEndpointFetcher = void 0; | ||
require("cross-fetch/polyfill"); | ||
const isStream = require("is-stream"); | ||
const n3_1 = require("n3"); | ||
const readable_from_web_1 = require("readable-from-web"); | ||
const sparqljs_1 = require("sparqljs"); | ||
@@ -9,6 +20,2 @@ const sparqljson_parse_1 = require("sparqljson-parse"); | ||
const stringifyStream = require("stream-to-string"); | ||
const readable_web_to_node_stream_1 = require("@smessie/readable-web-to-node-stream"); | ||
// tslint:disable:no-var-requires | ||
const n3 = require('n3'); | ||
const isStream = require('is-stream'); | ||
/** | ||
@@ -20,7 +27,8 @@ * A SparqlEndpointFetcher can send queries to SPARQL endpoints, | ||
constructor(args) { | ||
args = args || {}; | ||
this.method = args.method || 'POST'; | ||
this.additionalUrlParams = args.additionalUrlParams || new URLSearchParams(); | ||
this.defaultHeaders = args.defaultHeaders || new Headers(); | ||
this.fetchCb = args.fetch; | ||
var _a, _b, _c; | ||
this.method = (_a = args === null || args === void 0 ? void 0 : args.method) !== null && _a !== void 0 ? _a : 'POST'; | ||
this.timeout = args === null || args === void 0 ? void 0 : args.timeout; | ||
this.additionalUrlParams = (_b = args === null || args === void 0 ? void 0 : args.additionalUrlParams) !== null && _b !== void 0 ? _b : new URLSearchParams(); | ||
this.defaultHeaders = (_c = args === null || args === void 0 ? void 0 : args.defaultHeaders) !== null && _c !== void 0 ? _c : new Headers(); | ||
this.fetchCb = args === null || args === void 0 ? void 0 : args.fetch; | ||
this.sparqlJsonParser = new sparqljson_parse_1.SparqlJsonParser(args); | ||
@@ -30,11 +38,10 @@ this.sparqlXmlParser = new sparqlxml_parse_1.SparqlXmlParser(args); | ||
[SparqlEndpointFetcher.CONTENTTYPE_SPARQL_JSON]: { | ||
parseBooleanStream: (sparqlResponseStream) => this.sparqlJsonParser.parseJsonBooleanStream(sparqlResponseStream), | ||
parseResultsStream: (sparqlResponseStream) => this.sparqlJsonParser.parseJsonResultsStream(sparqlResponseStream), | ||
parseBooleanStream: sparqlResponseStream => this.sparqlJsonParser.parseJsonBooleanStream(sparqlResponseStream), | ||
parseResultsStream: sparqlResponseStream => this.sparqlJsonParser.parseJsonResultsStream(sparqlResponseStream), | ||
}, | ||
[SparqlEndpointFetcher.CONTENTTYPE_SPARQL_XML]: { | ||
parseBooleanStream: (sparqlResponseStream) => this.sparqlXmlParser.parseXmlBooleanStream(sparqlResponseStream), | ||
parseResultsStream: (sparqlResponseStream) => this.sparqlXmlParser.parseXmlResultsStream(sparqlResponseStream), | ||
parseBooleanStream: sparqlResponseStream => this.sparqlXmlParser.parseXmlBooleanStream(sparqlResponseStream), | ||
parseResultsStream: sparqlResponseStream => this.sparqlXmlParser.parseXmlResultsStream(sparqlResponseStream), | ||
}, | ||
}; | ||
this.timeout = args.timeout; | ||
} | ||
@@ -47,8 +54,10 @@ /** | ||
* @param {string} query A query. | ||
* @return {"SELECT" | "ASK" | "CONSTRUCT" | "UNKNOWN"} The query type. | ||
* @return {'SELECT' | 'ASK' | 'CONSTRUCT' | 'UNKNOWN'} The query type. | ||
*/ | ||
getQueryType(query) { | ||
const parsedQuery = new sparqljs_1.Parser({ sparqlStar: true }).parse(query); | ||
return parsedQuery.type === 'query' | ||
? (parsedQuery.queryType === 'DESCRIBE' ? 'CONSTRUCT' : parsedQuery.queryType) : "UNKNOWN"; | ||
if (parsedQuery.type === 'query') { | ||
return parsedQuery.queryType === 'DESCRIBE' ? 'CONSTRUCT' : parsedQuery.queryType; | ||
} | ||
return 'UNKNOWN'; | ||
} | ||
@@ -77,8 +86,4 @@ /** | ||
} | ||
else { | ||
return "UNKNOWN"; | ||
} | ||
; | ||
return 'UNKNOWN'; | ||
} | ||
; | ||
/** | ||
@@ -91,10 +96,11 @@ * Send a SELECT query to the given endpoint URL and return the resulting bindings stream. | ||
*/ | ||
async fetchBindings(endpoint, query) { | ||
const [contentType, responseStream] = await this | ||
.fetchRawStream(endpoint, query, SparqlEndpointFetcher.CONTENTTYPE_SPARQL); | ||
const parser = this.sparqlParsers[contentType]; | ||
if (!parser) { | ||
throw new Error('Unknown SPARQL results content type: ' + contentType); | ||
} | ||
return parser.parseResultsStream(responseStream); | ||
fetchBindings(endpoint, query) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const [contentType, responseStream] = yield this.fetchRawStream(endpoint, query, SparqlEndpointFetcher.CONTENTTYPE_SPARQL); | ||
const parser = this.sparqlParsers[contentType]; | ||
if (!parser) { | ||
throw new Error(`Unknown SPARQL results content type: ${contentType}`); | ||
} | ||
return parser.parseResultsStream(responseStream); | ||
}); | ||
} | ||
@@ -107,10 +113,11 @@ /** | ||
*/ | ||
async fetchAsk(endpoint, query) { | ||
const [contentType, responseStream] = await this | ||
.fetchRawStream(endpoint, query, SparqlEndpointFetcher.CONTENTTYPE_SPARQL); | ||
const parser = this.sparqlParsers[contentType]; | ||
if (!parser) { | ||
throw new Error('Unknown SPARQL results content type: ' + contentType); | ||
} | ||
return parser.parseBooleanStream(responseStream); | ||
fetchAsk(endpoint, query) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const [contentType, responseStream] = yield this.fetchRawStream(endpoint, query, SparqlEndpointFetcher.CONTENTTYPE_SPARQL); | ||
const parser = this.sparqlParsers[contentType]; | ||
if (!parser) { | ||
throw new Error(`Unknown SPARQL results content type: ${contentType}`); | ||
} | ||
return parser.parseBooleanStream(responseStream); | ||
}); | ||
} | ||
@@ -123,5 +130,7 @@ /** | ||
*/ | ||
async fetchTriples(endpoint, query) { | ||
const rawStream = (await this.fetchRawStream(endpoint, query, SparqlEndpointFetcher.CONTENTTYPE_TURTLE))[1]; | ||
return rawStream.pipe(new n3.StreamParser({ format: SparqlEndpointFetcher.CONTENTTYPE_TURTLE })); | ||
fetchTriples(endpoint, query) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const [contentType, responseStream] = yield this.fetchRawStream(endpoint, query, SparqlEndpointFetcher.CONTENTTYPE_TURTLE); | ||
return responseStream.pipe(new n3_1.StreamParser({ format: contentType })); | ||
}); | ||
} | ||
@@ -134,17 +143,20 @@ /** | ||
*/ | ||
async fetchUpdate(endpoint, query) { | ||
const AbortController = globalThis.AbortController || await Promise.resolve().then(() => require('abort-controller')); | ||
const abortController = new AbortController(); | ||
const defaultHeadersRaw = {}; | ||
this.defaultHeaders.forEach((value, key) => { | ||
defaultHeadersRaw[key] = value; | ||
fetchUpdate(endpoint, query) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const abortController = new AbortController(); | ||
const defaultHeadersRaw = {}; | ||
// Headers object does not have other means to iterate it according to the typings | ||
// eslint-disable-next-line unicorn/no-array-for-each | ||
this.defaultHeaders.forEach((value, key) => { | ||
defaultHeadersRaw[key] = value; | ||
}); | ||
const init = { | ||
method: 'POST', | ||
headers: Object.assign(Object.assign({}, defaultHeadersRaw), { 'content-type': 'application/sparql-update' }), | ||
body: query, | ||
signal: abortController.signal, | ||
}; | ||
yield this.handleFetchCall(endpoint, init, { ignoreBody: true }); | ||
abortController.abort(); | ||
}); | ||
const init = { | ||
method: 'POST', | ||
headers: Object.assign(Object.assign({}, defaultHeadersRaw), { 'content-type': 'application/sparql-update' }), | ||
body: query, | ||
signal: abortController.signal, | ||
}; | ||
await this.handleFetchCall(endpoint, init, { ignoreBody: true }); | ||
abortController.abort(); | ||
} | ||
@@ -161,21 +173,23 @@ /** | ||
*/ | ||
async fetchRawStream(endpoint, query, acceptHeader) { | ||
let url = this.method === 'POST' ? endpoint : endpoint + '?query=' + encodeURIComponent(query); | ||
// Initiate request | ||
const headers = new Headers(this.defaultHeaders); | ||
let body; | ||
headers.append('Accept', acceptHeader); | ||
if (this.method === 'POST') { | ||
headers.append('Content-Type', 'application/x-www-form-urlencoded'); | ||
body = new URLSearchParams(); | ||
body.set('query', query); | ||
this.additionalUrlParams.forEach((value, key) => { | ||
body.set(key, value); | ||
}); | ||
headers.append('Content-Length', body.toString().length.toString()); | ||
} | ||
else if (this.additionalUrlParams.toString() !== '') { | ||
url += `&${this.additionalUrlParams.toString()}`; | ||
} | ||
return this.handleFetchCall(url, { headers, method: this.method, body }); | ||
fetchRawStream(endpoint, query, acceptHeader) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
let url = this.method === 'POST' ? endpoint : `${endpoint}?query=${encodeURIComponent(query)}`; | ||
// Initiate request | ||
let body; | ||
const headers = new Headers(this.defaultHeaders); | ||
headers.append('Accept', acceptHeader); | ||
if (this.method === 'POST') { | ||
headers.append('Content-Type', 'application/x-www-form-urlencoded'); | ||
body = new URLSearchParams(); | ||
body.set('query', query); | ||
for (const [key, value] of this.additionalUrlParams.entries()) { | ||
body.set(key, value); | ||
} | ||
headers.append('Content-Length', body.toString().length.toString()); | ||
} | ||
else if (this.additionalUrlParams.toString().length > 0) { | ||
url += `&${this.additionalUrlParams.toString()}`; | ||
} | ||
return this.handleFetchCall(url, { headers, method: this.method, body }); | ||
}); | ||
} | ||
@@ -190,41 +204,37 @@ /** | ||
*/ | ||
async handleFetchCall(url, init, options = {}) { | ||
let timeoutId; | ||
if (this.timeout) { | ||
const controller = new AbortController(); | ||
init.signal = controller.signal; | ||
timeoutId = setTimeout(() => controller.abort(), this.timeout); | ||
} | ||
const httpResponse = await (this.fetchCb || fetch)(url, init); | ||
clearTimeout(timeoutId); | ||
let responseStream; | ||
// Handle response body | ||
if (!options.ignoreBody) { | ||
// Wrap WhatWG readable stream into a Node.js readable stream | ||
// If the body already is a Node.js stream (in the case of node-fetch), don't do explicit conversion. | ||
responseStream = isStream(httpResponse.body) | ||
? httpResponse.body : new readable_web_to_node_stream_1.ReadableWebToNodeStream(httpResponse.body); | ||
} | ||
// Determine the content type and emit it to the stream | ||
let contentType = httpResponse.headers.get('Content-Type') || ''; | ||
if (contentType.indexOf(';') > 0) { | ||
contentType = contentType.substr(0, contentType.indexOf(';')); | ||
} | ||
// Emit an error if the server returned an invalid response | ||
if (!httpResponse.ok) { | ||
const simpleUrl = /^[^?]*/u.exec(url)[0]; | ||
let bodyString = 'empty response'; | ||
if (responseStream) { | ||
bodyString = await stringifyStream(responseStream); | ||
handleFetchCall(url, init, options) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
var _a, _b, _c; | ||
let timeout; | ||
let responseStream; | ||
if (this.timeout) { | ||
const controller = new AbortController(); | ||
init.signal = controller.signal; | ||
timeout = setTimeout(() => controller.abort(), this.timeout); | ||
} | ||
throw new Error(`Invalid SPARQL endpoint response from ${simpleUrl} (HTTP status ${httpResponse.status}):\n${bodyString}`); | ||
} | ||
return [contentType, responseStream]; | ||
const httpResponse = yield ((_a = this.fetchCb) !== null && _a !== void 0 ? _a : fetch)(url, init); | ||
clearTimeout(timeout); | ||
// Handle response body | ||
if (!(options === null || options === void 0 ? void 0 : options.ignoreBody) && httpResponse.body) { | ||
// Wrap WhatWG readable stream into a Node.js readable stream | ||
// If the body already is a Node.js stream (in the case of node-fetch), don't do explicit conversion. | ||
responseStream = (isStream(httpResponse.body) ? httpResponse.body : (0, readable_from_web_1.readableFromWeb)(httpResponse.body)); | ||
} | ||
// Emit an error if the server returned an invalid response | ||
if (!httpResponse.ok || (!responseStream && !(options === null || options === void 0 ? void 0 : options.ignoreBody))) { | ||
const simpleUrl = url.split('?').at(0); | ||
const bodyString = responseStream ? yield stringifyStream(responseStream) : 'empty response'; | ||
throw new Error(`Invalid SPARQL endpoint response from ${simpleUrl} (HTTP status ${httpResponse.status}):\n${bodyString}`); | ||
} | ||
// Determine the content type | ||
const contentType = (_c = (_b = httpResponse.headers.get('Content-Type')) === null || _b === void 0 ? void 0 : _b.split(';').at(0)) !== null && _c !== void 0 ? _c : ''; | ||
return [contentType, responseStream]; | ||
}); | ||
} | ||
} | ||
exports.SparqlEndpointFetcher = SparqlEndpointFetcher; | ||
SparqlEndpointFetcher.CONTENTTYPE_SPARQL_JSON = 'application/sparql-results+json'; | ||
SparqlEndpointFetcher.CONTENTTYPE_SPARQL_XML = 'application/sparql-results+xml'; | ||
SparqlEndpointFetcher.CONTENTTYPE_TURTLE = 'text/turtle'; | ||
SparqlEndpointFetcher.CONTENTTYPE_SPARQL = `${SparqlEndpointFetcher.CONTENTTYPE_SPARQL_JSON};q=1.0,${SparqlEndpointFetcher.CONTENTTYPE_SPARQL_XML};q=0.7`; | ||
SparqlEndpointFetcher.CONTENTTYPE_TURTLE = 'text/turtle'; | ||
exports.SparqlEndpointFetcher = SparqlEndpointFetcher; | ||
//# sourceMappingURL=SparqlEndpointFetcher.js.map |
134
package.json
{ | ||
"name": "fetch-sparql-endpoint", | ||
"version": "4.1.1", | ||
"version": "4.2.0", | ||
"packageManager": "yarn@1.22.22", | ||
"description": "A simple, lightweight module to send queries to SPARQL endpoints and retrieve their results in a streaming fashion.", | ||
"author": "Ruben Taelman <rubensworks@gmail.com>", | ||
"license": "MIT", | ||
"homepage": "https://github.com/rubensworks/fetch-sparql-endpoint.js#readme", | ||
"repository": "git@github.com:rubensworks/fetch-sparql-endpoint.js.git", | ||
"bugs": { | ||
"url": "https://github.com/rubensworks/fetch-sparql-endpoint.js/issues" | ||
}, | ||
"keywords": [ | ||
@@ -13,14 +21,6 @@ "sparql", | ||
], | ||
"sideEffects": false, | ||
"main": "index.js", | ||
"typings": "index", | ||
"repository": "git@github.com:rubensworks/fetch-sparql-endpoint.js.git", | ||
"author": "Ruben Taelman <rubensworks@gmail.com>", | ||
"bugs": { | ||
"url": "https://github.com/rubensworks/fetch-sparql-endpoint.js/issues" | ||
}, | ||
"homepage": "https://github.com/rubensworks/fetch-sparql-endpoint.js#readme", | ||
"license": "MIT", | ||
"bin": { | ||
"fetch-sparql-endpoint": "bin/fetch-sparql-endpoint.js" | ||
}, | ||
"bin": "bin/fetch-sparql-endpoint.js", | ||
"files": [ | ||
@@ -30,86 +30,54 @@ "bin/**/*.d.ts", | ||
"bin/**/*.js.map", | ||
"index.d.ts", | ||
"index.js", | ||
"index.js.map", | ||
"lib/**/*.d.ts", | ||
"lib/**/*.js", | ||
"lib/**/*.js.map", | ||
"index.d.ts", | ||
"index.js.map", | ||
"index.js" | ||
"lib/**/*.js.map" | ||
], | ||
"pre-commit": [ | ||
"build", | ||
"lint", | ||
"test" | ||
], | ||
"scripts": { | ||
"test": "jest", | ||
"test-watch": "jest --watch", | ||
"coveralls": "jest --coverage && cat ./coverage/lcov.info | coveralls", | ||
"lint": "eslint .", | ||
"lint:fix": "eslint . --fix", | ||
"build": "tsc", | ||
"validate": "yarn list", | ||
"version": "manual-git-changelog onversion", | ||
"prepare": "yarn husky" | ||
}, | ||
"dependencies": { | ||
"@rdfjs/types": "*", | ||
"@types/readable-stream": "^2.3.11", | ||
"@types/sparqljs": "^3.1.3", | ||
"abort-controller": "^3.0.0", | ||
"cross-fetch": "^3.0.6", | ||
"@types/n3": "^1.0.0", | ||
"@types/readable-stream": "^4.0.0", | ||
"@types/sparqljs": "^3.0.0", | ||
"is-stream": "^2.0.0", | ||
"minimist": "^1.2.0", | ||
"n3": "^1.6.3", | ||
"rdf-string": "^1.6.0", | ||
"@smessie/readable-web-to-node-stream": "^3.0.3", | ||
"sparqljs": "^3.1.2", | ||
"sparqljson-parse": "^2.2.0", | ||
"sparqlxml-parse": "^2.1.1", | ||
"stream-to-string": "^1.1.0" | ||
"n3": "^1.0.0", | ||
"rdf-string": "^1.0.0", | ||
"readable-from-web": "^1.0.0", | ||
"sparqljs": "^3.0.0", | ||
"sparqljson-parse": "^2.0.0", | ||
"sparqlxml-parse": "^2.0.0", | ||
"stream-to-string": "^1.0.0", | ||
"yargs": "^17.0.0" | ||
}, | ||
"devDependencies": { | ||
"@types/jest": "^29.5.4", | ||
"@types/minimist": "^1.2.0", | ||
"@types/n3": "^1.10.3", | ||
"@rubensworks/eslint-config": "^3.0.0", | ||
"@types/jest": "^29.0.0", | ||
"arrayify-stream": "^2.0.0", | ||
"coveralls": "^3.0.0", | ||
"jest": "^29.7.0", | ||
"jest-rdf": "^1.7.0", | ||
"eslint": "^8.0.0", | ||
"husky": "^9.0.0", | ||
"jest": "^29.0.0", | ||
"jest-rdf": "^1.0.0", | ||
"manual-git-changelog": "^1.0.0", | ||
"pre-commit": "^1.2.2", | ||
"rdf-data-factory": "^1.1.0", | ||
"streamify-string": "^1.0.1", | ||
"ts-jest": "^29.1.1", | ||
"ts-loader": "^9.3.1", | ||
"tslint": "^6.0.0", | ||
"tslint-eslint-rules": "^5.4.0", | ||
"rdf-data-factory": "^1.0.0", | ||
"readable-stream-node-to-web": "^1.0.0", | ||
"streamify-string": "^1.0.0", | ||
"ts-jest": "^29.0.0", | ||
"ts-loader": "^9.0.0", | ||
"typescript": "^5.0.0", | ||
"readable-stream-node-to-web": "^1.0.1", | ||
"webpack": "^5.73.0", | ||
"webpack-cli": "^5.0.0", | ||
"web-streams-ponyfill": "^1.4.2" | ||
}, | ||
"jest": { | ||
"transform": { | ||
"^.+\\.ts$": [ | ||
"ts-jest", | ||
{ | ||
"tsconfig": "test/tsconfig.json" | ||
} | ||
] | ||
}, | ||
"transformIgnorePatterns": [ | ||
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$" | ||
], | ||
"testRegex": "(/test/.*|(\\.|/)(test|spec))\\.ts$", | ||
"moduleFileExtensions": [ | ||
"ts", | ||
"js" | ||
], | ||
"collectCoverage": true, | ||
"setupFilesAfterEnv": [ | ||
"jest-rdf" | ||
], | ||
"testEnvironment": "node" | ||
}, | ||
"scripts": { | ||
"test": "jest ${1}", | ||
"test-watch": "jest ${1} --watch", | ||
"coveralls": "jest --coverage && cat ./coverage/lcov.info | coveralls", | ||
"lint": "tslint index.ts lib/**/*.ts test/**/*.ts --exclude '**/*.d.ts'", | ||
"build": "tsc", | ||
"validate": "npm ls", | ||
"prepare": "npm run build", | ||
"version": "manual-git-changelog onversion" | ||
}, | ||
"sideEffects": false | ||
"webpack": "^5.0.0", | ||
"webpack-cli": "^5.0.0" | ||
} | ||
} |
@@ -35,3 +35,3 @@ # Fetch SPARQL Endpoint | ||
```js | ||
import {SparqlEndpointFetcher} from "fetch-sparql-endpoint"; | ||
import { SparqlEndpointFetcher } from 'fetch-sparql-endpoint'; | ||
@@ -44,9 +44,16 @@ const myFetcher = new SparqlEndpointFetcher(); | ||
const myFetcher = new SparqlEndpointFetcher({ | ||
method: 'POST', // A custom HTTP method for issuing (non-update) queries, defaults to POST. Update queries are always issued via POST. | ||
additionalUrlParams: new URLSearchParams({'infer': 'true', 'sameAs': 'false'}); // A set of additional parameters that well be added to fetchAsk, fetchBindings & fetchTriples requests | ||
defaultHeaders: new Headers(), // Optional default headers that will be included in each request | ||
fetch: fetch, // A custom fetch-API-supporting function | ||
dataFactory: DataFactory, // A custom RDFJS data factory | ||
prefixVariableQuestionMark: false, // If variable names in bindings should be prefixed with '?', defaults to false | ||
timeout: 5000 // Timeout for setting up server connection (Once a connection has been made, and the response is being parsed, the timeout does not apply anymore). | ||
// A custom HTTP method for issuing (non-update) queries, defaults to POST. Update queries are always issued via POST. | ||
method: 'POST', | ||
// A set of additional parameters that well be added to fetchAsk, fetchBindings & fetchTriples requests | ||
additionalUrlParams: new URLSearchParams({ infer: 'true', sameAs: 'false' }), | ||
// Optional default headers that will be included in each request | ||
defaultHeaders: new Headers(), | ||
// A custom fetch-API-supporting function | ||
fetch, | ||
// A custom RDFJS data factory | ||
dataFactory: DataFactory, | ||
// If variable names in bindings should be prefixed with '?', defaults to false | ||
prefixVariableQuestionMark: false, | ||
// Timeout for setting up server connection (Once a connection has been made, and the response is being parsed, the timeout does not apply anymore). | ||
timeout: 5000, | ||
}); | ||
@@ -61,3 +68,3 @@ ``` | ||
const bindingsStream = await fetcher.fetchBindings('https://dbpedia.org/sparql', 'SELECT * WHERE { ?s ?p ?o } LIMIT 100'); | ||
bindingsStream.on('data', (bindings) => console.log(bindings)); | ||
bindingsStream.on('data', bindings => console.log(bindings)); | ||
``` | ||
@@ -78,5 +85,5 @@ | ||
const bindingsStream = await fetcher.fetchBindings('https://dbpedia.org/sparql', 'SELECT * WHERE { ?s ?p ?o } LIMIT 100'); | ||
bindingsStream.on('data', (bindings) => console.log(bindings)); | ||
bindingsStream.on('data', bindings => console.log(bindings)); | ||
// Will print [ variable('s'), variable('p'), variable('o') ] | ||
bindingsStream.on('variables', (variables) => console.log(variables)); | ||
bindingsStream.on('variables', variables => console.log(variables)); | ||
``` | ||
@@ -101,3 +108,3 @@ | ||
const tripleStream = await fetcher.fetchTriples('https://dbpedia.org/sparql', 'CONSTRUCT { ?s ?p ?o } LIMIT 100'); | ||
tripleStream.on('data', (triple) => console.log(triple)); | ||
tripleStream.on('data', triple => console.log(triple)); | ||
``` | ||
@@ -129,7 +136,10 @@ | ||
```js | ||
// Outputs 'SELECT' | ||
fetcher.getQueryType('SELECT * WHERE { ?s ?p ?o } LIMIT 100'); | ||
// Outputs 'ASK' | ||
fetcher.getQueryType('ASK WHERE { ?s ?p ?o }'); | ||
// Outputs 'CONSTRUCT' | ||
fetcher.getQueryType('CONSTRUCT { ?s ?p ?o } LIMIT 100'); | ||
``` | ||
fetcher.getQueryType('SELECT * WHERE { ?s ?p ?o } LIMIT 100'); // Outputs 'SELECT' | ||
fetcher.getQueryType('ASK WHERE { ?s ?p ?o }'); // Outputs 'ASK' | ||
fetcher.getQueryType('CONSTRUCT { ?s ?p ?o } LIMIT 100'); // Outputs 'CONSTRUCT' | ||
``` | ||
@@ -140,18 +150,27 @@ This method will also throw an error if the query contains a syntax error. | ||
A command-line tool is provided to quickly query or update any SPARQL endpoint: | ||
A command-line tool is provided to quickly query or update any SPARQL endpoint. | ||
With basic authentication, the username and password should be made available | ||
via process-scoped environment variables `SPARQL_USERNAME` and `SPARQL_PASSWORD`. | ||
Usage: | ||
``` | ||
fetch-sparql-endpoint Sends a query to a SPARQL endpoint | ||
Options: | ||
--endpoint Send the query to this SPARQL endpoint [string] [required] | ||
--query Evaluate the given SPARQL query string [string] | ||
--file Evaluate the SPARQL query in the given file [string] | ||
--get Send query via HTTP GET instead of POST [boolean] [default: false] | ||
--timeout The timeout value in seconds to finish the query [number] | ||
--auth The type of authentication to use [choices: "basic"] | ||
--version Show version number [boolean] | ||
--help Show help [boolean] | ||
Usage: | ||
fetch-sparql-endpoint https://dbpedia.org/sparql [-q] 'SELECT * WHERE { ?s ?p ?o } 100' | ||
fetch-sparql-endpoint https://dbpedia.org/sparql -f query.sparql | ||
cat query.sparql | fetch-sparql-endpoint https://dbpedia.org/sparql | ||
Options: | ||
-q evaluate the given SPARQL query string | ||
-f evaluate the SPARQL query in the given file | ||
-g send query via HTTP GET instead of POST | ||
--help print this help message | ||
Examples: | ||
fetch-sparql-endpoint.js --endpoint Fetch 100 triples from the DBPedia | ||
https://dbpedia.org/sparql --query SPARQL endpoint | ||
'SELECT * WHERE { ?s ?p ?o } LIMIT 100' | ||
fetch-sparql-endpoint.js --endpoint Run the SPARQL query from query.rq | ||
https://dbpedia.org/sparql --file against the DBPedia SPARQL endpoint | ||
query.rq | ||
cat query.rq | fetch-sparql-endpoint.js Run the SPARQL query from query.rq | ||
--endpoint https://dbpedia.org/sparql against the DBPedia SPARQL endpoint | ||
``` | ||
@@ -158,0 +177,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
62630
13
17
488
175
5
2
+ Added@types/n3@^1.0.0
+ Addedreadable-from-web@^1.0.0
+ Addedyargs@^17.0.0
+ Added@types/n3@1.21.1(transitive)
+ Added@types/readable-stream@4.0.18(transitive)
+ Addedansi-regex@5.0.1(transitive)
+ Addedansi-styles@4.3.0(transitive)
+ Addedcliui@8.0.1(transitive)
+ Addedcolor-convert@2.0.1(transitive)
+ Addedcolor-name@1.1.4(transitive)
+ Addedemoji-regex@8.0.0(transitive)
+ Addedescalade@3.2.0(transitive)
+ Addedget-caller-file@2.0.5(transitive)
+ Addedis-fullwidth-code-point@3.0.0(transitive)
+ Addedreadable-from-web@1.0.0(transitive)
+ Addedrequire-directory@2.1.1(transitive)
+ Addedstring-width@4.2.3(transitive)
+ Addedstrip-ansi@6.0.1(transitive)
+ Addedwrap-ansi@7.0.0(transitive)
+ Addedy18n@5.0.8(transitive)
+ Addedyargs@17.7.2(transitive)
+ Addedyargs-parser@21.1.1(transitive)
- Removedabort-controller@^3.0.0
- Removedcross-fetch@^3.0.6
- Removedminimist@^1.2.0
- Removed@smessie/readable-web-to-node-stream@3.0.3(transitive)
- Removedcross-fetch@3.2.0(transitive)
- Removedminimist@1.2.8(transitive)
- Removednode-fetch@2.7.0(transitive)
- Removedtr46@0.0.3(transitive)
- Removedwebidl-conversions@3.0.1(transitive)
- Removedwhatwg-url@5.0.0(transitive)
Updated@types/sparqljs@^3.0.0
Updatedn3@^1.0.0
Updatedrdf-string@^1.0.0
Updatedsparqljs@^3.0.0
Updatedsparqljson-parse@^2.0.0
Updatedsparqlxml-parse@^2.0.0
Updatedstream-to-string@^1.0.0