Module: SPARQL::Server

Defined in:
lib/sparql/server.rb

Class Method Summary collapse

Class Method Details

.application(dataset: RDF::Repository.new, **options) ⇒ Sinatra::Base

A SPARQL Protocol and Graph Store server.

Note, this is a trivial server and implementations may consider implementing integrating it into their own Rack or Sinatra server using Rack::SPARQL or Sinatra::SPARQL.

Implements SPARQL 1.1 Protocol and SPARQL 1.1 Graph Store HTTP Protocol.

Protocol-specified dataset parameters create a merged repository as described in Sinatra::SPARQL::Helpers#dataset and Algebra::Operator::Dataset.

Options Hash (**options):

  • :host_authorization (Hash)

    A hash of options to host_authorization, to be used by the Rack::Protection::HostAuthorization middleware.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/sparql/server.rb', line 19

def application(dataset: RDF::Repository.new, **options)
  Sinatra.new do
    register Sinatra::SPARQL
    set :repository, dataset
    enable :logging
    disable :raise_errors, :show_exceptions if settings.production?

    if options[:host_authorization]
      set :host_authorization, options[:host_authorization]
    end

    mime_type :jsonld, "application/ld+json"
    mime_type :normalize, "application/normalized+n-quads"
    mime_type :sparql, "application/sparql-query"
    mime_type :ttl, "text/turtle"
    mime_type :sse, "application/sse+sparql-query"

    configure :development, :test do
      set :logging, 0
    end

    get '/' do
      opts = params.inject({}) {|memo, (k,v)| memo.merge(k.to_sym => v)}
      if opts[:query]
        query = opts[:query]
        halt 403, "Update not possible using GET" if opts[:update]
        repo = dataset(logger: request.logger, **opts)
        url = RDF::URI(request.url).tap {|u| u.query = nil}
        query = begin
          SPARQL.parse(query, base_uri: url)
        rescue SPARQL::Grammar::Parser::Error => e
          halt 400, "Error parsing query: #{e.message}"
        end
        res = query.execute(repo,
                            logger: request.logger,
                            **options.merge(opts))
        res.is_a?(RDF::Literal::Boolean) ? [res] : res
      elsif opts[:update]
        halt 406, "Inappropriate update option using GET"
      else
        settings.sparql_options.replace(standard_prefixes: true)
        settings.sparql_options.merge!(prefixes: {
          ssd: "http://www.w3.org/ns/sparql-service-description#",
          void: "http://rdfs.org/ns/void#"
        })
        repo = dataset(logger: request.logger, **options)
        service_description(repo: repo, endpoint: url)
      end
    end

    post '/' do
      request_body = request.body.read
      opts = params.inject({}) { |memo, (k,v)| memo.merge(k.to_sym => v) }
      # Note, this depends on the Rack::SPARQL::ContentNegotiation
      # middleware to rewrite application/x-www-form-urlencoded to be
      # conformant with either application/sparql-query or
      # application/sparql-update.
      query = begin
        update = case request.content_type
        when %r(application/sparql-query) then false
        when %r(application/sparql-update) then true
        else
          halt 406, "No query found for #{request.content_type}"
        end
        # XXX Rack always sets input to ASCII-8BIT
        #unless request.body.external_encoding == Encoding::UTF_8
        #  halt 400, "improper body encoding: #{request.body.external_encoding}"
        #end
        SPARQL.parse(request_body, base_uri: url, update: update)
      rescue SPARQL::Grammar::Parser::Error => e
        halt 400, "Error parsing #{update ? 'update' : 'query'}: #{e.message}"
      end
      repo = dataset(logger: request.logger, **opts)
      url = RDF::URI(request.url).tap {|u| u.query = nil}
      res = query.execute(repo,
                          logger: request.logger,
                          **options.merge(opts))
      res.is_a?(RDF::Literal::Boolean) ? [res] : res
    end
  end
end