Merge branch 'master' into 14946-ruby-2.5
[arvados.git] / services / api / app / controllers / application_controller.rb
index 3cfe5b54fda2a2a90afa168938ab5ff9e4760064..e07a5aca79b5a310d320c6901df9c5a82326465a 100644 (file)
@@ -33,28 +33,26 @@ class ApplicationController < ActionController::Base
 
   ERROR_ACTIONS = [:render_error, :render_not_found]
 
-  around_filter :set_current_request_id
-  before_filter :disable_api_methods
-  before_filter :set_cors_headers
-  before_filter :respond_with_json_by_default
-  before_filter :remote_ip
-  before_filter :load_read_auths
-  before_filter :require_auth_scope, except: ERROR_ACTIONS
-
-  before_filter :catch_redirect_hint
-  before_filter(:find_object_by_uuid,
+  around_action :set_current_request_id
+  before_action :disable_api_methods
+  before_action :set_cors_headers
+  before_action :respond_with_json_by_default
+  before_action :remote_ip
+  before_action :load_read_auths
+  before_action :require_auth_scope, except: ERROR_ACTIONS
+
+  before_action :catch_redirect_hint
+  before_action :load_required_parameters
+  before_action(:find_object_by_uuid,
                 except: [:index, :create] + ERROR_ACTIONS)
-  before_filter :load_required_parameters
-  before_filter :load_limit_offset_order_params, only: [:index, :contents]
-  before_filter :load_where_param, only: [:index, :contents]
-  before_filter :load_filters_param, only: [:index, :contents]
-  before_filter :find_objects_for_index, :only => :index
-  before_filter :reload_object_before_update, :only => :update
-  before_filter(:render_404_if_no_object,
+  before_action :load_limit_offset_order_params, only: [:index, :contents]
+  before_action :load_where_param, only: [:index, :contents]
+  before_action :load_filters_param, only: [:index, :contents]
+  before_action :find_objects_for_index, :only => :index
+  before_action :reload_object_before_update, :only => :update
+  before_action(:render_404_if_no_object,
                 except: [:index, :create] + ERROR_ACTIONS)
 
-  theme Rails.configuration.arvados_theme
-
   attr_writer :resource_attrs
 
   begin
@@ -83,14 +81,11 @@ class ApplicationController < ActionController::Base
 
   def default_url_options
     options = {}
-    if Rails.configuration.host
-      options[:host] = Rails.configuration.host
-    end
-    if Rails.configuration.port
-      options[:port] = Rails.configuration.port
-    end
-    if Rails.configuration.protocol
-      options[:protocol] = Rails.configuration.protocol
+    if Rails.configuration.Services.Controller.ExternalURL != URI("")
+      exturl = Rails.configuration.Services.Controller.ExternalURL
+      options[:host] = exturl.host
+      options[:port] = exturl.port
+      options[:protocol] = exturl.scheme
     end
     options
   end
@@ -164,8 +159,23 @@ class ApplicationController < ActionController::Base
     send_error("Path not found", status: 404)
   end
 
+  def render_accepted
+    send_json ({accepted: true}), status: 202
+  end
+
   protected
 
+  def bool_param(pname)
+    if params.include?(pname)
+      if params[pname].is_a?(Boolean)
+        return params[pname]
+      else
+        logger.warn "Warning: received non-boolean parameter '#{pname}' on #{self.class.inspect}."
+      end
+    end
+    false
+  end
+
   def send_error(*args)
     if args.last.is_a? Hash
       err = args.pop
@@ -183,15 +193,15 @@ class ApplicationController < ActionController::Base
     # The obvious render(json: ...) forces a slow JSON encoder. See
     # #3021 and commit logs. Might be fixed in Rails 4.1.
     render({
-             text: SafeJSON.dump(response).html_safe,
+             plain: SafeJSON.dump(response).html_safe,
              content_type: 'application/json'
            }.merge opts)
   end
 
   def find_objects_for_index
     @objects ||= model_class.readable_by(*@read_users, {
-      :include_trash => (params[:include_trash] || 'untrash' == action_name),
-      :include_old_versions => params[:include_old_versions]
+      :include_trash => (bool_param(:include_trash) || 'untrash' == action_name),
+      :include_old_versions => bool_param(:include_old_versions)
     })
     apply_where_limit_order_params
   end
@@ -238,7 +248,7 @@ class ApplicationController < ActionController::Base
               conditions[0] << " and #{ar_table_name}.#{attr} in (?)"
               conditions << value
             end
-          elsif value.is_a? String or value.is_a? Fixnum or value == true or value == false
+          elsif value.is_a? String or value.is_a? Integer or value == true or value == false
             conditions[0] << " and #{ar_table_name}.#{attr}=?"
             conditions << value
           elsif value.is_a? Hash
@@ -279,7 +289,7 @@ class ApplicationController < ActionController::Base
     @objects = @objects.order(@orders.join ", ") if @orders.any?
     @objects = @objects.limit(@limit)
     @objects = @objects.offset(@offset)
-    @objects = @objects.uniq(@distinct) if not @distinct.nil?
+    @objects = @objects.distinct(@distinct) if not @distinct.nil?
   end
 
   # limit_database_read ensures @objects (which must be an
@@ -302,7 +312,7 @@ class ApplicationController < ActionController::Base
       limit_query.each do |record|
         new_limit += 1
         read_total += record.read_length.to_i
-        if read_total >= Rails.configuration.max_index_database_read
+        if read_total >= Rails.configuration.API.MaxIndexDatabaseRead
           new_limit -= 1 if new_limit > 1
           @limit = new_limit
           break
@@ -319,10 +329,12 @@ class ApplicationController < ActionController::Base
   def resource_attrs
     return @attrs if @attrs
     @attrs = params[resource_name]
-    if @attrs.is_a? String
+    if @attrs.nil?
+      @attrs = {}
+    elsif @attrs.is_a? String
       @attrs = Oj.strict_load @attrs, symbol_keys: true
     end
-    unless @attrs.is_a? Hash
+    unless [Hash, ActionController::Parameters].include? @attrs.class
       message = "No #{resource_name}"
       if resource_name.index('_')
         message << " (or #{resource_name.camelcase(:lower)})"
@@ -413,8 +425,7 @@ class ApplicationController < ActionController::Base
   end
 
   def disable_api_methods
-    if Rails.configuration.disable_api_methods.
-        include?(controller_name + "." + action_name)
+    if Rails.configuration.API.DisabledAPIs.include?(controller_name + "." + action_name)
       send_error("Disabled", status: 404)
     end
   end
@@ -427,8 +438,8 @@ class ApplicationController < ActionController::Base
   end
 
   def respond_with_json_by_default
-    html_index = request.accepts.index(Mime::HTML)
-    if html_index.nil? or request.accepts[0...html_index].include?(Mime::JSON)
+    html_index = request.accepts.index(Mime[:html])
+    if html_index.nil? or request.accepts[0...html_index].include?(Mime[:json])
       request.format = :json
     end
   end
@@ -468,21 +479,31 @@ class ApplicationController < ActionController::Base
   end
 
   def load_json_value(hash, key, must_be_class=nil)
-    if hash[key].is_a? String
-      hash[key] = SafeJSON.load(hash[key])
-      if must_be_class and !hash[key].is_a? must_be_class
-        raise TypeError.new("parameter #{key.to_s} must be a #{must_be_class.to_s}")
-      end
+    return if hash[key].nil?
+
+    val = hash[key]
+    if val.is_a? ActionController::Parameters
+      val = val.to_unsafe_hash
+    elsif val.is_a? String
+      val = SafeJSON.load(val)
+      hash[key] = val
+    end
+    # When assigning a Hash to an ActionController::Parameters and then
+    # retrieve it, we get another ActionController::Parameters instead of
+    # a Hash. This doesn't happen with other types. This is why 'val' is
+    # being used to do type checking below.
+    if must_be_class and !val.is_a? must_be_class
+      raise TypeError.new("parameter #{key.to_s} must be a #{must_be_class.to_s}")
     end
   end
 
   def self.accept_attribute_as_json(attr, must_be_class=nil)
-    before_filter lambda { accept_attribute_as_json attr, must_be_class }
+    before_action lambda { accept_attribute_as_json attr, must_be_class }
   end
   accept_attribute_as_json :properties, Hash
   accept_attribute_as_json :info, Hash
   def accept_attribute_as_json(attr, must_be_class)
-    if params[resource_name] and resource_attrs.is_a? Hash
+    if params[resource_name] and [Hash, ActionController::Parameters].include?(resource_attrs.class)
       if resource_attrs[attr].is_a? Hash
         # Convert symbol keys to strings (in hashes provided by
         # resource_attrs)
@@ -495,7 +516,7 @@ class ApplicationController < ActionController::Base
   end
 
   def self.accept_param_as_json(key, must_be_class=nil)
-    prepend_before_filter lambda { load_json_value(params, key, must_be_class) }
+    prepend_before_action lambda { load_json_value(params, key, must_be_class) }
   end
   accept_param_as_json :reader_tokens, Array
 
@@ -519,7 +540,7 @@ class ApplicationController < ActionController::Base
       if @objects.respond_to? :except
         list[:items_available] = @objects.
           except(:limit).except(:offset).
-          count(:id, distinct: true)
+          distinct.count(:id)
       end
     when 'none'
     else