Merge branch '1692-redesign-dashboard'
[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         end
20       end
21     end
22
23     api_token = Thread.current[:arvados_api_token]
24     api_token ||= ''
25
26     resources_kind = class_kind(resources_kind).pluralize if resources_kind.is_a? Class
27     url = "#{self.arvados_v1_base}/#{resources_kind}#{action}"
28
29     query = {"api_token" => api_token}
30     if !data.nil?
31       data.each do |k,v|
32         if v.is_a? String or v.nil?
33           query[k] = v
34         elsif v == true
35           query[k] = 1
36         elsif v == false
37           query[k] = 0
38         else
39           query[k] = JSON.dump(v)
40         end
41       end
42     else
43       query["_method"] = "GET"
44     end 
45     
46     header = {"Accept" => "application/json"}
47
48     msg = @@api_client.post(url, 
49                             query,
50                             header: header)
51
52     if msg.status_code == 401
53       raise NotLoggedInException.new
54     end
55
56     json = msg.content
57     
58     begin
59       resp = Oj.load(json, :symbol_keys => true)
60     rescue Oj::ParseError
61       raise InvalidApiResponseException.new json
62     end
63     if not resp.is_a? Hash
64       raise InvalidApiResponseException.new json
65     end
66     if resp[:errors]
67       #if resp[:errors][0] == 'Not logged in'
68       #  raise NotLoggedInException.new
69       #else
70       #  errors = resp[:errors]
71       #  errors = errors.join("\n\n") if errors.is_a? Array
72       #  raise "API errors:\n\n#{errors}\n"
73     #end
74     end
75     resp
76   end
77
78   def unpack_api_response(j, kind=nil)
79     if j.is_a? Hash and j[:items].is_a? Array and j[:kind].match(/(_list|List)$/)
80       j[:items].collect { |x| unpack_api_response x, j[:kind] }
81     elsif j.is_a? Hash and (kind || j[:kind])
82       oclass = self.kind_class(kind || j[:kind])
83       if oclass
84         j.keys.each do |k|
85           childkind = j["#{k.to_s}_kind".to_sym]
86           if childkind
87             j[k] = self.unpack_api_response(j[k], childkind)
88           end
89         end
90         oclass.new.private_reload(j)
91       else
92         j
93       end
94     else
95       j
96     end
97   end
98
99   def arvados_login_url(params={})
100     if Rails.configuration.respond_to? :arvados_login_base
101       uri = Rails.configuration.arvados_login_base
102     else
103       uri = self.arvados_v1_base.sub(%r{/arvados/v\d+.*}, '/login')
104     end
105     if params.size > 0
106       uri += '?' << params.collect { |k,v|
107         CGI.escape(k.to_s) + '=' + CGI.escape(v.to_s)
108       }.join('&')
109     end
110   end
111
112   def arvados_logout_url(params={})
113     arvados_login_url(params).sub('/login','/logout')
114   end
115
116   def arvados_v1_base
117     Rails.configuration.arvados_v1_base
118   end
119
120   def arvados_schema
121     @arvados_schema ||= api 'schema', ''
122   end
123
124   def kind_class(kind)
125     kind.match(/^arvados\#(.+?)(_list|List)?$/)[1].pluralize.classify.constantize rescue nil
126   end
127
128   def class_kind(resource_class)
129     resource_class.to_s.underscore
130   end
131 end