summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoph Helma <helma@in-silico.ch>2013-03-26 10:57:31 +0100
committerChristoph Helma <helma@in-silico.ch>2013-03-26 10:57:31 +0100
commitade93108bb8a226dc77c07377db3ead54a550a44 (patch)
tree153863c5222fa82c7c64ac57839249d8cdd9532e
parent5b529f42d5fcc68b350b7bf10f6b4f7e71b7f9e2 (diff)
code cleanup and refactoring. 4store moved from opentox-client.
-rw-r--r--lib/4store.rb120
-rw-r--r--lib/opentox-server.rb3
-rw-r--r--lib/opentox.rb30
3 files changed, 141 insertions, 12 deletions
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 = "<html><body>" + turtle.gsub( regex, '<a href="\1">\1</a>\2' ).gsub(/\n/,'<br/>') + "</body></html>" 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