From ade93108bb8a226dc77c07377db3ead54a550a44 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Tue, 26 Mar 2013 10:57:31 +0100 Subject: code cleanup and refactoring. 4store moved from opentox-client. --- lib/4store.rb | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/opentox-server.rb | 3 +- lib/opentox.rb | 30 ++++++++----- 3 files changed, 141 insertions(+), 12 deletions(-) create mode 100644 lib/4store.rb diff --git a/lib/4store.rb b/lib/4store.rb new file mode 100644 index 0000000..d03a38e --- /dev/null +++ b/lib/4store.rb @@ -0,0 +1,120 @@ +module OpenTox + module Backend + class FourStore + + @@accept_formats = [ "application/rdf+xml", "text/turtle", "text/plain", "text/uri-list", "text/html", 'application/sparql-results+xml' ] + @@content_type_formats = [ "application/rdf+xml", "text/turtle", "text/plain", "application/x-turtle" ] + + def self.list mime_type + bad_request_error "'#{mime_type}' is not a supported mime type. Please specify one of #{@@accept_formats.join(", ")} in the Accept Header." unless @@accept_formats.include? mime_type + if mime_type =~ /(uri-list|html)/ + sparql = "SELECT DISTINCT ?g WHERE {GRAPH ?g {?s <#{RDF.type}> <#{klass}>; <#{RDF::DC.modified}> ?o.} } ORDER BY ?o" + else + sparql = "CONSTRUCT {?s ?p ?o.} WHERE {?s <#{RDF.type}> <#{klass}>; ?p ?o. }" + end + query sparql, mime_type + end + + def self.get uri, mime_type + bad_request_error "'#{mime_type}' is not a supported mime type. Please specify one of #{@@accept_formats.join(", ")} in the Accept Header." unless @@accept_formats.include? mime_type + sparql = "CONSTRUCT {?s ?p ?o.} FROM <#{uri}> WHERE { ?s ?p ?o. }" + rdf = query sparql, mime_type + resource_not_found_error "#{uri} not found." if rdf.empty? + rdf + end + + def self.post uri, rdf, mime_type + bad_request_error "'#{mime_type}' is not a supported content type. Please use one of #{@@content_type_formats.join(", ")}." unless @@content_type_formats.include? mime_type or mime_type == "multipart/form-data" + bad_request_error "Request body empty." unless rdf + mime_type = "application/x-turtle" if mime_type == "text/plain" # ntriples is turtle in 4store + RestClient.post File.join(four_store_uri,"data")+"/", :data => rdf.gsub(/\\C/,'C'), :graph => uri, "mime-type" => mime_type # remove backslashes in SMILES (4store interprets them as UTF-8 \C even within single quoates) + update "INSERT DATA { GRAPH <#{uri}> { <#{uri}> <#{RDF::DC.modified}> \"#{DateTime.now}\" } }" + end + + def self.put uri, rdf, mime_type + bad_request_error "'#{mime_type}' is not a supported content type. Please use one of #{@@content_type_formats.join(", ")}." unless @@content_type_formats.include? mime_type + bad_request_error "Reqest body empty." unless rdf + mime_type = "application/x-turtle" if mime_type == "text/plain" + RestClientWrapper.put File.join(four_store_uri,"data",uri), rdf, :content_type => mime_type + update "INSERT DATA { GRAPH <#{uri}> { <#{uri}> <#{RDF::DC.modified}> \"#{DateTime.now}\" } }" + end + + def self.delete uri + RestClientWrapper.delete data_uri(uri) + end + + def self.update sparql + RestClient.post(update_uri, :update => sparql ) + rescue + bad_request_error $!.message, update_uri + end + + def self.query sparql, mime_type + if sparql =~ /SELECT/i + # return list unless mime_type + case mime_type + when 'application/sparql-results+xml' + RestClient.get(sparql_uri, :params => { :query => sparql }, :accept => mime_type).body + when 'application/json' + RestClient.get(sparql_uri, :params => { :query => sparql }, :accept => mime_type).body + when /(uri-list|html)/ + uri_list = RestClient.get(sparql_uri, :params => { :query => sparql }, :accept => "text/plain").body.gsub(/"|<|>/,'').split("\n").drop(1).join("\n") + uri_list = uri_list.to_html if mime_type=~/html/ + return uri_list + else + bad_request_error "#{mime_type} is not a supported mime type for SELECT statements." + end + elsif sparql =~ /CONSTRUCT/i + case mime_type + when "text/plain", "application/rdf+xml" + RestClient.get(sparql_uri, :params => { :query => sparql }, :accept => mime_type).body + when /html|turtle/ + # TODO: fix and improve + nt = RestClient.get(sparql_uri, :params => { :query => sparql }, :accept => "text/plain").body # 4store returns ntriples for turtle + + rdf = RDF::Graph.new + RDF::Reader.for(:ntriples).new(nt) do |reader| + reader.each_statement { |statement| rdf << statement } + end + prefixes = {:rdf => "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"} + ['OT', 'DC', 'XSD', 'OLO'].each{|p| prefixes[p.downcase.to_sym] = eval("RDF::#{p}.to_s") } + # TODO: fails for large datasets?? multi_cell_call + turtle = RDF::N3::Writer.for(:turtle).buffer(:prefixes => prefixes) do |writer| + rdf.each{|statement| writer << statement} + end + regex = Regexp.new '(https?:\/\/[\S]+)([>"])' + turtle = "" + turtle.gsub( regex, '\1\2' ).gsub(/\n/,'
') + "" if mime_type =~ /html/ and !turtle.empty? + turtle + end + else + # TODO: check if this prevents SPARQL injections + bad_request_error "Only SELECT and CONSTRUCT are accepted SPARQL statements." + end + rescue + bad_request_error $!.message, sparql_uri + end + + def self.klass + RDF::OT[SERVICE.capitalize] + end + + def self.four_store_uri + # credentials are removed from uri in error.rb + $four_store[:uri].sub(%r{//},"//#{$four_store[:user]}:#{$four_store[:password]}@") + end + + def self.sparql_uri + File.join(four_store_uri, "sparql") + '/' + end + + def self.update_uri + File.join(four_store_uri, "update") + '/' + end + + def self.data_uri uri + File.join(four_store_uri, "data","?graph=#{uri}") + end + + end + end +end diff --git a/lib/opentox-server.rb b/lib/opentox-server.rb index 5e3aada..5ff3c22 100644 --- a/lib/opentox-server.rb +++ b/lib/opentox-server.rb @@ -2,5 +2,4 @@ require 'opentox-client' require 'rack' require 'rack/contrib' require 'sinatra' -require File.join(File.dirname(__FILE__),"opentox.rb") -require File.join(File.dirname(__FILE__),"authorization-helper.rb") +["4store.rb", "opentox.rb", "authorization-helper.rb"].each {|f| require File.join(File.dirname(__FILE__),f) } diff --git a/lib/opentox.rb b/lib/opentox.rb index 894033d..eb35b38 100644 --- a/lib/opentox.rb +++ b/lib/opentox.rb @@ -22,11 +22,17 @@ module OpenTox configure :development do register Sinatra::Reloader also_reload "./*.rb" + also_reload "./**/*.rb" also_reload "../opentox-client/lib/*.rb" also_reload File.join(ENV["HOME"],".opentox","config","#{SERVICE}.rb") end before do + @uri = uri(request.env['REQUEST_URI']) + # fix IE + request.env['HTTP_ACCEPT'] += ";text/html" if request.env["HTTP_USER_AGENT"]=~/MSIE/ + request.env['HTTP_ACCEPT'] = request.params["media"] if request.params["media"] + request.content_type ? response['Content-Type'] = request.content_type : response['Content-Type'] = request.env['HTTP_ACCEPT'] parse_input if request.request_method =~ /POST|PUT/ @accept = request.env['HTTP_ACCEPT'] @@ -35,10 +41,6 @@ module OpenTox response['Content-Type'] = @accept end - before "/#{SERVICE}/:id" do - @uri = uri("/#{SERVICE}/#{params[:id]}") - end - helpers do def parse_input case request.content_type @@ -56,7 +58,7 @@ module OpenTox end end - # Attention: Error within tasks are catched by Task.create + # Attention: Error within tasks are catched by Task.run error do error = request.env['sinatra.error'] if error.respond_to? :report @@ -97,10 +99,18 @@ module OpenTox # Default methods, may be overwritten by derived services # see http://jcalcote.wordpress.com/2008/10/16/put-or-post-the-rest-of-the-story/ - # Get a list of objects at the server + # Get a list of objects at the server or perform a SPARQL query get "/#{SERVICE}/?" do - $logger.debug "listing all #{SERVICE} objects" - FourStore.list @accept + if params[:query] + case @accept + when "text/uri-list" # result URIs are protected by A+A + FourStore.query(params[:query], "text/uri-list") + else # prevent searches for protected resources + bad_request_error "Accept header '#{@accept}' is disabled for SPARQL queries at service URIs in order to protect private data. Use 'text/uri-list' and repeat the query at the result URIs.", uri("/#{SERVICE}") + end + else + FourStore.list(@accept) + end end # Create a new resource @@ -111,9 +121,9 @@ module OpenTox @uri end - # Get resource representation + # Get resource representation or perform a SPARQL query get "/#{SERVICE}/:id/?" do - FourStore.get(@uri, @accept) + params[:query] ? FourStore.query(params[:query], @accept) : FourStore.get(@uri, @accept) end # Modify (i.e. add rdf statments to) a resource -- cgit v1.2.3