require 'erb' require 'net/http' require 'sparql/client' require 'uri' require_relative 'reader' require_relative 'lru_reader' require_relative 'concept' require_relative 'concept_scheme' module BELParser module Resource # SPARQLReader class SPARQLReader include Reader prepend LRUReader SCHEMES = [URI::HTTP, URI::HTTPS].freeze ALLOW_HEADER = 'Allow'.freeze def initialize(sparql_endpoint_url, validate_url = false) validate_sparql_endpoint_url(sparql_endpoint_url) if validate_url @sparql_repository = SPARQL::Client.new(sparql_endpoint_url) end def retrieve_resource(resource_identifier) uri = URI(resource_identifier.to_s) template_binding = binding sparql_query = RESOLVE_CONCEPT_SCHEME.result(template_binding) hash_to_concept_scheme(resource_identifier, execute_select(sparql_query).first) end def retrieve_value_from_resource(resource_identifier, value) uri = URI(resource_identifier.to_s) template_binding = binding sparql_query = RESOLVE_CONCEPT.result(template_binding) concept_scheme = retrieve_resource(resource_identifier) to_concept = method(:hash_to_concept).to_proc.curry[concept_scheme] concepts = execute_select(sparql_query).map(&to_concept).compact return nil if concepts.empty? concepts end def retrieve_values_from_resource(resource_identifier) uri = URI(resource_identifier.to_s) template_binding = binding sparql_query = RESOLVE_CONCEPTS.result(template_binding) concept_scheme = retrieve_resource(resource_identifier) to_concept = method(:hash_to_concept).to_proc.curry[concept_scheme] concepts = execute_select(sparql_query).map(&to_concept).compact return nil if concepts.empty? concepts end protected def execute_select(sparql_query) @sparql_repository.query(sparql_query).map do |solution| solution.to_hash end end def hash_to_concept_scheme(resource_identifier, hash) return nil if hash.empty? or hash[:types].value.empty? ConceptScheme.new(resource_identifier, *hash.values_at(:domain, :prefix, :prefLabel, :types)) end def hash_to_concept(concept_scheme, hash) return nil if hash.empty? Concept.new(concept_scheme, *hash.values_at(:concept, :prefLabel, :identifier, :title, :altLabels, :types)) end def validate_sparql_endpoint_url(url) url_s = url.to_s scheme, host, port = URI(url_s).select(:scheme, :host, :port) validate_uri_scheme(scheme, url_s) options_request = Net::HTTP::Options.new(url_s) options_response = Net::HTTP.start(host, port) do |http| http.request(Net::HTTP::Options.new(url_s)) end validate_200(options_response, url_s) validate_allowed_methods(options_response, url_s) end def validate_uri_scheme(scheme, url_s) unless SCHEMES.include?(URI.scheme_list[scheme.upcase]) raise ArgumentError, "expected HTTP or HTTPS scheme for url: #{url_s}" end end def validate_200(response, url_s) unless response.code_type == Net::HTTPOK raise ArgumentError, <<-MSG.gsub(/ {14}/, '').delete("\n") expected URL to respond 200 (received #{response.code}) for OPTIONS request to: #{url_s}" MSG end end def validate_allowed_methods(response, url_s) unless response[ALLOW_HEADER].to_s =~ /GET|POST/ raise ArgumentError, "expected URL to allow GET or POST: #{url_s}" end end RESOLVE_CONCEPT_SCHEME = ERB.new(<<-SPARQL) prefix belv: prefix dct: prefix rdf: prefix skos: select ?domain ?prefix ?prefLabel (group_concat(?type;separator='|') as ?types) where { <<%= uri %>> rdf:type skos:ConceptScheme . <<%= uri %>> rdf:type ?type . <<%= uri %>> belv:prefix ?prefix . <<%= uri %>> skos:prefLabel ?prefLabel . optional { <<%= uri %>> belv:domain ?domain . } } group by ?domain ?prefix ?prefLabel SPARQL RESOLVE_CONCEPT = ERB.new(<<-SPARQL) prefix belv: prefix dct: prefix rdf: prefix skos: select ?concept ?prefLabel ?identifier ?title (group_concat(distinct(?type);separator='|') as ?types) (group_concat(distinct(?altLabel);separator='|') as ?altLabels) where { {?concept skos:prefLabel "<%= value %>"} UNION {?concept dct:identifier "<%= value %>"} UNION {?concept dct:title "<%= value %>"} UNION {?concept skos:altLabel "<%= value %>"} ?concept skos:inScheme <<%= uri %>> . ?concept rdf:type ?type . ?concept skos:prefLabel ?prefLabel . ?concept dct:identifier ?identifier . optional { ?concept dct:title ?title . ?concept skos:altLabel ?altLabel . } } group by ?concept ?prefLabel ?identifier ?title SPARQL RESOLVE_CONCEPTS = ERB.new(<<-SPARQL) prefix belv: prefix dct: prefix rdf: prefix skos: select ?concept ?prefLabel ?identifier ?title (group_concat(distinct(?type);separator='|') as ?types) (group_concat(distinct(?altLabel);separator='|') as ?altLabels) where { ?concept skos:inScheme <<%= uri %>> . ?concept rdf:type ?type . ?concept skos:prefLabel ?prefLabel . optional { ?concept dct:identifier ?identifier . ?concept dct:title ?title . ?concept skos:altLabel ?altLabel } } group by ?concept ?prefLabel ?identifier ?title SPARQL end end end