import { objectValues , getOrCreateArrayInMap , } from '../../viewUtils/collections' ; import { DataProvider , LinkElementsParams , FilterParams } from '../provider' ; import { Dictionary , ClassModel , LinkType , ElementModel , LinkModel , LinkCount , PropertyModel , ElementIri , ElementTypeIri , LinkTypeIri , PropertyTypeIri , LocalizedString , } from '../model' ; import { prependAdditionalBindings , enrichElementsWithImages , flattenClassTree , getClassTree , getClassInfo , getPropertyInfo , getLinkTypes , getElementsInfo , getElementTypes , getLinksInfo , getLinksTypeIds , getFilteredData , getLinksTypesOf , getLinkStatistics , triplesToElementBinding , isDirectLink , isDirectProperty , } from './responseHandler' ; import { ClassBinding , ElementBinding , LinkBinding , PropertyBinding , BlankBinding , FilterBinding , LinkCountBinding , LinkTypeBinding , ElementImageBinding , ElementTypeBinding , SparqlResponse , Triple , } from './sparqlModels' ; import { SparqlDataProviderSettings , OWLStatsSettings , LinkConfiguration , PropertyConfiguration , } from './sparqlDataProviderSettings' ; import * as BlankNodes from './blankNodes' ; import { parseTurtleText } from './turtle' ; export enum SparqlQueryMethod { GET = 1 , POST } export type QueryFunction = ( params : { url : string ; body ? : string ; headers : { [ header ] : string ; } ; method : string ; } ) => Promise < Response > ; /**
 * Runtime settings of SPARQL data provider
 */ export interface SparqlDataProviderOptions { /**
     * If it's true then blank nodes will be present on the paper
     * By default blank nodes wont be shown
     */ acceptBlankNodes ? : boolean ; /**
     *  sparql endpoint URL to use
     */ endpointUrl : string ; /**
     * properties to use as image URLs
     */ imagePropertyUris ? : string [ ] ; /**
     * Allows to extract/fetch image URLs externally instead of using `imagePropertyUris` option.
     */ prepareImages ? : ( elementInfo : Dictionary < ElementModel > ) => Promise < Dictionary < string > > ; /**
     * Allows to extract/fetch labels separately from SPARQL query as an alternative or
     * in addition to `label` output binding.
     */ prepareLabels ? : ( resources : Set < string > ) => Promise < Map < string , LocalizedString [ ] > > ; /**
     * wether to use GET (more compatible (Virtuozo), more error-prone due to large request URLs)
     * or POST(less compatible, better on large data sets)
     */ queryMethod ? : SparqlQueryMethod ; queryFunction ? : QueryFunction ; } export class SparqlDataProvider implements DataProvider { readonly options : SparqlDataProviderOptions ; readonly settings : SparqlDataProviderSettings ; private linkByPredicate = new Map < string , LinkConfiguration [ ] > ( ) ; private linkById = new Map < LinkTypeIri , LinkConfiguration > ( ) ; private openWorldLinks : boolean ; private propertyByPredicate = new Map < string , PropertyConfiguration [ ] > ( ) ; private openWorldProperties : boolean ; constructor ( options , settings = OWLStatsSettings ) { const { queryFunction = queryInternal } = options ; this . options = { ... options , queryFunction } ; this . settings = settings ; for ( const link of settings . linkConfigurations ) { this . linkById . set ( link . id as LinkTypeIri , link ) ; const predicate = isDirectLink ( link ) ? link . path : link . id ; getOrCreateArrayInMap ( this . linkByPredicate , predicate ) . push ( link ) ; } this . openWorldLinks = settings . linkConfigurations . length === 0 || Boolean ( settings . openWorldLinks ) ; for ( const property of settings . propertyConfigurations ) { const predicate = isDirectProperty ( property ) ? property . path : property . id ; getOrCreateArrayInMap ( this . propertyByPredicate , predicate ) . push ( property ) ; } this . openWorldProperties = settings . propertyConfigurations . length === 0 || Boolean ( settings . openWorldProperties ) ; } async classTree ( ) { const { defaultPrefix , schemaLabelProperty , classTreeQuery , } = this . settings ; if ( ! classTreeQuery ) { return [ ] ; } const query = defaultPrefix + resolveTemplate ( classTreeQuery , { schemaLabelProperty , } ) ; const result = await this . executeSparqlQuery < ClassBinding > ( query ) ; const classTree = getClassTree ( result ) ; if ( this . options . prepareLabels ) { await attachLabels ( flattenClassTree ( classTree ) , this . options . prepareLabels ) ; } return classTree ; } async propertyInfo ( params ) { const { defaultPrefix , schemaLabelProperty , propertyInfoQuery , } = this . settings ; let properties ; if ( propertyInfoQuery ) { const ids = params . propertyIds . map ( escapeIri ) . map ( ( id ) => ` ( ${ id } )` ) . join ( ' ' ) ; const query = defaultPrefix + resolveTemplate ( propertyInfoQuery , { ids , schemaLabelProperty , } ) ; const result = await this . executeSparqlQuery < PropertyBinding > ( query ) ; properties = getPropertyInfo ( result ) ; } else { properties = { } ; for ( const id of params . propertyIds ) { properties [ id ] = { id , label : { values : [ ] } } ; } } if ( this . options . prepareLabels ) { await attachLabels ( objectValues ( properties ) , this . options . prepareLabels ) ; } return properties ; } async classInfo ( params ) { const { defaultPrefix , schemaLabelProperty , classInfoQuery , } = this . settings ; let classes ; if ( classInfoQuery ) { const ids = params . classIds . map ( escapeIri ) . map ( ( id ) => ` ( ${ id } )` ) . join ( ' ' ) ; const query = defaultPrefix + resolveTemplate ( classInfoQuery , { ids , schemaLabelProperty , } ) ; const result = await this . executeSparqlQuery < ClassBinding > ( query ) ; classes = getClassInfo ( result ) ; } else { classes = params . classIds . map ( ( id ) : ClassModel => ( { id , label : { values : [ ] } , children : [ ] } ) ) ; } if ( this . options . prepareLabels ) { await attachLabels ( classes , this . options . prepareLabels ) ; } return classes ; } async linkTypesInfo ( params ) { const { defaultPrefix , schemaLabelProperty , linkTypesInfoQuery , } = this . settings ; let linkTypes ; if ( linkTypesInfoQuery ) { const ids = params . linkTypeIds . map ( escapeIri ) . map ( ( id ) => ` ( ${ id } )` ) . join ( ' ' ) ; const query = defaultPrefix + resolveTemplate ( linkTypesInfoQuery , { ids , schemaLabelProperty , } ) ; const result = await this . executeSparqlQuery < LinkTypeBinding > ( query ) ; linkTypes = getLinkTypes ( result ) ; } else { linkTypes = params . linkTypeIds . map ( ( id ) : LinkType => ( { id , label : { values : [ ] } } ) ) ; } if ( this . options . prepareLabels ) { await attachLabels ( linkTypes , this . options . prepareLabels ) ; } return linkTypes ; } async linkTypes ( ) { const { defaultPrefix , schemaLabelProperty , linkTypesQuery , linkTypesPattern , } = this . settings ; if ( ! linkTypesQuery ) { return [ ] ; } const query = defaultPrefix + resolveTemplate ( linkTypesQuery , { linkTypesPattern , schemaLabelProperty , } ) ; const result = await this . executeSparqlQuery < LinkTypeBinding > ( query ) ; const linkTypes = getLinkTypes ( result ) ; if ( this . options . prepareLabels ) { await attachLabels ( linkTypes , this . options . prepareLabels ) ; } return linkTypes ; } async elementInfo ( params ) { const nonBlankResources = params . elementIds . filter ( ( id ) => ! BlankNodes . isEncodedBlank ( id ) ) ; const blankNodeResponse = this . options . acceptBlankNodes ? BlankNodes . elementInfo ( params . elementIds ) : undefined ; let triples ; if ( nonBlankResources . length > 0 ) { const ids = nonBlankResources . map ( escapeIri ) . map ( ( id ) => ` (${ id })` ) . join ( ' ' ) ; const { defaultPrefix , dataLabelProperty , elementInfoQuery , } = this . settings ; const query = defaultPrefix + resolveTemplate ( elementInfoQuery , { ids , dataLabelProperty , propertyConfigurations : this . formatPropertyInfo ( ) , } ) ; triples = await this . executeSparqlConstruct ( query ) ; } else { triples = [ ] ; } const types = this . queryManyElementTypes ( this . settings . propertyConfigurations . length > 0 ? params . elementIds : [ ] ) ; const bindings = triplesToElementBinding ( triples ) ; const bindingsWithBlanks = prependAdditionalBindings ( bindings , blankNodeResponse ) ; const elementModels = getElementsInfo ( bindingsWithBlanks , await types , this . propertyByPredicate , this . openWorldProperties ) ; if ( this . options . prepareLabels ) { await attachLabels ( objectValues ( elementModels ) , this . options . prepareLabels ) ; } if ( this . options . prepareImages ) { await prepareElementImages ( this . options . prepareImages , elementModels ) ; } else if ( this . options . imagePropertyUris && this . options . imagePropertyUris . length ) { await this . attachImages ( elementModels , this . options . imagePropertyUris ) ; } return elementModels ; } private async attachImages ( elementsInfo , types ) { const ids = Object . keys ( elementsInfo ) . filter ( ( id ) => ! BlankNodes . isEncodedBlank ( id ) ) . map ( escapeIri ) . map ( ( id ) => ` ( ${ id } )` ) . join ( ' ' ) ; const typesString = types . map ( escapeIri ) . map ( ( id ) => ` ( ${ id } )` ) . join ( ' ' ) ; const query = this . settings . defaultPrefix + `
            SELECT ?inst ?linkType ?image
            WHERE {{
                VALUES (?inst) {${ ids }}
                VALUES (?linkType) {${ typesString }}
                ${ this . settings . imageQueryPattern }
            }}
        ` ; try { const bindings = await this . executeSparqlQuery < ElementImageBinding > ( query ) ; enrichElementsWithImages ( bindings , elementsInfo ) ; } catch ( err ) { console . error ( err ) ; } } async linksInfo ( params ) { const nonBlankResources = params . elementIds . filter ( ( id ) => ! BlankNodes . isEncodedBlank ( id ) ) ; const blankNodeResponse = this . options . acceptBlankNodes ? BlankNodes . linksInfo ( params . elementIds ) : undefined ; const linkConfigurations = this . formatLinkLinks ( ) ; let bindings ; let types ; if ( nonBlankResources . length > 0 ) { const ids = nonBlankResources . map ( escapeIri ) . map ( ( id ) => ` ( ${ id } )` ) . join ( ' ' ) ; const linksInfoQuery = this . settings . defaultPrefix + resolveTemplate ( this . settings . linksInfoQuery , { ids , linkConfigurations , } ) ; bindings = this . executeSparqlQuery < LinkBinding > ( linksInfoQuery ) ; types = this . queryManyElementTypes ( params . elementIds ) ; } else { bindings = Promise . resolve ( { head : { vars : [ ] } , results : { bindings : [ ] } , } ) ; types = this . queryManyElementTypes ( [ ] ) ; } const bindingsWithBlanks = prependAdditionalBindings ( await bindings , blankNodeResponse ) ; const linksInfo = getLinksInfo ( bindingsWithBlanks , await types , this . linkByPredicate , this . openWorldLinks ) ; return linksInfo ; } async linkTypesOf ( params ) { if ( this . options . acceptBlankNodes && BlankNodes . isEncodedBlank ( params . elementId ) ) { return Promise . resolve ( getLinksTypesOf ( BlankNodes . linkTypesOf ( params ) ) ) ; } const { defaultPrefix , linkTypesOfQuery , linkTypesStatisticsQuery , filterTypePattern , } = this . settings ; const elementIri = escapeIri ( params . elementId ) ; const forAll = this . formatLinkUnion ( params . elementId , undefined , undefined , '?outObject' , '?inObject' , false ) ; if ( forAll . usePredicatePart ) { forAll . unionParts . push ( `{ ${ elementIri } ?link ?outObject }` ) ; forAll . unionParts . push ( `{ ?inObject ?link ${ elementIri } }` ) ; } const query = defaultPrefix + resolveTemplate ( linkTypesOfQuery , { elementIri , linkConfigurations : forAll . unionParts . join ( '\nUNION\n' ) , } ) ; const linkTypeBindings = await this . executeSparqlQuery < LinkTypeBinding > ( query ) ; const linkTypeIds = getLinksTypeIds ( linkTypeBindings , this . linkByPredicate , this . openWorldLinks ) ; const navigateElementFilterOut = this . options . acceptBlankNodes ? `FILTER (IsIri(?outObject) || IsBlank(?outObject))` : `FILTER IsIri(?outObject)` ; const navigateElementFilterIn = this . options . acceptBlankNodes ? `FILTER (IsIri(?inObject) || IsBlank(?inObject))` : `FILTER IsIri(?inObject)` ; const foundLinkStats = [ ] ; await Promise . all ( linkTypeIds . map ( async ( linkId ) => { const linkConfig = this . linkById . get ( linkId ) ; let linkConfigurationOut ; let linkConfigurationIn ; if ( ! linkConfig || isDirectLink ( linkConfig ) ) { const predicate = escapeIri ( linkConfig && isDirectLink ( linkConfig ) ? linkConfig . path : linkId ) ; linkConfigurationOut = `${ elementIri } ${ predicate } ?outObject` ; linkConfigurationIn = `?inObject ${ predicate } ${ elementIri }` ; } else { linkConfigurationOut = this . formatLinkPath ( linkConfig . path , elementIri , '?outObject' ) ; linkConfigurationIn = this . formatLinkPath ( linkConfig . path , '?inObject' , elementIri ) ; } if ( linkConfig && linkConfig . domain ?. length > 0 ) { const commaSeparatedDomains = linkConfig . domain . map ( escapeIri ) . join ( ', ' ) ; const restrictionOut = filterTypePattern . replace ( /[?$]inst\b/g , elementIri ) ; const restrictionIn = filterTypePattern . replace ( /[?$]inst\b/g , '?inObject' ) ; linkConfigurationOut += ` { ${ restrictionOut } FILTER(?class IN (${ commaSeparatedDomains })) }` ; linkConfigurationIn += ` { ${ restrictionIn } FILTER(?class IN (${ commaSeparatedDomains })) }` ; } const statsQuery = defaultPrefix + resolveTemplate ( linkTypesStatisticsQuery , { linkId : escapeIri ( linkId ) , elementIri , linkConfigurationOut , linkConfigurationIn , navigateElementFilterOut , navigateElementFilterIn , } ) ; const bindings = await this . executeSparqlQuery < LinkCountBinding > ( statsQuery ) ; const linkStats = getLinkStatistics ( bindings ) ; if ( linkStats ) { foundLinkStats . push ( linkStats ) ; } } ) ) ; return foundLinkStats ; } linkElements ( params ) { return this . filter ( { refElementId : params . elementId , refElementLinkId : params . linkId , linkDirection : params . direction , limit : params . limit , offset : params . offset , languageCode : '' , } ) ; } async filter ( baseParams ) { const params = { ... baseParams } ; if ( params . limit === undefined ) { params . limit = 100 ; } const types = this . querySingleElementTypes ( params . refElementId && this . settings . linkConfigurations . length > 0 ? params . refElementId : undefined ) ; const blankFiltration = this . options . acceptBlankNodes ? BlankNodes . filter ( params ) : undefined ; if ( blankFiltration && blankFiltration . results . bindings . length > 0 ) { return getFilteredData ( blankFiltration , await types , this . linkByPredicate , this . openWorldLinks ) ; } const filterQuery = this . createFilterQuery ( params ) ; const bindings = await this . executeSparqlQuery < ElementBinding & FilterBinding > ( filterQuery ) ; let bindingsWithBlanks ; if ( this . options . acceptBlankNodes ) { bindingsWithBlanks = await BlankNodes . updateFilterResults ( bindings , ( blankQuery ) => this . executeSparqlQuery < BlankBinding > ( blankQuery ) , this . settings ) ; } else { bindingsWithBlanks = bindings as SparqlResponse < ElementBinding & FilterBinding > ; } const elementModels = getFilteredData ( bindingsWithBlanks , await types , this . linkByPredicate , this . openWorldLinks ) ; if ( this . options . prepareLabels ) { await attachLabels ( objectValues ( elementModels ) , this . options . prepareLabels ) ; } return elementModels ; } private createFilterQuery ( params ) { if ( ! params . refElementId && params . refElementLinkId ) { throw new Error ( 'Cannot execute refElementLink filter without refElement' ) ; } let outerProjection = '?inst ?class ?label ?blankType' ; let innerProjection = '?inst' ; let refQueryPart = '' ; let refQueryTypes = '' ; if ( params . refElementId ) { outerProjection += ' ?link ?direction' ; innerProjection += ' ?link ?direction' ; refQueryPart = this . createRefQueryPart ( { elementId : params . refElementId , linkId : params . refElementLinkId , direction : params . linkDirection , } ) ; if ( this . settings . linkConfigurations . length > 0 ) { outerProjection += ' ?classAll' ; refQueryTypes = this . settings . filterTypePattern . replace ( /[?$]class\b/g , '?classAll' ) ; } } let elementTypePart = '' ; if ( params . elementTypeId ) { const elementTypeIri = escapeIri ( params . elementTypeId ) ; elementTypePart = this . settings . filterTypePattern . replace ( /[?$]class\b/g , elementTypeIri ) ; elementTypePart += ' .' ; } const { defaultPrefix , fullTextSearch , dataLabelProperty } = this . settings ; let textSearchPart = '' ; if ( params . text ) { innerProjection += ' ?score' ; if ( this . settings . fullTextSearch . extractLabel ) { textSearchPart += sparqlExtractLabel ( '?inst' , '?extractedLabel' ) ; } textSearchPart = resolveTemplate ( fullTextSearch . queryPattern , { text : params . text , dataLabelProperty , } ) ; } const blankNodes = this . options . acceptBlankNodes ; if ( blankNodes ) { outerProjection += ` ${ BlankNodes . BLANK_NODE_QUERY_PARAMETERS }` ; } return `${ defaultPrefix }
            ${ fullTextSearch . prefix }

        SELECT ${ outerProjection }
        WHERE {
            {
                SELECT DISTINCT ${ innerProjection } WHERE {
                    ${ elementTypePart }
                    ${ refQueryPart }
                    ${ textSearchPart }
                    ${ this . settings . filterAdditionalRestriction }
                }
                ${ textSearchPart ? 'ORDER BY DESC(?score)' : '' }
                LIMIT ${ params . limit } OFFSET ${ params . offset }
            }
            ${ refQueryTypes }
            ${ resolveTemplate ( this . settings . filterElementInfoPattern , { dataLabelProperty , } ) }
            ${ blankNodes ? BlankNodes . BLANK_NODE_QUERY : '' }
        } ${ textSearchPart ? 'ORDER BY DESC(?score)' : '' }
        ` ; } executeSparqlQuery < Binding > ( query ) { const method = this . options . queryMethod ? this . options . queryMethod : SparqlQueryMethod . GET ; return executeSparqlQuery < Binding > ( this . options . endpointUrl , query , method , this . options . queryFunction ) ; } executeSparqlConstruct ( query ) { const method = this . options . queryMethod ? this . options . queryMethod : SparqlQueryMethod . GET ; return executeSparqlConstruct ( this . options . endpointUrl , query , method , this . options . queryFunction ) ; } protected createRefQueryPart ( params ) { const { elementId , linkId , direction } = params ; const { unionParts , usePredicatePart } = this . formatLinkUnion ( elementId , linkId , direction , '?inst' , '?inst' , true ) ; if ( usePredicatePart ) { const refElementIRI = escapeIri ( params . elementId ) ; let refLinkType ; if ( linkId ) { const link = this . linkById . get ( linkId ) ; refLinkType = link && isDirectLink ( link ) ? escapeIri ( link . path ) : escapeIri ( linkId ) ; } const linkPattern = refLinkType || '?link' ; const bindType = refLinkType ? `BIND(${ refLinkType } as ?link)` : '' ; const blankFilter = this . options . acceptBlankNodes ? 'FILTER(isIri(?inst) || isBlank(?inst))' : 'FILTER(isIri(?inst))' ; if ( ! direction || direction === 'out' ) { unionParts . push ( `{ ${ refElementIRI } ${ linkPattern } ?inst BIND("out" as ?direction) ${ bindType } ${ blankFilter } }` ) ; } if ( ! direction || direction === 'in' ) { unionParts . push ( `{ ?inst ${ linkPattern } ${ refElementIRI } BIND("in" as ?direction) ${ bindType } ${ blankFilter } }` ) ; } } let resultPattern = unionParts . length === 0 ? 'FILTER(false)' : unionParts . join ( `\nUNION\n` ) ; const useAllLinksPattern = ! linkId && this . settings . filterRefElementLinkPattern . length > 0 ; if ( useAllLinksPattern ) { resultPattern += `\n${ this . settings . filterRefElementLinkPattern }` ; } return resultPattern ; } private formatLinkUnion ( refElementIri , linkIri , direction , outElementVar , inElementVar , bindDirection ) { const { linkConfigurations } = this . settings ; const fixedIri = escapeIri ( refElementIri ) ; const unionParts = [ ] ; let hasDirectLink = false ; for ( const link of linkConfigurations ) { if ( linkIri && link . id !== linkIri ) { continue ; } if ( isDirectLink ( link ) ) { hasDirectLink = true ; } else { const linkType = escapeIri ( link . id ) ; if ( ! direction || direction === 'out' ) { const path = this . formatLinkPath ( link . path , fixedIri , outElementVar ) ; const boundedDirection = bindDirection ? `BIND("out" as ?direction) ` : '' ; unionParts . push ( `{ ${ path } BIND(${ linkType } as ?link) ${ boundedDirection }}` ) ; } if ( ! direction || direction === 'in' ) { const path = this . formatLinkPath ( link . path , inElementVar , fixedIri ) ; const boundedDirection = bindDirection ? `BIND("in" as ?direction) ` : '' ; unionParts . push ( `{ ${ path } BIND(${ linkType } as ?link) ${ boundedDirection }}` ) ; } } } const usePredicatePart = this . openWorldLinks || hasDirectLink ; return { unionParts , usePredicatePart } ; } formatLinkLinks ( ) { const unionParts = [ ] ; let hasDirectLink = false ; for ( const link of this . settings . linkConfigurations ) { if ( isDirectLink ( link ) ) { hasDirectLink = true ; } else { const linkType = escapeIri ( link . id ) ; unionParts . push ( `{ ${ this . formatLinkPath ( link . path , '?source' , '?target' ) } BIND(${ linkType } as ?type) }` ) ; } } const usePredicatePart = this . openWorldLinks || hasDirectLink ; if ( usePredicatePart ) { unionParts . push ( `{ ?source ?type ?target }` ) ; } return unionParts . join ( '\nUNION\n' ) ; } formatLinkPath ( path , source , target ) { return path . replace ( /[?$]source\b/g , source ) . replace ( /[?$]target\b/g , target ) ; } formatPropertyInfo ( ) { const unionParts = [ ] ; let hasDirectProperty = false ; for ( const property of this . settings . propertyConfigurations ) { if ( isDirectProperty ( property ) ) { hasDirectProperty = true ; } else { const propType = escapeIri ( property . id ) ; const formatted = this . formatPropertyPath ( property . path , '?inst' , '?propValue' ) ; unionParts . push ( `{ ${ formatted } BIND(${ propType } as ?propType) }` ) ; } } const usePredicatePart = this . openWorldProperties || hasDirectProperty ; if ( usePredicatePart ) { unionParts . push ( `{ ?inst ?propType ?propValue }` ) ; } return unionParts . join ( '\nUNION\n' ) ; } formatPropertyPath ( path , subject , value ) { return path . replace ( /[?$]inst\b/g , subject ) . replace ( /[?$]value\b/g , value ) ; } private async querySingleElementTypes ( element ) { const types = await this . queryManyElementTypes ( element ? [ element ] : [ ] ) ; return types . get ( element ) ; } private async queryManyElementTypes ( elements ) { if ( elements . length === 0 ) { return new Map ( ) ; } const { filterTypePattern } = this . settings ; const ids = elements . filter ( ( iri ) => ! BlankNodes . isEncodedBlank ( iri ) ) . map ( ( iri ) => `(${ escapeIri ( iri ) })` ) . join ( ' ' ) ; const queryTemplate = this . settings . defaultPrefix + `SELECT ?inst ?class { VALUES(?inst) { \${ids} } \${filterTypePattern} }` ; const query = resolveTemplate ( queryTemplate , { ids , filterTypePattern } ) ; let response = await this . executeSparqlQuery < ElementTypeBinding > ( query ) ; if ( this . options . acceptBlankNodes && elements . find ( BlankNodes . isEncodedBlank ) ) { const blankResponse = BlankNodes . getElementTypes ( elements ) ; response = prependAdditionalBindings ( response , blankResponse ) ; } return getElementTypes ( response ) ; } } interface LabeledItem { id : string ; label : { values : LocalizedString [ ] ; } ; } async function attachLabels ( items , fetchLabels ) { const resources = new Set < string > ( ) ; for ( const item of items ) { if ( BlankNodes . isEncodedBlank ( item . id ) ) { continue ; } resources . add ( item . id ) ; } const labels = await fetchLabels ( resources ) ; for ( const item of items ) { if ( labels . has ( item . id ) ) { item . label = { values : labels . get ( item . id ) } ; } } } function prepareElementImages ( fetchImages , elementsInfo ) { return fetchImages ( elementsInfo ) . then ( ( images ) => { for ( const iri in images ) { if ( Object . prototype . hasOwnProperty . call ( images , iri ) && elementsInfo [ iri ] ) { elementsInfo [ iri ] . image = images [ iri ] ; } } } ) ; } function resolveTemplate ( template , values ) { let result = template ; for ( const replaceKey in values ) { if ( ! values . hasOwnProperty ( replaceKey ) ) { continue ; } const replaceValue = values [ replaceKey ] ; if ( replaceValue ) { result = result . replace ( new RegExp ( '\\${' + replaceKey + '}' , 'g' ) , replaceValue ) ; } } return result ; } export function executeSparqlQuery < Binding > ( endpoint , query , method , queryFunction ) { let internalQuery ; if ( method === SparqlQueryMethod . GET ) { internalQuery = queryFunction ( { url : appendQueryParams ( endpoint , { query } ) , headers : { Accept : 'application/sparql-results+json' , } , method : 'GET' , } ) ; } else { internalQuery = queryFunction ( { url : endpoint , body : query , headers : { Accept : 'application/sparql-results+json' , 'Content-Type' : 'application/sparql-query; charset=UTF-8' , } , method : 'POST' , } ) ; } return internalQuery . then ( ( response ) : Promise < SparqlResponse < Binding > > => { if ( response . ok ) { return response . json ( ) ; } else { const error = new Error ( response . statusText ) ; ( error as any ) . response = response ; throw error ; } } ) ; } export function executeSparqlConstruct ( endpoint , query , method , queryFunction ) { let internalQuery ; if ( method === SparqlQueryMethod . GET ) { internalQuery = queryFunction ( { url : appendQueryParams ( endpoint , { query } ) , headers : { Accept : 'text/turtle' , } , method : 'GET' , } ) ; } else { internalQuery = queryFunction ( { url : endpoint , body : query , headers : { Accept : 'text/turtle' , 'Content-Type' : 'application/sparql-query; charset=UTF-8' , } , method : 'POST' , } ) ; } return internalQuery . then ( ( response ) => { if ( response . ok ) { return response . text ( ) ; } else { const error = new Error ( response . statusText ) ; ( error as any ) . response = response ; throw error ; } } ) . then ( parseTurtleText ) ; } function appendQueryParams ( endpoint , queryParams = { } ) { const initialSeparator = endpoint . indexOf ( '?' ) < 0 ? '?' : '&' ; const additionalParams = initialSeparator + Object . keys ( queryParams ) . map ( ( key ) => `${ key }=${ encodeURIComponent ( queryParams [ key ] ) }` ) . join ( '&' ) ; return endpoint + additionalParams ; } function queryInternal ( params ) { return fetch ( params . url , { method : params . method , body : params . body , credentials : 'same-origin' , mode : 'cors' , cache : 'default' , headers : params . headers , } ) ; } function sparqlExtractLabel ( subject , label ) { return `
        BIND ( str( ${ subject } ) as ?uriStr)
        BIND ( strafter(?uriStr, "#") as ?label3)
        BIND ( strafter(strafter(?uriStr, "//"), "/") as ?label6)
        BIND ( strafter(?label6, "/") as ?label5)
        BIND ( strafter(?label5, "/") as ?label4)
        BIND (if (?label3 != "", ?label3,
            if (?label4 != "", ?label4,
            if (?label5 != "", ?label5, ?label6))) as ${ label })
    ` ; } function escapeIri ( iri ) { if ( typeof iri !== 'string' ) { throw new Error ( `Cannot escape IRI of type "${ typeof iri }"` ) ; } return `<${ iri }>` ; }