14988: Refactors ArvadosBase class to not subclass ActiveRecord.
[arvados.git] / apps / workbench / app / controllers / application_controller.rb
index ee3ac4d6810588b7d630705a5efe4cd6e08bd6ae..21e9b49fd800fdce05d34e3358eafc9692111e12 100644 (file)
@@ -1,3 +1,7 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
 class ApplicationController < ActionController::Base
   include ArvadosApiClientHelper
   include ApplicationHelper
@@ -7,19 +11,19 @@ class ApplicationController < ActionController::Base
 
   ERROR_ACTIONS = [:render_error, :render_not_found]
 
-  prepend_before_filter :set_current_request_id, except: ERROR_ACTIONS
-  around_filter :thread_clear
-  around_filter :set_thread_api_token
+  around_action :thread_clear
+  around_action :set_current_request_id
+  around_action :set_thread_api_token
   # Methods that don't require login should
-  #   skip_around_filter :require_thread_api_token
-  around_filter :require_thread_api_token, except: ERROR_ACTIONS
-  before_filter :ensure_arvados_api_exists, only: [:index, :show]
-  before_filter :set_cache_buster
-  before_filter :accept_uuid_as_id_param, except: ERROR_ACTIONS
-  before_filter :check_user_agreements, except: ERROR_ACTIONS
-  before_filter :check_user_profile, except: ERROR_ACTIONS
-  before_filter :load_filters_and_paging_params, except: ERROR_ACTIONS
-  before_filter :find_object_by_uuid, except: [:create, :index, :choose] + ERROR_ACTIONS
+  #   skip_around_action :require_thread_api_token
+  around_action :require_thread_api_token, except: ERROR_ACTIONS
+  before_action :ensure_arvados_api_exists, only: [:index, :show]
+  before_action :set_cache_buster
+  before_action :accept_uuid_as_id_param, except: ERROR_ACTIONS
+  before_action :check_user_agreements, except: ERROR_ACTIONS
+  before_action :check_user_profile, except: ERROR_ACTIONS
+  before_action :load_filters_and_paging_params, except: ERROR_ACTIONS
+  before_action :find_object_by_uuid, except: [:create, :index, :choose] + ERROR_ACTIONS
   theme :select_theme
 
   begin
@@ -232,11 +236,18 @@ class ApplicationController < ActionController::Base
       objects = @objects
     end
     if objects.respond_to?(:result_offset) and
-        objects.respond_to?(:result_limit) and
-        objects.respond_to?(:items_available)
+        objects.respond_to?(:result_limit)
       next_offset = objects.result_offset + objects.result_limit
-      if next_offset < objects.items_available
+      if objects.respond_to?(:items_available) and (next_offset < objects.items_available)
         next_offset
+      elsif @objects.results.size > 0 and (params[:count] == 'none' or
+           (params[:controller] == 'search' and params[:action] == 'choose'))
+        last_object_class = @objects.last.class
+        if params['last_object_class'].nil? or params['last_object_class'] == last_object_class.to_s
+          next_offset
+        else
+          @objects.select{|obj| obj.class == last_object_class}.size
+        end
       else
         nil
       end
@@ -342,6 +353,9 @@ class ApplicationController < ActionController::Base
 
   def update
     @updates ||= params[@object.resource_param_name.to_sym]
+    if @updates.is_a? ActionController::Parameters
+      @updates = @updates.to_unsafe_hash
+    end
     @updates.keys.each do |attr|
       if @object.send(attr).is_a? Hash
         if @updates[attr].is_a? String
@@ -350,6 +364,9 @@ class ApplicationController < ActionController::Base
         if params[:merge] || params["merge_#{attr}".to_sym]
           # Merge provided Hash with current Hash, instead of
           # replacing.
+          if @updates[attr].is_a? ActionController::Parameters
+            @updates[attr] = @updates[attr].to_unsafe_hash
+          end
           @updates[attr] = @object.send(attr).with_indifferent_access.
             deep_merge(@updates[attr].with_indifferent_access)
         end
@@ -562,6 +579,19 @@ class ApplicationController < ActionController::Base
     Rails.cache.delete_matched(/^request_#{Thread.current.object_id}_/)
   end
 
+  def set_current_request_id
+    response.headers['X-Request-Id'] =
+      Thread.current[:request_id] =
+      "req-" + Random::DEFAULT.rand(2**128).to_s(36)[0..19]
+    yield
+    Thread.current[:request_id] = nil
+  end
+
+  def append_info_to_payload(payload)
+    super
+    payload[:request_id] = response.headers['X-Request-Id']
+  end
+
   # Set up the thread with the given API token and associated user object.
   def load_api_token(new_token)
     Thread.current[:arvados_api_token] = new_token
@@ -1227,8 +1257,15 @@ class ApplicationController < ActionController::Base
         @objects_for[obj.name] = obj
       end
     else
+      key_prefix = "request_#{Thread.current.object_id}_#{dataclass.to_s}_"
       dataclass.where(uuid: uuids).each do |obj|
         @objects_for[obj.uuid] = obj
+        if dataclass == Collection
+          # The collecions#index defaults to "all attributes except manifest_text"
+          # Hence, this object is not suitable for preloading the find() cache.
+        else
+          Rails.cache.write(key_prefix + obj.uuid, obj.as_json)
+        end
       end
     end
     @objects_for
@@ -1243,13 +1280,50 @@ class ApplicationController < ActionController::Base
     end
   end
 
-  def wiselinks_layout
-    'body'
+  # helper method to get the names of collection files selected
+  helper_method :selected_collection_files
+  def selected_collection_files params
+    link_uuids, coll_ids = params["selection"].partition do |sel_s|
+      ArvadosBase::resource_class_for_uuid(sel_s) == Link
+    end
+
+    unless link_uuids.empty?
+      Link.select([:head_uuid]).where(uuid: link_uuids).each do |link|
+        if ArvadosBase::resource_class_for_uuid(link.head_uuid) == Collection
+          coll_ids << link.head_uuid
+        end
+      end
+    end
+
+    uuids = []
+    pdhs = []
+    source_paths = Hash.new { |hash, key| hash[key] = [] }
+    coll_ids.each do |coll_id|
+      if m = CollectionsHelper.match(coll_id)
+        key = m[1] + m[2]
+        pdhs << key
+        source_paths[key] << m[4]
+      elsif m = CollectionsHelper.match_uuid_with_optional_filepath(coll_id)
+        key = m[1]
+        uuids << key
+        source_paths[key] << m[4]
+      end
+    end
+
+    unless pdhs.empty?
+      Collection.where(portable_data_hash: pdhs.uniq).
+          select([:uuid, :portable_data_hash]).each do |coll|
+        unless source_paths[coll.portable_data_hash].empty?
+          uuids << coll.uuid
+          source_paths[coll.uuid] = source_paths.delete(coll.portable_data_hash)
+        end
+      end
+    end
+
+    [uuids, source_paths]
   end
 
-  def set_current_request_id
-    # Request ID format: '<timestamp>-<9_digits_random_number>'
-    current_request_id = "#{Time.new.to_i}-#{sprintf('%09d', rand(0..10**9-1))}"
-    Thread.current[:current_request_id] = current_request_id
+  def wiselinks_layout
+    'body'
   end
 end