<commit_before>class ApiController
  module Renderer
    #
    # Helper proc for rendering a collection of type specified.
    #
    def render_collection_type(type, id, is_subcollection = false)
      klass = collection_class(type)
      opts  = {
        :name               => type.to_s,
        :is_subcollection   => is_subcollection,
        :resource_actions   => "resource_actions_#{type}",
        :collection_actions => "collection_actions_#{type}"
      }
      if id
        render_resource type, resource_search(id, type, klass), opts
      else
        opts[:count]            = klass.count
        opts[:expand_resources] = expand?(:resources)

        res = collection_search(is_subcollection, type, klass)

        opts[:subcount] = res.length

        render_collection type, res, opts
      end
    end

    #
    # Helper proc to render a collection
    #
    def render_collection(type, resources, opts = {})
      validate_response_format
      render :json => collection_to_jbuilder(type, gen_reftype(type, opts), resources, opts).target!
    end

    #
    # Helper proc to render a single resource
    #
    def render_resource(type, resource, opts = {})
      validate_response_format
      render :json => resource_to_jbuilder(type, gen_reftype(type, opts), resource, opts).target!
    end

    #
    # We want reftype to reflect subcollection if targeting as such.
    #
    def gen_reftype(type, opts)
      opts[:is_subcollection] ? "#{@req[:collection]}/#{@req[:c_id]}/#{type}" : type
    end

    # Methods for Serialization as Jbuilder Objects.

    #
    # Given a resource, return its serialized flavor using Jbuilder
    #
    def collection_to_jbuilder(type, reftype, resources, opts = {})
      Jbuilder.new do |json|
        json.ignore_nil!
        [:name, :count, :subcount].each do |opt_name|
          json.set! "#{opt_name}", opts[opt_name] if opts[opt_name]
        end
        json.resources resources.collect do |resource|
          if opts[:expand_resources]
            add_hash json, resource_to_jbuilder(type, reftype, resource, opts).attributes!
          else
            json.href normalize_href(reftype, resource["id"])
          end
        end
        aspecs = get_aspecs(type, opts[:collection_actions], :collection,
                            :is_subcollection => opts[:is_subcollection], :ref => reftype)
        add_actions(json, aspecs, reftype)
      end
    end

    def resource_to_jbuilder(type, reftype, resource, opts = {})
      reftype = get_reftype(type, reftype, resource, opts)
      json    = Jbuilder.new
      json.ignore_nil!

      normalize_options = {:add_href => true}

      pas = physical_attribute_selection(resource)
      normalize_options[:render_attributes] = pas if pas.present?

      add_hash json, normalize_hash(reftype, resource, normalize_options), :render_resource_attr, resource

      if resource.respond_to?(:attributes)
        expand_virtual_attributes(json, type, resource)
        expand_subcollections(json, type, resource)
      end

      expand_actions(resource, json, type, opts)
      expand_resource_custom_actions(resource, json, type)
      json
    end

    def get_reftype(type, reftype, resource, _opts = {})
      # sometimes we are returning different objects than the posted resource, i.e. request for an order.
      return reftype unless resource.respond_to?(:attributes)

      rclass = resource.class
      if collection_class(type) != rclass
        matched_type, = collection_config.detect do |_collection, spec|
          spec[:klass] && spec[:klass].constantize == rclass
        end
      end
      matched_type || reftype
    end

    #
    # type is the collection type we need to get actions specifications for
    # typed_actions is the optional type specific method to return actions
    # item_type is :collection or :resource
    #
    # opts[:is_subcollection] is true if accessing as subcollection or subresource
    # opts[:ref] is how to identify item, either the reftype of the collection or href of resource
    # opts[:resource] is the object to get action specs for the specific resource
    #
    def get_aspecs(type, typed_actions, item_type, opts = {})
      aspecs = []
      if typed_actions
        aspecs = if respond_to?(typed_actions)
                   send(typed_actions, opts[:is_subcollection], opts[:ref])
                 else
                   gen_action_specs(type, item_type, opts[:is_subcollection], opts[:ref], opts[:resource])
                 end
      end
      aspecs
    end

    #
    # Common proc for adding a child element to the Jbuilder
    #
    def add_child(json, hash)
      json.child! { |js| hash.each { |attr, value| js.set! attr, value } } unless hash.blank?
    end

    #
    # Common proc for adding a hash directly to the Jbuilder
    #
    def add_hash(json, hash, render_attr_proc = nil, resource = nil)
      return if hash.blank?
      hash.each do |attr, value|
        json.set! attr, value if render_attr_proc.nil? || send(render_attr_proc, resource, attr)
      end
    end

    #
    # Render nothing for normal resource deletes.
    #
    def render_normal_destroy
      head :no_content
    end

    #
    # Render nothing for normal update.
    #
    def render_normal_update(type, res = {})
      render_resource type, res
    end

    #
    # Return the response format requested, i.e. :json or raise an error
    #
    def validate_response_format
      accept = request.headers["Accept"]
      return :json if accept.blank? || accept.include?("json") || accept.include?("*/*")
      raise UnsupportedMediaTypeError, "Invalid Response Format #{accept} requested"
    end

    #
    # Method name for optional accessor of virtual attributes
    #
    def virtual_attribute_accessor(type, attr)
      method = "fetch_#{type}_#{attr}"
      respond_to?(method) ? method : nil
    end

    private

    def resource_search(id, type, klass)
      target = respond_to?("find_#{type}") ? public_send("find_#{type}", id) : klass.find(id)
      res = Rbac.filtered([target], :user => @auth_user_obj, :class => klass).first
      raise Forbidden, "Access to the resource #{type}/#{id} is forbidden" unless res
      res
    end

    def collection_search(is_subcollection, type, klass)
      res =
        if is_subcollection
          send("#{type}_query_resource", parent_resource_obj)
        elsif by_tag_param
          klass.find_tagged_with(:all => by_tag_param, :ns  => TAG_NAMESPACE)
        else
          klass.all
        end

      miq_expression = filter_param(klass)

      if miq_expression
        sql, _, attrs = miq_expression.to_sql
        res = res.where(sql) if attrs[:supported_by_sql]<commit_msg>[api] Allow for custom collection search<commit_after>class ApiController
  module Renderer
    #
    # Helper proc for rendering a collection of type specified.
    #
    def render_collection_type(type, id, is_subcollection = false)
      klass = collection_class(type)
      opts  = {
        :name               => type.to_s,
        :is_subcollection   => is_subcollection,
        :resource_actions   => "resource_actions_#{type}",
        :collection_actions => "collection_actions_#{type}"
      }
      if id
        render_resource type, resource_search(id, type, klass), opts
      else
        opts[:count]            = klass.count
        opts[:expand_resources] = expand?(:resources)

        res = collection_search(is_subcollection, type, klass)

        opts[:subcount] = res.length

        render_collection type, res, opts
      end
    end

    #
    # Helper proc to render a collection
    #
    def render_collection(type, resources, opts = {})
      validate_response_format
      render :json => collection_to_jbuilder(type, gen_reftype(type, opts), resources, opts).target!
    end

    #
    # Helper proc to render a single resource
    #
    def render_resource(type, resource, opts = {})
      validate_response_format
      render :json => resource_to_jbuilder(type, gen_reftype(type, opts), resource, opts).target!
    end

    #
    # We want reftype to reflect subcollection if targeting as such.
    #
    def gen_reftype(type, opts)
      opts[:is_subcollection] ? "#{@req[:collection]}/#{@req[:c_id]}/#{type}" : type
    end

    # Methods for Serialization as Jbuilder Objects.

    #
    # Given a resource, return its serialized flavor using Jbuilder
    #
    def collection_to_jbuilder(type, reftype, resources, opts = {})
      Jbuilder.new do |json|
        json.ignore_nil!
        [:name, :count, :subcount].each do |opt_name|
          json.set! "#{opt_name}", opts[opt_name] if opts[opt_name]
        end
        json.resources resources.collect do |resource|
          if opts[:expand_resources]
            add_hash json, resource_to_jbuilder(type, reftype, resource, opts).attributes!
          else
            json.href normalize_href(reftype, resource["id"])
          end
        end
        aspecs = get_aspecs(type, opts[:collection_actions], :collection,
                            :is_subcollection => opts[:is_subcollection], :ref => reftype)
        add_actions(json, aspecs, reftype)
      end
    end

    def resource_to_jbuilder(type, reftype, resource, opts = {})
      reftype = get_reftype(type, reftype, resource, opts)
      json    = Jbuilder.new
      json.ignore_nil!

      normalize_options = {:add_href => true}

      pas = physical_attribute_selection(resource)
      normalize_options[:render_attributes] = pas if pas.present?

      add_hash json, normalize_hash(reftype, resource, normalize_options), :render_resource_attr, resource

      if resource.respond_to?(:attributes)
        expand_virtual_attributes(json, type, resource)
        expand_subcollections(json, type, resource)
      end

      expand_actions(resource, json, type, opts)
      expand_resource_custom_actions(resource, json, type)
      json
    end

    def get_reftype(type, reftype, resource, _opts = {})
      # sometimes we are returning different objects than the posted resource, i.e. request for an order.
      return reftype unless resource.respond_to?(:attributes)

      rclass = resource.class
      if collection_class(type) != rclass
        matched_type, = collection_config.detect do |_collection, spec|
          spec[:klass] && spec[:klass].constantize == rclass
        end
      end
      matched_type || reftype
    end

    #
    # type is the collection type we need to get actions specifications for
    # typed_actions is the optional type specific method to return actions
    # item_type is :collection or :resource
    #
    # opts[:is_subcollection] is true if accessing as subcollection or subresource
    # opts[:ref] is how to identify item, either the reftype of the collection or href of resource
    # opts[:resource] is the object to get action specs for the specific resource
    #
    def get_aspecs(type, typed_actions, item_type, opts = {})
      aspecs = []
      if typed_actions
        aspecs = if respond_to?(typed_actions)
                   send(typed_actions, opts[:is_subcollection], opts[:ref])
                 else
                   gen_action_specs(type, item_type, opts[:is_subcollection], opts[:ref], opts[:resource])
                 end
      end
      aspecs
    end

    #
    # Common proc for adding a child element to the Jbuilder
    #
    def add_child(json, hash)
      json.child! { |js| hash.each { |attr, value| js.set! attr, value } } unless hash.blank?
    end

    #
    # Common proc for adding a hash directly to the Jbuilder
    #
    def add_hash(json, hash, render_attr_proc = nil, resource = nil)
      return if hash.blank?
      hash.each do |attr, value|
        json.set! attr, value if render_attr_proc.nil? || send(render_attr_proc, resource, attr)
      end
    end

    #
    # Render nothing for normal resource deletes.
    #
    def render_normal_destroy
      head :no_content
    end

    #
    # Render nothing for normal update.
    #
    def render_normal_update(type, res = {})
      render_resource type, res
    end

    #
    # Return the response format requested, i.e. :json or raise an error
    #
    def validate_response_format
      accept = request.headers["Accept"]
      return :json if accept.blank? || accept.include?("json") || accept.include?("*/*")
      raise UnsupportedMediaTypeError, "Invalid Response Format #{accept} requested"
    end

    #
    # Method name for optional accessor of virtual attributes
    #
    def virtual_attribute_accessor(type, attr)
      method = "fetch_#{type}_#{attr}"
      respond_to?(method) ? method : nil
    end

    private

    def resource_search(id, type, klass)
      target = respond_to?("find_#{type}") ? public_send("find_#{type}", id) : klass.find(id)
      res = Rbac.filtered([target], :user => @auth_user_obj, :class => klass).first
      raise Forbidden, "Access to the resource #{type}/#{id} is forbidden" unless res
      res
    end

    def collection_search(is_subcollection, type, klass)
      res =
        if is_subcollection
          send("#{type}_query_resource", parent_resource_obj)
        elsif by_tag_param
          klass.find_tagged_with(:all => by_tag_param, :ns  => TAG_NAMESPACE)
        else
          klass.all
        end

      res = res.where(public_send("#{type}_search_conditions")) if respond_to?("#{type}_search_conditions")

      miq_expression = filter_param(klass)

      if miq_expression
        sql, _, attrs = miq_expression.to_sql
        res = res.where(sql) if attrs[:supported_by_sql]
{'fraction_non_alphanumeric': 0.09386521162070491, 'fraction_numerical': 0.0, 'mean_word_length': 2.690612244897959, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '965176', 'n_tokens_mistral': 3966, 'n_tokens_neox': 3911, 'n_words': 1084}