Use operating system's bundled CA certificates if available.
[arvados.git] / apps / workbench / app / models / arvados_api_client.rb
1 require 'httpclient'
2 require 'thread'
3
4 class ArvadosApiClient
5   class NotLoggedInException < StandardError
6   end
7   class InvalidApiResponseException < StandardError
8   end
9
10   @@client_mtx = Mutex.new
11   @@api_client = nil
12
13   def api(resources_kind, action, data=nil)
14     @@client_mtx.synchronize do
15       if not @@api_client 
16         @@api_client = HTTPClient.new
17         if Rails.configuration.arvados_insecure_https
18           @@api_client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
19         else
20           # Use system CA certificates
21           @@api_client.ssl_config.add_trust_ca('/etc/ssl/certs')
22         end
23       end
24     end
25
26     api_token = Thread.current[:arvados_api_token]
27     api_token ||= ''
28
29     resources_kind = class_kind(resources_kind).pluralize if resources_kind.is_a? Class
30     url = "#{self.arvados_v1_base}/#{resources_kind}#{action}"
31
32     query = {"api_token" => api_token}
33     if !data.nil?
34       data.each do |k,v|
35         if v.is_a? String or v.nil?
36           query[k] = v
37         elsif v == true
38           query[k] = 1
39         elsif v == false
40           query[k] = 0
41         else
42           query[k] = JSON.dump(v)
43         end
44       end
45     else
46       query["_method"] = "GET"
47     end 
48     
49     header = {"Accept" => "application/json"}
50
51     msg = @@api_client.post(url, 
52                             query,
53                             header: header)
54
55     if msg.status_code == 401
56       raise NotLoggedInException.new
57     end
58
59     json = msg.content
60     
61     begin
62       resp = Oj.load(json, :symbol_keys => true)
63     rescue Oj::ParseError
64       raise InvalidApiResponseException.new json
65     end
66     if not resp.is_a? Hash
67       raise InvalidApiResponseException.new json
68     end
69     if resp[:errors]
70       #if resp[:errors][0] == 'Not logged in'
71       #  raise NotLoggedInException.new
72       #else
73       #  errors = resp[:errors]
74       #  errors = errors.join("\n\n") if errors.is_a? Array
75       #  raise "API errors:\n\n#{errors}\n"
76     #end
77     end
78     resp
79   end
80
81   def unpack_api_response(j, kind=nil)
82     if j.is_a? Hash and j[:items].is_a? Array and j[:kind].match(/(_list|List)$/)
83       ary = j[:items].collect { |x| unpack_api_response x, j[:kind] }
84       if j[:items_available]
85         (class << ary; self; end).class_eval { attr_accessor :items_available }
86         ary.items_available = j[:items_available]
87       end
88       ary
89     elsif j.is_a? Hash and (kind || j[:kind])
90       oclass = self.kind_class(kind || j[:kind])
91       if oclass
92         j.keys.each do |k|
93           childkind = j["#{k.to_s}_kind".to_sym]
94           if childkind
95             j[k] = self.unpack_api_response(j[k], childkind)
96           end
97         end
98         oclass.new.private_reload(j)
99       else
100         j
101       end
102     else
103       j
104     end
105   end
106
107   def arvados_login_url(params={})
108     if Rails.configuration.respond_to? :arvados_login_base
109       uri = Rails.configuration.arvados_login_base
110     else
111       uri = self.arvados_v1_base.sub(%r{/arvados/v\d+.*}, '/login')
112     end
113     if params.size > 0
114       uri += '?' << params.collect { |k,v|
115         CGI.escape(k.to_s) + '=' + CGI.escape(v.to_s)
116       }.join('&')
117     end
118   end
119
120   def arvados_logout_url(params={})
121     arvados_login_url(params).sub('/login','/logout')
122   end
123
124   def arvados_v1_base
125     Rails.configuration.arvados_v1_base
126   end
127
128   def arvados_schema
129     @arvados_schema ||= api 'schema', ''
130   end
131
132   def kind_class(kind)
133     kind.match(/^arvados\#(.+?)(_list|List)?$/)[1].pluralize.classify.constantize rescue nil
134   end
135
136   def class_kind(resource_class)
137     resource_class.to_s.underscore
138   end
139 end