summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoph Helma <helma@in-silico.ch>2012-02-23 17:56:46 +0000
committerChristoph Helma <helma@in-silico.ch>2012-02-23 17:56:46 +0000
commitfa9069e13fb6b1c8bb4ebcdf82f1cf1c04ad71ca (patch)
treeefe8bff6c3538c856805cadac832531aa6b0821e
parentb651c4d199a7b4d6a06cdefb281601bddd2fc885 (diff)
(partially) switched back to RestClientWrapper
-rw-r--r--lib/compound.rb8
-rw-r--r--lib/opentox-client.rb2
-rw-r--r--lib/opentox.rb67
-rw-r--r--lib/overwrite.rb39
-rw-r--r--lib/rest_client_wrapper.rb182
-rw-r--r--lib/task.rb10
-rw-r--r--test/rest.rb23
-rw-r--r--test/ruby-api.rb34
8 files changed, 245 insertions, 120 deletions
diff --git a/lib/compound.rb b/lib/compound.rb
index 8761d50..ce0fdbf 100644
--- a/lib/compound.rb
+++ b/lib/compound.rb
@@ -11,21 +11,21 @@ module OpenTox
# @param [String] smiles Smiles string
# @return [OpenTox::Compound] Compound
def self.from_smiles service_uri, smiles, subjectid=nil
- Compound.new RestClient.post(service_uri, smiles, {:content_type => 'chemical/x-daylight-smiles', :subjectid => subjectid})
+ Compound.new RestClientWrapper.post(service_uri, smiles, {:content_type => 'chemical/x-daylight-smiles', :subjectid => subjectid})
end
# Create a compound from inchi string
# @param [String] smiles InChI string
# @return [OpenTox::Compound] Compound
def self.from_inchi service_uri, inchi, subjectid=nil
- Compound.new RestClient.post(service_uri, inchi, {:content_type => 'chemical/x-inchi', :subjectid => subjectid})
+ Compound.new RestClientWrapper.post(service_uri, inchi, {:content_type => 'chemical/x-inchi', :subjectid => subjectid})
end
# Create a compound from sdf string
# @param [String] smiles SDF string
# @return [OpenTox::Compound] Compound
def self.from_sdf service_uri, sdf, subjectid=nil
- Compound.new RestClient.post(service_uri, sdf, {:content_type => 'chemical/x-mdl-sdfile', :subjectid => subjectid})
+ Compound.new RestClientWrapper.post(service_uri, sdf, {:content_type => 'chemical/x-mdl-sdfile', :subjectid => subjectid})
end
# Create a compound from name. Relies on an external service for name lookups.
@@ -34,7 +34,7 @@ module OpenTox
# @param [String] name name can be also an InChI/InChiKey, CAS number, etc
# @return [OpenTox::Compound] Compound
def self.from_name service_uri, name, subjectid=nil
- Compound.new RestClient.post(service_uri, name, {:content_type => 'text/plain', :subjectid => subjectid})
+ Compound.new RestClientWrapper.post(service_uri, name, {:content_type => 'text/plain', :subjectid => subjectid})
end
# Get InChI
diff --git a/lib/opentox-client.rb b/lib/opentox-client.rb
index fc6cbd1..1a5e7c3 100644
--- a/lib/opentox-client.rb
+++ b/lib/opentox-client.rb
@@ -6,7 +6,9 @@ require "rest-client"
require 'uri'
require 'yaml'
require 'logger'
+require File.join(File.dirname(__FILE__),"overwrite.rb")
require File.join(File.dirname(__FILE__),"error.rb")
+require File.join(File.dirname(__FILE__),"rest_client_wrapper.rb")
require File.join(File.dirname(__FILE__),"otlogger.rb") # avoid require conflicts with logger
require File.join(File.dirname(__FILE__),"opentox.rb")
require File.join(File.dirname(__FILE__),"task.rb")
diff --git a/lib/opentox.rb b/lib/opentox.rb
index 2fbf9dc..f81ae10 100644
--- a/lib/opentox.rb
+++ b/lib/opentox.rb
@@ -4,45 +4,6 @@ RDF::OT1 = RDF::Vocabulary.new 'http://www.opentox.org/api/1.1#'
RDF::OTA = RDF::Vocabulary.new 'http://www.opentox.org/algorithmTypes.owl#'
SERVICES = ["Compound", "Feature", "Dataset", "Algorithm", "Model", "Validation", "Task", "Investigation"]
-class String
- def underscore
- self.gsub(/::/, '/').
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
- tr("-", "_").
- downcase
- end
-end
-
-module URI
-
- def self.task? uri
- uri =~ /task/ and URI.valid? uri
- end
-
- def self.dataset? uri, subjectid=nil
- uri =~ /dataset/ and URI.accessible? uri, subjectid=nil
- end
-
- def self.model? uri, subjectid=nil
- uri =~ /model/ and URI.accessible? uri, subjectid=nil
- end
-
- def self.accessible? uri, subjectid=nil
- Net::HTTP.get_response(URI.parse(uri))
- true
- rescue
- false
- end
-
- def self.valid? uri
- u = URI::parse(uri)
- u.scheme!=nil and u.host!=nil
- rescue URI::InvalidURIError
- false
- end
-end
-
# defaults to stderr, may be changed to file output
$logger = OTLogger.new(STDERR) # no rotation
$logger.level = Logger::DEBUG
@@ -59,13 +20,15 @@ module OpenTox
# Ruby interface
-
# override to read all error codes
def metadata reload=true
- if reload
+ if reload or @metadata.empty?
@metadata = {}
# ignore error codes from Task services (may contain eg 500 which causes exceptions in RestClient and RDF::Reader
- RestClient.get(@uri) do |response, request, result, &block|
+ # TODO: convert to RestClientWrapper
+ kind_of?(OpenTox::Dataset) ? uri = File.join(@uri,"metadata") : uri = @uri
+ RestClient.get(uri) do |response, request, result|
+ #response = RestClientWrapper.get(@uri) #do |response, request, result|
$logger.warn "#{@uri} returned #{result}" unless response.code == 200 or response.code == 202 or URI.task? @uri
RDF::Reader.for(:rdfxml).new(response) do |reader|
reader.each_statement do |statement|
@@ -74,6 +37,7 @@ module OpenTox
end
end
end
+ #puts @metadata.inspect
@metadata
end
@@ -88,46 +52,41 @@ module OpenTox
def get params={}
params[:subjectid] ||= @subjectid
params[:accept] ||= 'application/rdf+xml'
- @response = RestClient.get @uri, params
+ @response = RestClientWrapper.get @uri, params
end
def post payload={}, params={}
params[:subjectid] ||= @subjectid
params[:accept] ||= 'application/rdf+xml'
- @response = RestClient.post(@uri.to_s, payload, params)
- begin
- @response.to_s.to_object
- rescue
- @response
- end
+ @response = RestClientWrapper.post(@uri.to_s, payload, params)
end
def put payload={}, params={}
params[:subjectid] ||= @subjectid
params[:accept] ||= 'application/rdf+xml'
- @response = RestClient.put(@uri.to_s, payload, params)
+ @response = RestClientWrapper.put(@uri.to_s, payload, params)
end
def delete params={}
params[:subjectid] ||= @subjectid
params[:accept] ||= 'application/rdf+xml'
- @response = RestClient.delete(@uri.to_s,:subjectid => @subjectid)
+ @response = RestClientWrapper.delete(@uri.to_s,:subjectid => @subjectid)
end
# class methods
module ClassMethods
def create service_uri, subjectid=nil
- uri = RestClient.post(service_uri, {}, :subjectid => subjectid).chomp
+ uri = RestClientWrapper.post(service_uri, {}, :subjectid => subjectid).chomp
subjectid ? eval("#{self}.new(\"#{uri}\", #{subjectid})") : eval("#{self}.new(\"#{uri}\")")
end
def from_file service_uri, file, subjectid=nil
- RestClient.post(service_uri, :file => File.new(file), :subjectid => subjectid).chomp.to_object
+ RestClientWrapper.post(service_uri, :file => File.new(file), :subjectid => subjectid).chomp.to_object
end
def all service_uri, subjectid=nil
- uris = RestClient.get(service_uri, {:accept => 'text/uri-list'}).split("\n").compact
+ uris = RestClientWrapper.get(service_uri, {:accept => 'text/uri-list'}).split("\n").compact
uris.collect{|uri| subjectid ? eval("#{self}.new(\"#{uri}\", #{subjectid})") : eval("#{self}.new(\"#{uri}\")")}
end
diff --git a/lib/overwrite.rb b/lib/overwrite.rb
new file mode 100644
index 0000000..e883d45
--- /dev/null
+++ b/lib/overwrite.rb
@@ -0,0 +1,39 @@
+class String
+ def underscore
+ self.gsub(/::/, '/').
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
+ tr("-", "_").
+ downcase
+ end
+end
+
+module URI
+
+ def self.task? uri
+ uri =~ /task/ and URI.valid? uri
+ end
+
+ def self.dataset? uri, subjectid=nil
+ uri =~ /dataset/ and URI.accessible? uri, subjectid=nil
+ end
+
+ def self.model? uri, subjectid=nil
+ uri =~ /model/ and URI.accessible? uri, subjectid=nil
+ end
+
+ def self.accessible? uri, subjectid=nil
+ Net::HTTP.get_response(URI.parse(uri))
+ true
+ rescue
+ false
+ end
+
+ def self.valid? uri
+ u = URI::parse(uri)
+ u.scheme!=nil and u.host!=nil
+ rescue URI::InvalidURIError
+ false
+ end
+end
+
diff --git a/lib/rest_client_wrapper.rb b/lib/rest_client_wrapper.rb
new file mode 100644
index 0000000..89de277
--- /dev/null
+++ b/lib/rest_client_wrapper.rb
@@ -0,0 +1,182 @@
+module OpenTox
+
+ class WrapperResult < String
+ attr_accessor :content_type, :code
+ end
+
+ class RestClientWrapper
+
+ # performs a GET REST call
+ # raises OpenTox::Error if call fails (rescued in overwrite.rb -> halt 502)
+ # per default: waits for Task to finish and returns result URI of Task
+ # @param [String] uri destination URI
+ # @param [optional,Hash] headers contains params like accept-header
+ # @param [optional,OpenTox::Task] waiting_task (can be a OpenTox::Subtask as well), progress is updated accordingly
+ # @param [wait,Boolean] wait set to false to NOT wait for task if result is task
+ # @return [OpenTox::WrapperResult] a String containing the result-body of the REST call
+ def self.get(uri, headers={}, waiting_task=nil, wait=true )
+ execute( "get", uri, nil, headers, waiting_task, wait)
+ end
+
+ # performs a POST REST call
+ # raises OpenTox::Error if call fails (rescued in overwrite.rb -> halt 502)
+ # per default: waits for Task to finish and returns result URI of Task
+ # @param [String] uri destination URI
+ # @param [optional,String] payload data posted to the service
+ # @param [optional,Hash] headers contains params like accept-header
+ # @param [optional,OpenTox::Task] waiting_task (can be a OpenTox::Subtask as well), progress is updated accordingly
+ # @param [wait,Boolean] wait set to false to NOT wait for task if result is task
+ # @return [OpenTox::WrapperResult] a String containing the result-body of the REST call
+ def self.post(uri, payload=nil, headers={}, waiting_task=nil, wait=true )
+ execute( "post", uri, payload, headers, waiting_task, wait )
+ end
+
+ # performs a PUT REST call
+ # raises OpenTox::Error if call fails (rescued in overwrite.rb -> halt 502)
+ # @param [String] uri destination URI
+ # @param [optional,Hash] headers contains params like accept-header
+ # @param [optional,String] payload data put to the service
+ # @return [OpenTox::WrapperResult] a String containing the result-body of the REST call
+ def self.put(uri, payload=nil, headers={} )
+ execute( "put", uri, payload, headers )
+ end
+
+ # performs a DELETE REST call
+ # raises OpenTox::Error if call fails (rescued in overwrite.rb -> halt 502)
+ # @param [String] uri destination URI
+ # @param [optional,Hash] headers contains params like accept-header
+ # @return [OpenTox::WrapperResult] a String containing the result-body of the REST call
+ def self.delete(uri, headers=nil )
+ execute( "delete", uri, nil, headers)
+ end
+
+ private
+ def self.execute( rest_call, uri, payload=nil, headers={}, waiting_task=nil, wait=true )
+
+ raise OpenTox::BadRequestError.new "uri is null" unless uri
+ raise OpenTox::BadRequestError.new "not a uri: "+uri.to_s unless URI.valid? uri.to_s
+ raise "headers are no hash: "+headers.inspect unless headers==nil or headers.is_a?(Hash)
+ raise OpenTox::BadRequestError.new "accept should go into the headers" if payload and payload.is_a?(Hash) and payload[:accept]
+ raise OpenTox::BadRequestError.new "content_type should go into the headers" if payload and payload.is_a?(Hash) and payload[:content_type]
+ raise "__waiting_task__ must be 'nil' or '(sub)task', is "+waiting_task.class.to_s if
+ waiting_task!=nil and !(waiting_task.is_a?(Task) || waiting_task.is_a?(SubTask))
+ headers.each{ |k,v| headers.delete(k) if v==nil } if headers #remove keys with empty values, as this can cause problems
+ ## PENDING partner services accept subjectid only in header
+ headers = {} unless headers
+ headers[:subjectid] = payload.delete(:subjectid) if payload and payload.is_a?(Hash) and payload.has_key?(:subjectid)
+
+ # PENDING needed for NUTA, until we finally agree on how to send subjectid
+ headers[:subjectid] = payload.delete(:subjectid) if uri=~/ntua/ and payload and payload.is_a?(Hash) and payload.has_key?(:subjectid)
+
+ begin
+ #LOGGER.debug "RestCall: "+rest_call.to_s+" "+uri.to_s+" "+headers.inspect+" "+payload.inspect
+ resource = RestClient::Resource.new(uri,{:timeout => 600})
+ if rest_call=="post" || rest_call=="put"
+ result = resource.send(rest_call, payload, headers){|response, request, result| response }
+ else
+ result = resource.send(rest_call, headers){|response, request, result| response }
+ end
+ #LOGGER.debug "result body size: #{result.body.size}"
+
+ # PENDING NTUA does return errors with 200
+ raise RestClient::ExceptionWithResponse.new(result) if uri=~/ntua/ and result.body =~ /about.*http:\/\/anonymous.org\/error/
+
+ # result is a string, with the additional fields content_type and code
+ res = WrapperResult.new(result.body)
+ res.content_type = result.headers[:content_type]
+ raise "content-type not set" unless res.content_type
+ res.code = result.code
+
+ # TODO: Ambit returns task representation with 200 instead of result URI
+ return res if res.code==200 || !wait
+
+ while (res.code==201 || res.code==202)
+ res = wait_for_task(res, uri, waiting_task)
+ end
+ raise "illegal status code: '"+res.code.to_s+"'" unless res.code==200
+ return res
+
+ rescue RestClient::RequestTimeout => ex
+ received_error ex.message, 408, nil, {:rest_uri => uri, :headers => headers, :payload => payload}
+ rescue Errno::ETIMEDOUT => ex
+ received_error ex.message, 408, nil, {:rest_uri => uri, :headers => headers, :payload => payload}
+ rescue Errno::ECONNREFUSED => ex
+ received_error ex.message, 500, nil, {:rest_uri => uri, :headers => headers, :payload => payload}
+ rescue RestClient::ExceptionWithResponse => ex
+ # error comming from a different webservice,
+ received_error ex.http_body, ex.http_code, ex.response.net_http_res.content_type, {:rest_uri => uri, :headers => headers, :payload => payload}
+ rescue OpenTox::RestCallError => ex
+ # already a rest-error, probably comes from wait_for_task, just pass through
+ raise ex
+ rescue => ex
+ # some internal error occuring in rest_client_wrapper, just pass through
+ raise ex
+ end
+ end
+
+ def self.wait_for_task( res, base_uri, waiting_task=nil )
+ #TODO remove TUM hack
+ res.content_type = "text/uri-list" if base_uri =~/tu-muenchen/ and res.content_type == "application/x-www-form-urlencoded;charset=UTF-8"
+
+ task = nil
+ case res.content_type
+ when /application\/rdf\+xml/
+ task = OpenTox::Task.from_rdfxml(res)
+ when /yaml/
+ task = OpenTox::Task.from_yaml(res)
+ when /text\//
+ raise "uri list has more than one entry, should be a task" if res.content_type=~/text\/uri-list/ and res.split("\n").size > 1 #if uri list contains more then one uri, its not a task
+ task = OpenTox::Task.find(res.to_s.chomp) if res.to_s.uri?
+ else
+ raise "unknown content-type for task : '"+res.content_type.to_s+"'"+" base-uri: "+base_uri.to_s+" content: "+res[0..200].to_s
+ end
+
+ #LOGGER.debug "result is a task '"+task.uri.to_s+"', wait for completion"
+ task.wait_for_completion waiting_task
+ unless task.completed? # maybe task was cancelled / error
+ if task.errorReport
+ received_error task.errorReport, task.http_code, nil, {:rest_uri => task.uri, :rest_code => task.http_code}
+ else
+ raise "status of task '"+task.uri.to_s+"' is no longer running (hasStatus is '"+task.status+
+ "'), but it is neither completed nor has an errorReport"
+ end
+ end
+
+ res = WrapperResult.new task.result_uri
+ res.code = task.http_code
+ res.content_type = "text/uri-list"
+ return res
+ end
+
+ def self.received_error( body, code, content_type=nil, params=nil )
+
+ # try to parse body
+ report = nil
+ if body.is_a?(OpenTox::ErrorReport)
+ report = body
+ else
+ case content_type
+ when /yaml/
+ report = YAML.load(body)
+ when /rdf/
+ report = OpenTox::ErrorReport.from_rdf(body)
+ end
+ end
+
+ unless report
+ # parsing was not successfull
+ # raise 'plain' RestCallError
+ err = OpenTox::RestCallError.new("REST call returned error: '"+body.to_s+"'")
+ err.rest_params = params
+ raise err
+ else
+ # parsing sucessfull
+ # raise RestCallError with parsed report as error cause
+ err = OpenTox::RestCallError.new("REST call subsequent error")
+ err.errorCause = report
+ err.rest_params = params
+ raise err
+ end
+ end
+ end
+end
diff --git a/lib/task.rb b/lib/task.rb
index 9aaee04..286e998 100644
--- a/lib/task.rb
+++ b/lib/task.rb
@@ -7,7 +7,7 @@ module OpenTox
attr_accessor :pid, :observer_pid
def self.create service_uri, params={}
- task = Task.new RestClient.post(service_uri,params).chomp
+ task = Task.new RestClientWrapper.post(service_uri,params).chomp
pid = fork do
begin
result_uri = yield
@@ -53,20 +53,20 @@ module OpenTox
def cancel
kill
- RestClient.put(File.join(@uri,'Cancelled'),{})
+ RestClientWrapper.put(File.join(@uri,'Cancelled'),{})
end
def completed(uri)
#TODO: subjectid?
#TODO: error code
raise "\"#{uri}\" does not exist." unless URI.accessible? uri
- RestClient.put(File.join(@uri,'Completed'),{:resultURI => uri})
+ RestClientWrapper.put(File.join(@uri,'Completed'),{:resultURI => uri})
end
def error error
$logger.error self if $logger
report = ErrorReport.create(error,"http://localhost")
- RestClient.put(File.join(@uri,'Error'),{:errorReport => report})
+ RestClientWrapper.put(File.join(@uri,'Error'),{:errorReport => report})
kill
end
@@ -104,7 +104,7 @@ module OpenTox
begin
case method
when /=/
- res = RestClient.put(File.join(@uri,method.sub(/=/,'')),{})
+ res = RestClientWrapper.put(File.join(@uri,method.sub(/=/,'')),{})
super unless res.code == 200
#when /\?/
#return hasStatus == method.sub(/\?/,'').capitalize
diff --git a/test/rest.rb b/test/rest.rb
deleted file mode 100644
index 412e265..0000000
--- a/test/rest.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require 'test/unit'
-$LOAD_PATH << File.join(File.dirname(__FILE__),'..','lib')
-require File.join File.dirname(__FILE__),'..','lib','opentox-client.rb'
-
-class RestTest < Test::Unit::TestCase
-
- def test_post_get_delete
- service_uri = "http://ot-dev.in-silico.ch/dataset"
- dataset = OpenTox::Dataset.create service_uri
- assert_match /#{service_uri}/, dataset.uri.to_s
- puts dataset.uri
- puts dataset.class
- puts dataset.to_yaml
- metadata = dataset.metadata
- puts dataset.class
-=begin
- assert_equal RDF::OT.Dataset, metadata[RDF.type]
- assert_equal dataset.uri, metadata[RDF::XSD.anyURI]
-=end
- dataset.delete
- end
-
-end
diff --git a/test/ruby-api.rb b/test/ruby-api.rb
deleted file mode 100644
index 0c9386b..0000000
--- a/test/ruby-api.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require 'test/unit'
-$LOAD_PATH << File.join(File.dirname(__FILE__),'..','lib')
-require File.join File.dirname(__FILE__),'..','lib','opentox-client.rb'
-
-class RubyAPITest < Test::Unit::TestCase
-
- def test_all
- datasets = OpenTox::Dataset.all "http://ot-dev.in-silico.ch/dataset"
- assert_equal OpenTox::Dataset, datasets.first.class
- assert_equal RDF::OT.Dataset, datasets.last.metadata[RDF.type]
- end
-=begin
-
- def test_create
- d = OpenTox::Dataset.create "http://ot-dev.in-silico.ch/dataset"
- puts d.inspect
- assert_equal OpenTox::Dataset, d.class
- assert_equal RDF::OT.Dataset, d.metadata[RDF.type]
- d.delete
- end
-
- def test_save
- d = OpenTox::Dataset.create "http://ot-dev.in-silico.ch/dataset"
- d.metadata
- d.metadata[RDF::DC.title] = "test"
- d.save
- # TODO: save does not work with datasets
- #puts d.response.code.inspect
- #assert_equal "test", d.metadata[RDF::DC.title] # should reload metadata
- d.delete
- end
-=end
-
-end