closes #9824
[arvados.git] / services / api / app / controllers / arvados / v1 / groups_controller.rb
1 class Arvados::V1::GroupsController < ApplicationController
2
3   def self._contents_requires_parameters
4     params = _index_requires_parameters.
5       merge({
6               uuid: {
7                 type: 'string', required: false, default: nil
8               },
9             })
10     params.delete(:select)
11     params
12   end
13
14   def render_404_if_no_object
15     if params[:action] == 'contents'
16       if !params[:uuid]
17         # OK!
18         @object = nil
19         true
20       elsif @object
21         # Project group
22         true
23       elsif (@object = User.where(uuid: params[:uuid]).first)
24         # "Home" pseudo-project
25         true
26       else
27         super
28       end
29     else
30       super
31     end
32   end
33
34   def contents
35     load_searchable_objects
36     send_json({
37       :kind => "arvados#objectList",
38       :etag => "",
39       :self_link => "",
40       :offset => @offset,
41       :limit => @limit,
42       :items_available => @items_available,
43       :items => @objects.as_api_response(nil)
44     })
45   end
46
47   protected
48
49   def load_searchable_objects
50     all_objects = []
51     @items_available = 0
52
53     # Trick apply_where_limit_order_params into applying suitable
54     # per-table values. *_all are the real ones we'll apply to the
55     # aggregate set.
56     limit_all = @limit
57     offset_all = @offset
58     # save the orders from the current request as determined by load_param,
59     # but otherwise discard them because we're going to be getting objects
60     # from many models
61     request_orders = @orders.clone
62     @orders = []
63
64     request_filters = @filters
65
66     klasses = [Group,
67      Job, PipelineInstance, PipelineTemplate, ContainerRequest, Workflow,
68      Collection,
69      Human, Specimen, Trait]
70
71     table_names = klasses.map(&:table_name)
72     request_filters.each do |col, op, val|
73       if col.index('.') && !table_names.include?(col.split('.', 2)[0])
74         raise ArgumentError.new("Invalid attribute '#{col}' in filter")
75       end
76     end
77
78     klasses.each do |klass|
79       # If the currently requested orders specifically match the
80       # table_name for the current klass, apply that order.
81       # Otherwise, order by recency.
82       request_order =
83         request_orders.andand.find { |r| r =~ /^#{klass.table_name}\./i } ||
84         klass.default_orders.join(", ")
85
86       @select = nil
87       where_conds = {}
88       where_conds[:owner_uuid] = @object.uuid if @object
89       if klass == Collection
90         @select = klass.selectable_attributes - ["manifest_text"]
91       elsif klass == Group
92         where_conds[:group_class] = "project"
93       end
94
95       @filters = request_filters.map do |col, op, val|
96         if !col.index('.')
97           [col, op, val]
98         elsif (col = col.split('.', 2))[0] == klass.table_name
99           [col[1], op, val]
100         else
101           nil
102         end
103       end.compact
104
105       @objects = klass.readable_by(*@read_users).
106         order(request_order).where(where_conds)
107       @limit = limit_all - all_objects.count
108       apply_where_limit_order_params klass
109       klass_object_list = object_list
110       klass_items_available = klass_object_list[:items_available] || 0
111       @items_available += klass_items_available
112       @offset = [@offset - klass_items_available, 0].max
113       all_objects += klass_object_list[:items]
114     end
115
116     @objects = all_objects
117     @limit = limit_all
118     @offset = offset_all
119   end
120
121 end