UNPKG

sparnatural

Version:

Visual client-side SPARQL query builder and knowledge graph exploration tool

221 lines (186 loc) 6.79 kB
import LocalCacheData from "../../../datastorage/LocalCacheData"; import { Catalog } from "../../../settings/Catalog"; import { UrlFetcher } from "./UrlFetcher"; export interface SparqlHandlerIfc { executeSparql( sparql:string, callback: (data: any) => void, errorCallback?:(error: any) => void ):void; } export class SparqlHandlerFactory { protected lang:string; protected localCacheDataTtl:any; protected extraHeaders:Map<string,string>; protected customizedSparqlHandler:SparqlHandlerIfc; protected catalog?:Catalog; constructor( lang:string, localCacheDataTtl:any, extraHeaders:Map<string,string>, customizedSparqlHandler?:SparqlHandlerIfc, catalog?:Catalog ) { this.lang = lang; this.localCacheDataTtl = localCacheDataTtl; this.extraHeaders = extraHeaders; this.customizedSparqlHandler = customizedSparqlHandler; this.catalog = catalog; } buildSparqlHandler(endpoints:string[]):SparqlHandlerIfc { // if customized handler, use it if(this.customizedSparqlHandler) { return this.customizedSparqlHandler; } // if more than one endpoint if(endpoints.length > 1) { // extract selected endpoints from full catalog let subCatalog = this.catalog.extractSubCatalog(endpoints); return new MultipleEndpointSparqlHandler( new UrlFetcher(this.localCacheDataTtl, this.extraHeaders), subCatalog, this.lang ); } else { // only one single endpoint let endpoint:string = endpoints[0] return new EndpointSparqlHandler(new UrlFetcher(this.localCacheDataTtl, this.extraHeaders), endpoint); } } } /** * Executes a SPARQL query against a remote endpoint at a known URL */ export class EndpointSparqlHandler implements SparqlHandlerIfc { urlFetcher:UrlFetcher; sparqlEndpointUrl: any; constructor( urlFetcher:UrlFetcher, sparqlEndpointUrl: any ) { this.urlFetcher = urlFetcher, this.sparqlEndpointUrl = sparqlEndpointUrl; } buildUrl(sparql:string):string { var separator = this.sparqlEndpointUrl.indexOf("?") > 0 ? "&" : "?"; var url = this.sparqlEndpointUrl + separator + this.buildParameters(sparql) return url; } buildParameters(sparql:string):string { return "query=" + encodeURIComponent(sparql) + "&format=json"; } executeSparqlPost( sparql:string, callback: (data: {}) => void, errorCallback?:(error: any) => void ):void { let url = this.sparqlEndpointUrl; const headers = new Headers(); headers.append("Content-Type", "application/x-www-form-urlencoded"); headers.append("Accept", "application/sparql-results+json,*/*;q=0.9"); return this.urlFetcher.fetchUrlWithParameters( url, { method:"POST", body:this.buildParameters(sparql), headers: headers }, callback, errorCallback ); } executeSparqlGet( sparql:string, callback: (data: {}) => void, errorCallback?:(error: any) => void ):void { let url = this.buildUrl(sparql); this.urlFetcher.fetchUrl( url, callback, errorCallback ); } executeSparql( sparql:string, callback: (data: {}) => void, errorCallback?:(error: any) => void ):void { if(sparql.length < 1024) this.executeSparqlGet(sparql,callback,errorCallback); else this.executeSparqlPost(sparql,callback,errorCallback); } } export class MultipleEndpointSparqlHandler implements SparqlHandlerIfc { urlFetcher:UrlFetcher; catalog: Catalog; addExtraEndpointColumn:boolean; // name of the extra column to add in the result set to express the endpoint extraColumnName = "group"; lang:string; constructor( urlFetcher:UrlFetcher, catalog: Catalog, lang:string ) { this.urlFetcher = urlFetcher, this.catalog = catalog; this.addExtraEndpointColumn = true; this.lang = lang; } executeSparql( sparql:string, callback: (data: any) => void, errorCallback?:(error: any) => void ):void { const promises:Promise<{}>[] = []; for(const i in this.catalog.getServices()) { console.log("Calling "+this.catalog.getServices()[i].getEndpointURL()) let fetcher = new EndpointSparqlHandler(this.urlFetcher, this.catalog.getServices()[i].getEndpointURL()); promises[promises.length] = new Promise((resolve, reject) => { fetcher.executeSparql( sparql, (data: any) =>{resolve({ endpoint: this.catalog.getServices()[i], sparqlResult: data })}, (error: any)=>{reject(error)} ) }); } // then wait for all Promises Promise.all(promises).then((values:any[]) => { let finalResult:any = {}; // copy the same head as first result, with an extra "endpoint" column finalResult.head = values[0].sparqlResult.head; finalResult.head.vars.push(this.extraColumnName); // prepare the "results" section finalResult.results = { // same distinct as first result distinct: values[0].sparqlResult.results.distinct, // never ordered ordered: false, // prepare bindings section bindings: [] }; // then for each SPARQL results of structure {endpoint : xx, sparqlJson: {...}} for (const v of values) { // add an extra "endpoint" column with the endpoint at the end of each binding finalResult.results.bindings.push( // remap each binding to add the endpoint column at the end // then unpack the array ...v.sparqlResult.results.bindings.map((b: { [x: string]: { type: string; value: any; }; }) => { if(this.addExtraEndpointColumn) { b[this.extraColumnName] = {type: "literal", value:v.endpoint.getTitle(this.lang)}; } return b; }) ); } // TODO : handle errors // and then call the callback callback(finalResult); }); } }