5 class NotLoggedInException < StandardError
7 class InvalidApiResponseException < StandardError
10 @@client_mtx = Mutex.new
12 @@profiling_enabled = Rails.configuration.profiling_enabled
14 def api(resources_kind, action, data=nil)
17 @@client_mtx.synchronize do
19 @@api_client = HTTPClient.new
20 if Rails.configuration.arvados_insecure_https
21 @@api_client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
23 # Use system CA certificates
24 @@api_client.ssl_config.add_trust_ca('/etc/ssl/certs')
29 api_token = Thread.current[:arvados_api_token]
32 resources_kind = class_kind(resources_kind).pluralize if resources_kind.is_a? Class
33 url = "#{self.arvados_v1_base}/#{resources_kind}#{action}"
35 # Clean up /arvados/v1/../../discovery/v1 to /discovery/v1
36 url.sub! '/arvados/v1/../../', '/'
38 query = {"api_token" => api_token}
41 if v.is_a? String or v.nil?
48 query[k] = JSON.dump(v)
52 query["_method"] = "GET"
54 if @@profiling_enabled
55 query["_profile"] = "true"
58 header = {"Accept" => "application/json"}
60 profile_checkpoint { "Prepare request #{url} #{query[:uuid]} #{query[:where]}" }
61 msg = @@api_client.post(url,
64 profile_checkpoint 'API transaction'
66 if msg.status_code == 401
67 raise NotLoggedInException.new
73 resp = Oj.load(json, :symbol_keys => true)
75 raise InvalidApiResponseException.new json
77 if not resp.is_a? Hash
78 raise InvalidApiResponseException.new json
80 if msg.status_code != 200
81 errors = resp[:errors]
82 errors = errors.join("\n\n") if errors.is_a? Array
83 raise "#{errors} [API: #{msg.status_code}]"
86 Rails.logger.info "API client: " \
87 "#{resp.delete(:_profile)[:request_time]} request_time"
89 profile_checkpoint 'Parse response'
93 def self.patch_paging_vars(ary, items_available, offset, limit)
95 (class << ary; self; end).class_eval { attr_accessor :items_available }
96 ary.items_available = items_available
99 (class << ary; self; end).class_eval { attr_accessor :offset }
103 (class << ary; self; end).class_eval { attr_accessor :limit }
109 def unpack_api_response(j, kind=nil)
110 if j.is_a? Hash and j[:items].is_a? Array and j[:kind].match(/(_list|List)$/)
111 ary = j[:items].collect { |x| unpack_api_response x, j[:kind] }
112 ArvadosApiClient::patch_paging_vars(ary, j[:items_available], j[:offset], j[:limit])
113 elsif j.is_a? Hash and (kind || j[:kind])
114 oclass = self.kind_class(kind || j[:kind])
117 childkind = j["#{k.to_s}_kind".to_sym]
119 j[k] = self.unpack_api_response(j[k], childkind)
122 oclass.new.private_reload(j)
131 def arvados_login_url(params={})
132 if Rails.configuration.respond_to? :arvados_login_base
133 uri = Rails.configuration.arvados_login_base
135 uri = self.arvados_v1_base.sub(%r{/arvados/v\d+.*}, '/login')
138 uri += '?' << params.collect { |k,v|
139 CGI.escape(k.to_s) + '=' + CGI.escape(v.to_s)
144 def arvados_logout_url(params={})
145 arvados_login_url(params).sub('/login','/logout')
149 Rails.configuration.arvados_v1_base
153 @discovery ||= api '../../discovery/v1/apis/arvados/v1/rest', ''
157 kind.match(/^arvados\#(.+?)(_list|List)?$/)[1].pluralize.classify.constantize rescue nil
160 def class_kind(resource_class)
161 resource_class.to_s.underscore
165 def profile_checkpoint label=nil
166 return if !@@profiling_enabled
167 label = yield if block_given?
169 if label and @profile_t0
170 Rails.logger.info "API client: #{t - @profile_t0} #{label}"