<pre>
GET https://{{ site.arvados_api_host }}/arvados/v1/groups?filters=[["owner_uuid","=","xyzzy-tpzed-a4lcehql0dv2u25"]]
-
+
POST https://{{ site.arvados_api_host }}/arvados/v1/groups
_method=GET
filters=[["owner_uuid","=","xyzzy-tpzed-a4lcehql0dv2u25"]]
table(table table-bordered table-condensed).
|*Parameter name*|*Value*|*Description*|
-|limit|integer|Maximum number of resources to return|
-|filters|array|Conditions for selecting resources to return|
+|limit |integer|Maximum number of resources to return|
+|offset |integer|Skip the first 'offset' objects|
+|filters |array |Conditions for selecting resources to return|
+|order |array |List of fields to use to determine sorting order for returned objects|
+|select |array |Specify which fields to return|
+|distinct|boolean|true: (default) do not return duplicate objects<br> false: permitted to return duplicates|
h2. Create
+module ApiTemplateOverride
+ def allowed_to_render?(fieldset, field, model, options)
+ if options[:select]
+ return options[:select].include? field.to_s
+ end
+ super
+ end
+end
+
+class ActsAsApi::ApiTemplate
+ prepend ApiTemplateOverride
+end
+
require 'load_param'
require 'record_filters'
ERROR_ACTIONS = [:render_error, :render_not_found]
+
respond_to :json
protect_from_forgery
attr_accessor :resource_attrs
def index
- @objects.uniq!(&:id)
+ @objects.uniq!(&:id) if @select.nil? or @select.include? "id"
if params[:eager] and params[:eager] != '0' and params[:eager] != 0 and params[:eager] != ''
@objects.each(&:eager_load_associations)
end
end
end
+ @objects = @objects.select(@select.map { |s| "#{table_name}.#{ActiveRecord::Base.connection.quote_column_name s.to_s}" }.join ", ") if @select
@objects = @objects.order(@orders.join ", ") if @orders.any?
@objects = @objects.limit(@limit)
@objects = @objects.offset(@offset)
+ @objects = @objects.uniq(@distinct) if not @distinct.nil?
end
def resource_attrs
:self_link => "",
:offset => @offset,
:limit => @limit,
- :items => @objects.as_api_response(nil)
+ :items => @objects.as_api_response(nil, {select: @select})
}
if @objects.respond_to? :except
@object_list[:items_available] = @objects.
{
filters: { type: 'array', required: false },
where: { type: 'object', required: false },
- order: { type: 'string', required: false },
+ order: { type: 'array', required: false },
+ select: { type: 'array', required: false },
+ distinct: { type: 'boolean', required: false },
limit: { type: 'integer', required: false, default: DEFAULT_LIMIT },
offset: { type: 'integer', required: false, default: 0 },
}
cancelled_at: nil,
success: nil
})
- params[:order] ||= 'priority desc, created_at'
+ params[:order] ||= ['priority desc', 'created_at']
find_objects_for_index
index
end
type: "string",
description: "Order in which to return matching #{k.to_s.underscore.pluralize}.",
location: "query"
+ },
+ select: {
+ type: "array",
+ description: "Select which fields to return",
+ location: "query"
+ },
+ distinct: {
+ type: "boolean",
+ description: "Return each distinct object",
+ location: "query"
}
},
response: {
@orders = []
if params[:order]
- params[:order].split(',').each do |order|
+ od = []
+ (case params[:order]
+ when String
+ if params[:order].starts_with? '['
+ od = Oj.load(params[:order])
+ raise unless od.is_a? Array
+ od
+ else
+ params[:order].split(',')
+ end
+ when Array
+ params[:order]
+ else
+ []
+ end).each do |order|
+ order = order.to_s
attr, direction = order.strip.split " "
direction ||= 'asc'
if attr.match /^[a-z][_a-z0-9]+$/ and
end
end
end
+
if @orders.empty?
- @orders << default_orders
+ @orders = default_orders
end
+
+ case params[:select]
+ when Array
+ @select = params[:select]
+ when String
+ begin
+ @select = Oj.load params[:select]
+ raise unless @select.is_a? Array
+ rescue
+ raise ArgumentError.new("Could not parse \"select\" param as an array")
+ end
+ end
+
+ if @select
+ # Any ordering columns must be selected when doing select,
+ # otherwise it is an SQL error, so filter out invaliding orderings.
+ @orders.select! { |o|
+ # match select column against order array entry
+ @select.select { |s| /^#{table_name}.#{s}( (asc|desc))?$/.match o }.any?
+ }
+ end
+
+ @distinct = true if (params[:distinct] == true || params[:distinct] == "true")
+ @distinct = false if (params[:distinct] == false || params[:distinct] == "false")
end
--- /dev/null
+require 'test_helper'
+
+class SelectTest < ActionDispatch::IntegrationTest
+ test "should select just two columns" do
+ get "/arvados/v1/links", {:format => :json, :select => ['uuid', 'link_class']}, auth(:active)
+ assert_response :success
+ assert_equal json_response['items'].count, json_response['items'].select { |i|
+ i.count == 2 and i['uuid'] != nil and i['link_class'] != nil
+ }.count
+ end
+
+ test "fewer distinct than total count" do
+ get "/arvados/v1/links", {:format => :json, :select => ['link_class'], :distinct => false}, auth(:active)
+ assert_response :success
+ links = json_response['items']
+
+ get "/arvados/v1/links", {:format => :json, :select => ['link_class'], :distinct => true}, auth(:active)
+ assert_response :success
+ distinct = json_response['items']
+
+ assert distinct.count < links.count, "distinct count should be less than link count"
+ assert_equal links.uniq.count, distinct.count
+ end
+
+ test "select with order" do
+ get "/arvados/v1/links", {:format => :json, :select => ['uuid'], :order => ["uuid asc"]}, auth(:active)
+ assert_response :success
+
+ assert json_response['items'].length > 0
+
+ p = ""
+ json_response['items'].each do |i|
+ assert i['uuid'] > p
+ p = i['uuid']
+ end
+ end
+
+ test "select two columns with order" do
+ get "/arvados/v1/links", {:format => :json, :select => ['link_class', 'uuid'], :order => ['link_class asc', "uuid desc"]}, auth(:active)
+ assert_response :success
+
+ assert json_response['items'].length > 0
+
+ prev_link_class = ""
+ prev_uuid = "zzzzz-zzzzz-zzzzzzzzzzzzzzz"
+
+ json_response['items'].each do |i|
+ if prev_link_class != i['link_class']
+ prev_uuid = "zzzzz-zzzzz-zzzzzzzzzzzzzzz"
+ end
+
+ assert i['link_class'] >= prev_link_class
+ assert i['uuid'] < prev_uuid
+
+ prev_link_class = i['link_class']
+ prev_uuid = i['uuid']
+ end
+ end
+
+ test "select two columns with old-style order syntax" do
+ get "/arvados/v1/links", {:format => :json, :select => ['link_class', 'uuid'], :order => 'link_class asc, uuid desc'}, auth(:active)
+ assert_response :success
+
+ assert json_response['items'].length > 0
+
+ prev_link_class = ""
+ prev_uuid = "zzzzz-zzzzz-zzzzzzzzzzzzzzz"
+
+ json_response['items'].each do |i|
+ if prev_link_class != i['link_class']
+ prev_uuid = "zzzzz-zzzzz-zzzzzzzzzzzzzzz"
+ end
+
+ assert i['link_class'] >= prev_link_class
+ assert i['uuid'] < prev_uuid
+
+ prev_link_class = i['link_class']
+ prev_uuid = i['uuid']
+ end
+ end
+
+end
module ArvadosTestSupport
def json_response
- @json_response ||= ActiveSupport::JSON.decode @response.body
+ ActiveSupport::JSON.decode @response.body
end
def api_token(api_client_auth_name)