X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/190d0f110dab5d51dcfa7b13ff658583fdd21a5d..0f644e242ef37c911ad3dc25aca8135c339de349:/apps/workbench/app/models/arvados_resource_list.rb diff --git a/apps/workbench/app/models/arvados_resource_list.rb b/apps/workbench/app/models/arvados_resource_list.rb index 1a3c6b7e3c..9ba61eaba0 100644 --- a/apps/workbench/app/models/arvados_resource_list.rb +++ b/apps/workbench/app/models/arvados_resource_list.rb @@ -1,9 +1,18 @@ +# Copyright (C) The Arvados Authors. All rights reserved. +# +# SPDX-License-Identifier: AGPL-3.0 + class ArvadosResourceList include ArvadosApiClientHelper include Enumerable + attr_reader :resource_class + def initialize resource_class=nil @resource_class = resource_class + @fetch_multiple_pages = true + @arvados_api_token = Thread.current[:arvados_api_token] + @reader_tokens = Thread.current[:reader_tokens] end def eager(bool=true) @@ -11,7 +20,25 @@ class ArvadosResourceList self end + def distinct(bool=true) + @distinct = bool + self + end + + def include_trash(option=nil) + @include_trash = option + self + end + + def recursive(option=nil) + @recursive = option + self + end + def limit(max_results) + if not max_results.nil? and not max_results.is_a? Integer + raise ArgumentError("argument to limit() must be an Integer or nil") + end @limit = max_results self end @@ -44,17 +71,17 @@ class ArvadosResourceList end def where(cond) - cond = cond.dup - cond.keys.each do |uuid_key| - if cond[uuid_key] and (cond[uuid_key].is_a? Array or - cond[uuid_key].is_a? ArvadosBase) + @cond = cond.dup + @cond.keys.each do |uuid_key| + if @cond[uuid_key] and (@cond[uuid_key].is_a? Array or + @cond[uuid_key].is_a? ArvadosBase) # Coerce cond[uuid_key] to an array of uuid strings. This # allows caller the convenience of passing an array of real # objects and uuids in cond[uuid_key]. - if !cond[uuid_key].is_a? Array - cond[uuid_key] = [cond[uuid_key]] + if !@cond[uuid_key].is_a? Array + @cond[uuid_key] = [@cond[uuid_key]] end - cond[uuid_key] = cond[uuid_key].collect do |item| + @cond[uuid_key] = @cond[uuid_key].collect do |item| if item.is_a? ArvadosBase item.uuid else @@ -63,52 +90,61 @@ class ArvadosResourceList end end end - cond.keys.select { |x| x.match /_kind$/ }.each do |kind_key| - if cond[kind_key].is_a? Class - cond = cond.merge({ kind_key => 'arvados#' + arvados_api_client.class_kind(cond[kind_key]) }) + @cond.keys.select { |x| x.match /_kind$/ }.each do |kind_key| + if @cond[kind_key].is_a? Class + @cond = @cond.merge({ kind_key => 'arvados#' + arvados_api_client.class_kind(@cond[kind_key]) }) end end - api_params = { - _method: 'GET', - where: cond - } - api_params[:eager] = '1' if @eager - api_params[:limit] = @limit if @limit - api_params[:offset] = @offset if @offset - api_params[:select] = @select if @select - api_params[:order] = @orderby_spec if @orderby_spec - api_params[:filters] = @filters if @filters - res = arvados_api_client.api @resource_class, '', api_params - @results = arvados_api_client.unpack_api_response res + self + end + + # with_count sets the 'count' parameter to 'exact' or 'none' -- see + # https://doc.arvados.org/api/methods.html#index + def with_count(count_param='exact') + @count = count_param + self + end + + def fetch_multiple_pages(f) + @fetch_multiple_pages = f self end def results - self.where({}) if !@results + if !@results + @results = [] + self.each_page do |r| + @results.concat r + end + end @results end def results=(r) @results = r + @items_available = r.items_available if r.respond_to? :items_available + @result_limit = r.limit if r.respond_to? :limit + @result_offset = r.offset if r.respond_to? :offset + @results end - def all - where({}) + def to_ary + results end def each(&block) - results.each do |m| - block.call m + if not @results.nil? + @results.each &block + else + self.each_page do |items| + items.each do |i| + block.call i + end + end end self end - def collect - results.collect do |m| - yield m - end - end - def first results.first end @@ -129,62 +165,86 @@ class ArvadosResourceList end end - def to_ary - results - end - def to_hash - Hash[results.collect { |x| [x.uuid, x] }] + Hash[self.collect { |x| [x.uuid, x] }] end def empty? - results.empty? + self.first.nil? end def items_available - results.items_available if results.respond_to? :items_available + results + @items_available end def result_limit - results.limit if results.respond_to? :limit + results + @result_limit end def result_offset - results.offset if results.respond_to? :offset + results + @result_offset end - def result_links - results.links if results.respond_to? :links + # Obsolete method retained during api transition. + def links_for item_or_uuid, link_class=false + [] end - # Return links provided with API response that point to the - # specified object, and have the specified link_class. If link_class - # is false or omitted, return all links pointing to the specified - # object. - def links_for item_or_uuid, link_class=false - return [] if !result_links - unless @links_for_uuid - @links_for_uuid = {} - result_links.each do |link| - if link.respond_to? :head_uuid - @links_for_uuid[link.head_uuid] ||= [] - @links_for_uuid[link.head_uuid] << link - end - end - end - if item_or_uuid.respond_to? :uuid - uuid = item_or_uuid.uuid - else - uuid = item_or_uuid - end - (@links_for_uuid[uuid] || []).select do |link| - link_class == false or link.link_class == link_class + protected + + def each_page + api_params = { + _method: 'GET' + } + api_params[:count] = @count if @count + api_params[:where] = @cond if @cond + api_params[:eager] = '1' if @eager + api_params[:select] = @select if @select + api_params[:order] = @orderby_spec if @orderby_spec + api_params[:filters] = @filters if @filters + api_params[:distinct] = @distinct if @distinct + api_params[:include_trash] = @include_trash if @include_trash + if @fetch_multiple_pages + # Default limit to (effectively) api server's MAX_LIMIT + api_params[:limit] = 2**(0.size*8 - 1) - 1 end - end - # Note: this arbitrarily chooses one of (possibly) multiple names. - def name_for item_or_uuid - links_for(item_or_uuid, 'name').first.andand.name + item_count = 0 + offset = @offset || 0 + @result_limit = nil + @result_offset = nil + + begin + api_params[:offset] = offset + api_params[:limit] = (@limit - item_count) if @limit + + res = arvados_api_client.api(@resource_class, '', api_params, + arvados_api_token: @arvados_api_token, + reader_tokens: @reader_tokens) + items = arvados_api_client.unpack_api_response res + + @items_available = items.items_available if items.respond_to?(:items_available) + @result_limit = items.limit if (@fetch_multiple_pages == false) and items.respond_to?(:limit) + @result_offset = items.offset if (@fetch_multiple_pages == false) and items.respond_to?(:offset) + + break if items.nil? or not items.any? + + item_count += items.size + if items.respond_to?(:offset) + offset = items.offset + items.size + else + offset = item_count + end + + yield items + + break if @limit and item_count >= @limit + break if items.respond_to? :items_available and offset >= items.items_available + end while @fetch_multiple_pages + self end end