3400: ArvadosResourceList retains api token that is active when it is created.
[arvados.git] / apps / workbench / app / models / arvados_resource_list.rb
1 class ArvadosResourceList
2   include ArvadosApiClientHelper
3   include Enumerable
4
5   def initialize resource_class=nil
6     @resource_class = resource_class
7     @fetch_multiple_pages = true
8     @arvados_api_token = Thread.current[:arvados_api_token]
9     @reader_tokens = Thread.current[:reader_tokens]
10   end
11
12   def eager(bool=true)
13     @eager = bool
14     self
15   end
16
17   def limit(max_results)
18     if not max_results.nil? and not max_results.is_a? Integer
19       raise ArgumentError("argument to limit() must be an Integer or nil")
20     end
21     @limit = max_results
22     self
23   end
24
25   def offset(skip)
26     @offset = skip
27     self
28   end
29
30   def order(orderby_spec)
31     @orderby_spec = orderby_spec
32     self
33   end
34
35   def select(columns=nil)
36     # If no column arguments were given, invoke Enumerable#select.
37     if columns.nil?
38       super()
39     else
40       @select ||= []
41       @select += columns
42       self
43     end
44   end
45
46   def filter _filters
47     @filters ||= []
48     @filters += _filters
49     self
50   end
51
52   def where(cond)
53     @cond = cond.dup
54     @cond.keys.each do |uuid_key|
55       if @cond[uuid_key] and (@cond[uuid_key].is_a? Array or
56                              @cond[uuid_key].is_a? ArvadosBase)
57         # Coerce cond[uuid_key] to an array of uuid strings.  This
58         # allows caller the convenience of passing an array of real
59         # objects and uuids in cond[uuid_key].
60         if !@cond[uuid_key].is_a? Array
61           @cond[uuid_key] = [@cond[uuid_key]]
62         end
63         @cond[uuid_key] = @cond[uuid_key].collect do |item|
64           if item.is_a? ArvadosBase
65             item.uuid
66           else
67             item
68           end
69         end
70       end
71     end
72     @cond.keys.select { |x| x.match /_kind$/ }.each do |kind_key|
73       if @cond[kind_key].is_a? Class
74         @cond = @cond.merge({ kind_key => 'arvados#' + arvados_api_client.class_kind(@cond[kind_key]) })
75       end
76     end
77     self
78   end
79
80   def fetch_multiple_pages(f)
81     @fetch_multiple_pages = f
82     self
83   end
84
85   def results
86     if !@results
87       @results = []
88       self.each_page do |r|
89         @results.concat r
90       end
91     end
92     @results
93   end
94
95   def results=(r)
96     @results = r
97     @items_available = r.items_available if r.respond_to? :items_available
98     @result_limit = r.limit if r.respond_to? :limit
99     @result_offset = r.offset if r.respond_to? :offset
100     @result_links = r.links if r.respond_to? :links
101     @results
102   end
103
104   def to_ary
105     results
106   end
107
108   def each(&block)
109     if not @results.nil?
110       @results.each &block
111     else
112       self.each_page do |items|
113         items.each do |i|
114           block.call i
115         end
116       end
117     end
118     self
119   end
120
121   def first
122     results.first
123   end
124
125   def last
126     results.last
127   end
128
129   def [](*x)
130     results.send('[]', *x)
131   end
132
133   def |(x)
134     if x.is_a? Hash
135       self.to_hash | x
136     else
137       results | x.to_ary
138     end
139   end
140
141   def to_hash
142     Hash[self.collect { |x| [x.uuid, x] }]
143   end
144
145   def empty?
146     self.first.nil?
147   end
148
149   def items_available
150     @items_available
151   end
152
153   def result_limit
154     @result_limit
155   end
156
157   def result_offset
158     @result_offset
159   end
160
161   def result_links
162     @result_links
163   end
164
165   # Return links provided with API response that point to the
166   # specified object, and have the specified link_class. If link_class
167   # is false or omitted, return all links pointing to the specified
168   # object.
169   def links_for item_or_uuid, link_class=false
170     return [] if !result_links
171     unless @links_for_uuid
172       @links_for_uuid = {}
173       result_links.each do |link|
174         if link.respond_to? :head_uuid
175           @links_for_uuid[link.head_uuid] ||= []
176           @links_for_uuid[link.head_uuid] << link
177         end
178       end
179     end
180     if item_or_uuid.respond_to? :uuid
181       uuid = item_or_uuid.uuid
182     else
183       uuid = item_or_uuid
184     end
185     (@links_for_uuid[uuid] || []).select do |link|
186       link_class == false or link.link_class == link_class
187     end
188   end
189
190   protected
191
192   def each_page
193     api_params = {
194       _method: 'GET'
195     }
196     api_params[:where] = @cond if @cond
197     api_params[:eager] = '1' if @eager
198     api_params[:limit] = @limit if @limit
199     api_params[:select] = @select if @select
200     api_params[:order] = @orderby_spec if @orderby_spec
201     api_params[:filters] = @filters if @filters
202
203     item_count = 0
204     offset = @offset || 0
205
206     begin
207       api_params[:offset] = offset
208       api_params[:limit] = (@limit - item_count) if @limit
209
210       res = arvados_api_client.api(@resource_class, '', api_params,
211                                    arvados_api_token: @arvados_api_token,
212                                    reader_tokens: @reader_tokens)
213       items = arvados_api_client.unpack_api_response res
214
215       break if items.nil? or not items.any?
216
217       @items_available = items.items_available if items.respond_to? :items_available
218       @result_limit = items.limit if items.respond_to? :limit
219       @result_offset = items.offset if items.respond_to? :offset
220
221       item_count += items.size
222       offset = @result_offset + items.size
223
224       yield items
225
226       break if @limit.is_a? Integer and item_count >= @limit
227       break if items.respond_to? :items_available and offset >= items.items_available
228     end while @fetch_multiple_pages
229     self
230   end
231
232 end