# frozen_string_literal: true

require 'rom/initializer'

module ROM
  class Changeset
    # Associated changesets automatically set up FKs
    #
    # @api public
    class Associated
      extend Initializer

      # @!attribute [r] left
      #   @return [Changeset::Create] Child changeset
      param :left

      # @!attribute [r] associations
      #   @return [Array] List of association identifiers from relation schema
      option :associations

      # Infer association name from an object with a schema
      #
      # This expects other to be an object with a schema that includes a primary key
      # attribute with :source meta information. This makes it work with both structs
      # and relations
      #
      # @see Stateful#associate
      #
      # @api private
      def self.infer_assoc_name(other)
        schema = other.class.schema
        attrs = schema.is_a?(Hash) ? schema.values : schema
        pk = attrs.detect { |attr| attr.meta[:primary_key] }

        if pk
          pk.meta[:source]
        else
          raise ArgumentError, "can't infer association name for #{other}"
        end
      end

      # Commit changeset's composite command
      #
      # @example
      #   task_changeset = tasks.
      #     changeset(title: 'Task One').
      #     associate(user, :user).
      #     commit
      #   # {:id => 1, :user_id => 1, title: 'Task One'}
      #
      # @return [Array<Hash>, Hash]
      #
      # @api public
      def commit
        command.call
      end

      # Associate with other changesets
      #
      # @see Changeset#associate
      #
      # @return [Associated]
      #
      # @api public
      def associate(other, name = Associated.infer_assoc_name(other))
        self.class.new(left, associations: associations.merge(name => other))
      end

      # Create a composed command
      #
      # @example using existing parent data
      #   user_changeset = users.changeset(name: 'Jane')
      #   task_changeset = tasks.changeset(title: 'Task One')
      #
      #   user = users.create(user_changeset)
      #   task = tasks.create(task_changeset.associate(user, :user))
      #
      # @example saving both parent and child in one go
      #   user_changeset = users.changeset(name: 'Jane')
      #   task_changeset = tasks.changeset(title: 'Task One')
      #
      #   task = tasks.create(task_changeset.associate(user, :user))
      #
      # This works *only* with parent => child(ren) changeset hierarchy
      #
      # @return [ROM::Command::Composite]
      #
      # @api public
      def command
        associations.reduce(left.command.curry(left)) do |a, (assoc, other)|
          case other
          when Changeset
            a >> other.command.with_association(assoc).curry(other)
          when Associated
            a >> other.command.with_association(assoc)
          when Array
            raise NotImplementedError, 'Changeset::Associate does not support arrays yet'
          else
            a.with_association(assoc, parent: other)
          end
        end
      end

      # @api private
      def relation
        left.relation
      end
    end
  end
end


def command
        associations.reduce(left.command.curry(left)) do |a, (assoc, other)|
          case other
          when Changeset
            a >> other.command.with_association(assoc).curry(other)
          when Associated
            a >> other.command.with_association(assoc)
          when Array
            raise NotImplementedError, 'Changeset::Associate does not support arrays yet'
          else
            a.with_association(assoc, parent: other)
          end
        end
      end


# frozen_string_literal: true

require 'rom/support/notifications'

require 'rom/changeset/create'
require 'rom/changeset/update'
require 'rom/changeset/delete'

require 'rom/changeset/extensions/relation'

module ROM
  module Plugins
    module Relation
      # Relation plugin which adds `Relation#changeset` method
      #
      # @api public
      module Changeset
        TYPES = {
          create: ROM::Changeset::Create,
          update: ROM::Changeset::Update,
          delete: ROM::Changeset::Delete
        }.freeze

        extend Notifications::Listener

        subscribe('configuration.relations.class.ready') do |event|
          event[:relation].include(InstanceMethods)
        end

        # Relation instance methods provided by the Changeset plugin
        #
        # @api public
        module InstanceMethods
          # Create a changeset for a relation
          #
          # @overload changeset(type, data)
          #   Create a changeset of one of predefined types
          #
          #   @example creating a record
          #     users.
          #       changeset(:create, name: 'Jane').
          #       commit
          #     # => #<ROM::Struct::User id=1 name="Jane">
          #
          #   @example updating a record
          #     users.
          #       by_pk(1).
          #       changeset(:update, name: 'Jack').
          #       commit
          #     # => #<ROM::Struct::User id=1 name="Jane">
          #
          #   @example providing data as a separate step
          #     changeset = users.changeset(:create)
          #     jack = changeset.data(name: 'Jack').commit
          #     jane = changeset.data(name: 'Jane').commit
          #
          #   @example using a command graph
          #     users.
          #       changeset(
          #         :create,
          #         name: "Jane",
          #         posts: [{ title: "Something about aliens" }]
          #       )
          #
          #   @param [Symbol] type The changeset type
          #   @param [Hash] data
          #   @return [Changeset]
          #
          # @overload changeset(changeset_class, data)
          #   @example using a custom changeset class
          #     class NewUser < ROM::Changeset::Create
          #       map do |tuple|
          #         { **tuple, name: tuple.values_at(:first_name, :last_name).join(' ') }
          #       end
          #     end
          #
          #     users.changeset(NewUser, first_name: 'John', last_name: 'Doe').commit
          #
          #   @param [Class] changeset_class A custom changeset class
          #   @return [Changeset]
          #
          # @api public
          def changeset(type, data = EMPTY_HASH)
            klass = type.is_a?(Symbol) ? TYPES.fetch(type) : type

            unless klass < ROM::Changeset
              raise ArgumentError, "+#{type.name}+ must be a Changeset descendant"
            end

            if klass < ROM::Changeset::Stateful
              klass.new(self, __data__: data)
            else
              klass.new(self)
            end
          rescue KeyError
            raise ArgumentError,
                  "+#{type.inspect}+ is not a valid changeset type. " \
                  "Must be one of: #{TYPES.keys.inspect}"
          end
        end
      end
    end
  end
end


# frozen_string_literal: true

require 'rom/repository/relation_reader'

module ROM
  class Repository
    # Class-level APIs for repositories
    #
    # @api public
    module ClassInterface
      # Create a root-repository class and set its root relation
      #
      # @example
      #   # where :users is the relation name in your rom container
      #   class UserRepo < ROM::Repository[:users]
      #   end
      #
      # @param name [Symbol] The relation `register_as` value
      #
      # @return [Class] descendant of ROM::Repository::Root
      #
      # @api public
      def [](name)
        fetch_or_store(name) do
          base =
            if self < Repository::Root # rubocop:disable Style/MinMaxComparison
              self
            else
              Repository::Root
            end

          klass = ::Class.new(base)
          klass.root(name)
          klass
        end
      end

      # Initialize a new repository object, establishing configured relation proxies from
      # the passed container
      #
      # @overload new(container, **options)
      #   Initialize with container as leading parameter
      #
      #   @param [ROM::Container] container Finalized rom container
      #
      #   @param [Hash] options Repository options
      #   @option options [Module] :struct_namespace Custom struct namespace
      #   @option options [Boolean] :auto_struct Enable/Disable auto-struct mapping
      #
      # @overload new(**options)
      #   Inititalize with container as option
      #
      #   @param [Hash] options Repository options
      #   @option options [ROM::Container] :container Finalized rom container
      #   @option options [Module] :struct_namespace Custom struct namespace
      #   @option options [Boolean] :auto_struct Enable/Disable auto-struct mapping
      #
      # @api public
      def new(container = nil, root: Undefined, **options)
        container ||= options.fetch(:container)

        unless relation_reader.added_to?(self)
          include relation_reader.new(
            relations: container.relations.elements.keys,
            cache: container.cache,
            root: root
          )
        end

        super(**options, container: container)
      end

      # Inherits configured relations and commands
      #
      # @api private
      def inherited(klass)
        super

        return if self === Repository # rubocop:disable Style/CaseEquality

        klass.extend(::Dry::Core::Cache)
        klass.commands(*commands)
      end

      # Defines command methods on a root repository
      #
      # @example
      #   class UserRepo < ROM::Repository[:users]
      #     commands :create, update: :by_pk, delete: :by_pk
      #   end
      #
      #   # with custom command plugin
      #   class UserRepo < ROM::Repository[:users]
      #     commands :create, use: :my_command_plugin
      #   end
      #
      #   # with custom mapper
      #   class UserRepo < ROM::Repository[:users]
      #     commands :create, mapper: :my_custom_mapper
      #   end
      #
      # @param [Array<Symbol>] names A list of command names
      # @option :mapper [Symbol] An optional mapper identifier
      # @option :use [Symbol] An optional command plugin identifier
      #
      # @return [Array<Symbol>] A list of defined command names
      #
      # @api public
      #
      # rubocop:disable Metrics/MethodLength
      def commands(*names, mapper: nil, use: nil, plugins_options: EMPTY_HASH, **opts)
        if names.any? || opts.any?
          @commands = names + opts.to_a

          @commands.each do |spec|
            type, *view = Array(spec).flatten

            if view.empty?
              define_command_method(
                type,
                mapper: mapper,
                use: use,
                plugins_options: plugins_options
              )
            else
              define_restricted_command_method(
                type,
                view,
                mapper: mapper,
                use: use,
                plugins_options: plugins_options
              )
            end
          end
        else
          @commands ||= []
        end
      end
      # rubocop:enable Metrics/MethodLength

      # @api public
      def use(plugin, **options)
        ROM.plugin_registry[:repository].fetch(plugin).apply_to(self, **options)
      end

      private

      # @api private
      def define_command_method(type, **opts)
        define_method(type) do |*input|
          if input.size == 1 && input[0].respond_to?(:commit)
            input[0].commit
          else
            root.command(type, **opts).call(*input)
          end
        end
      end

      # @api private
      def define_restricted_command_method(type, views, **opts)
        views.each do |view_name|
          meth_name = views.size > 1 ? :"#{type}_#{view_name}" : type

          define_method(meth_name) do |*args|
            arity = root.__send__(view_name).arity

            view_args = args[0..arity - 1]
            input = args[arity..args.size - 1]

            changeset = input.first

            if changeset.respond_to?(:commit)
              changeset.commit
            else
              root.command(type, **opts).public_send(view_name, *view_args).call(*input)
            end
          end
        end
      end
    end
  end
end


def new(container = nil, root: Undefined, **options)
        container ||= options.fetch(:container)

        unless relation_reader.added_to?(self)
          include relation_reader.new(
            relations: container.relations.elements.keys,
            cache: container.cache,
            root: root
          )
        end

        super(**options, container: container)
      end


def commands(*names, mapper: nil, use: nil, plugins_options: EMPTY_HASH, **opts)
        if names.any? || opts.any?
          @commands = names + opts.to_a

          @commands.each do |spec|
            type, *view = Array(spec).flatten

            if view.empty?
              define_command_method(
                type,
                mapper: mapper,
                use: use,
                plugins_options: plugins_options
              )
            else
              define_restricted_command_method(
                type,
                view,
                mapper: mapper,
                use: use,
                plugins_options: plugins_options
              )
            end
          end
        else
          @commands ||= []
        end
      end


def define_restricted_command_method(type, views, **opts)
        views.each do |view_name|
          meth_name = views.size > 1 ? :"#{type}_#{view_name}" : type

          define_method(meth_name) do |*args|
            arity = root.__send__(view_name).arity

            view_args = args[0..arity - 1]
            input = args[arity..args.size - 1]

            changeset = input.first

            if changeset.respond_to?(:commit)
              changeset.commit
            else
              root.command(type, **opts).public_send(view_name, *view_args).call(*input)
            end
          end
        end
      end


# frozen_string_literal: true

module ROM
  # TODO: finish this in 1.1.0
  #
  # @api private
  class Session
    include Dry::Equalizer(:queue, :status)

    attr_reader :repo

    attr_reader :queue

    attr_reader :status

    def initialize(repo)
      @repo = repo
      @status = :pending
      initialize_queue!
    end

    def add(changeset)
      queue << changeset
      self
    end

    def commit!
      queue.each(&:commit)

      @status = :success

      self
    rescue StandardError => e
      @status = :failure
      raise e
    ensure
      initialize_queue!
    end

    def pending?
      status.equal?(:pending)
    end

    def success?
      status.equal?(:success)
    end

    def failure?
      status.equal?(:failure)
    end

    private

    def initialize_queue!
      @queue = []
    end
  end
end


# frozen_string_literal: true

module Grape
  # An Endpoint is the proxy scope in which all routing
  # blocks are executed. In other words, any methods
  # on the instance level of this class may be called
  # from inside a `get`, `post`, etc.
  class Endpoint
    extend Forwardable
    include Grape::DSL::Settings
    include Grape::DSL::Headers
    include Grape::DSL::InsideRoute

    attr_reader :env, :request, :source, :options, :endpoints

    def_delegators :request, :params, :headers, :cookies
    def_delegator :cookies, :response_cookies

    class << self
      def block_to_unbound_method(block)
        return unless block

        define_method :temp_unbound_method, block
        method = instance_method(:temp_unbound_method)
        remove_method :temp_unbound_method
        method
      end
    end

    # Create a new endpoint.
    # @param new_settings [InheritableSetting] settings to determine the params,
    #   validations, and other properties from.
    # @param options [Hash] attributes of this endpoint
    # @option options path [String or Array] the path to this endpoint, within
    #   the current scope.
    # @option options method [String or Array] which HTTP method(s) can be used
    #   to reach this endpoint.
    # @option options route_options [Hash]
    # @note This happens at the time of API definition, so in this context the
    # endpoint does not know if it will be mounted under a different endpoint.
    # @yield a block defining what your API should do when this endpoint is hit
    def initialize(new_settings, **options, &block)
      self.inheritable_setting = new_settings.point_in_time_copy

      # now +namespace_stackable(:declared_params)+ contains all params defined for
      # this endpoint and its parents, but later it will be cleaned up,
      # see +reset_validations!+ in lib/grape/dsl/validations.rb
      inheritable_setting.route[:declared_params] = inheritable_setting.namespace_stackable[:declared_params].flatten
      inheritable_setting.route[:saved_validations] = inheritable_setting.namespace_stackable[:validations].dup

      inheritable_setting.namespace_stackable[:representations] ||= []
      inheritable_setting.namespace_inheritable[:default_error_status] ||= 500

      @options = options

      @options[:path] = Array(@options[:path])
      @options[:path] << '/' if @options[:path].empty?
      @options[:method] = Array(@options[:method])

      @status = nil
      @stream = nil
      @body = nil
      @source = self.class.block_to_unbound_method(block)
      @before_filter_passed = false
      @endpoints = @options[:app].endpoints if @options[:app].respond_to?(:endpoints)
    end

    # Update our settings from a given set of stackable parameters. Used when
    # the endpoint's API is mounted under another one.
    def inherit_settings(namespace_stackable)
      parent_validations = namespace_stackable[:validations]
      inheritable_setting.route[:saved_validations].concat(parent_validations) if parent_validations.any?
      parent_declared_params = namespace_stackable[:declared_params]
      inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params.any?

      endpoints&.each { |e| e.inherit_settings(namespace_stackable) }
    end

    def routes
      @routes ||= endpoints&.flat_map(&:routes) || to_routes
    end

    def reset_routes!
      endpoints&.each(&:reset_routes!)
      @namespace = nil
      @routes = nil
    end

    def mount_in(router)
      if endpoints
        compile!
        return endpoints.each { |e| e.mount_in(router) }
      end

      reset_routes!
      compile!
      routes.each do |route|
        router.append(route.apply(self))
        next if inheritable_setting.namespace_inheritable[:do_not_route_head] || route.request_method != Rack::GET

        route.dup.then do |head_route|
          head_route.convert_to_head_request!
          router.append(head_route.apply(self))
        end
      end
    end

    def namespace
      @namespace ||= Namespace.joined_space_path(inheritable_setting.namespace_stackable[:namespace])
    end

    def call(env)
      dup.call!(env)
    end

    def call!(env)
      env[Grape::Env::API_ENDPOINT] = self
      @env = env
      # this adds the helpers only to the instance
      singleton_class.include(@helpers) if @helpers
      @app.call(env)
    end

    def ==(other)
      other.is_a?(self.class) &&
        options == other.options &&
        inheritable_setting.to_hash == other.inheritable_setting.to_hash
    end
    alias eql? ==

    # The purpose of this override is solely for stripping internals when an error occurs while calling
    # an endpoint through an api. See https://github.com/ruby-grape/grape/issues/2398
    # Otherwise, it calls super.
    def inspect
      return super unless env

      "#{self.class} in '#{route.origin}' endpoint"
    end

    protected

    def run
      ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env:) do
        @request = Grape::Request.new(env, build_params_with: inheritable_setting.namespace_inheritable[:build_params_with])
        begin
          run_filters befores, :before
          @before_filter_passed = true

          if env.key?(Grape::Env::GRAPE_ALLOWED_METHODS)
            header['Allow'] = env[Grape::Env::GRAPE_ALLOWED_METHODS].join(', ')
            raise Grape::Exceptions::MethodNotAllowed.new(header) unless options?

            header 'Allow', header['Allow']
            response_object = ''
            status 204
          else
            run_filters before_validations, :before_validation
            run_validators(request:)
            run_filters after_validations, :after_validation
            response_object = execute
          end

          run_filters afters, :after
          build_response_cookies

          # status verifies body presence when DELETE
          @body ||= response_object

          # The body commonly is an Array of Strings, the application instance itself, or a Stream-like object
          response_object = stream || [body]

          [status, header, response_object]
        ensure
          run_filters finallies, :finally
        end
      end
    end

    def execute
      return unless source

      ActiveSupport::Notifications.instrument('endpoint_render.grape', endpoint: self) do
        source.bind_call(self)
      end
    end

    def run_validators(request:)
      validators = inheritable_setting.route[:saved_validations]
      return if validators.empty?

      validation_errors = []

      Grape::Validations::ParamScopeTracker.track do
        ActiveSupport::Notifications.instrument('endpoint_run_validators.grape', endpoint: self, validators:, request:) do
          validators.each do |validator|
            validator.validate(request)
          rescue Grape::Exceptions::Validation => e
            validation_errors << e
            break if validator.fail_fast?
          rescue Grape::Exceptions::ValidationArrayErrors => e
            validation_errors.concat e.errors
            break if validator.fail_fast?
          end
        end
      end

      raise(Grape::Exceptions::ValidationErrors.new(errors: validation_errors, headers: header)) if validation_errors.any?
    end

    def run_filters(filters, type = :other)
      return if filters.blank?

      ActiveSupport::Notifications.instrument('endpoint_run_filters.grape', endpoint: self, filters:, type:) do
        filters.each { |filter| instance_eval(&filter) }
      end
    end

    %i[befores before_validations after_validations afters finallies].each do |method|
      define_method method do
        inheritable_setting.namespace_stackable[method]
      end
    end

    def options?
      options[:options_route_enabled] &&
        env[Rack::REQUEST_METHOD] == Rack::OPTIONS
    end

    private

    attr_reader :before_filter_passed

    def compile!
      @app = options[:app] || build_stack
      @helpers = build_helpers
    end

    def to_routes
      route_options = options[:route_options]
      default_route_options = prepare_default_route_attributes(route_options)
      complete_route_options = route_options.merge(default_route_options)
      path_settings = prepare_default_path_settings

      options[:method].flat_map do |method|
        options[:path].map do |path|
          prepared_path = Path.new(path, default_route_options[:namespace], path_settings)
          pattern = Grape::Router::Pattern.new(
            origin: prepared_path.origin,
            suffix: prepared_path.suffix,
            anchor: default_route_options[:anchor],
            params: route_options[:params],
            format: options[:format],
            version: default_route_options[:version],
            requirements: default_route_options[:requirements]
          )
          Grape::Router::Route.new(self, method, pattern, complete_route_options)
        end
      end
    end

    def prepare_default_route_attributes(route_options)
      {
        namespace:,
        version: prepare_version(inheritable_setting.namespace_inheritable[:version]),
        requirements: prepare_routes_requirements(route_options[:requirements]),
        prefix: inheritable_setting.namespace_inheritable[:root_prefix],
        anchor: route_options.fetch(:anchor, true),
        settings: inheritable_setting.route.except(:declared_params, :saved_validations),
        forward_match: options[:forward_match]
      }
    end

    def prepare_default_path_settings
      namespace_stackable_hash = inheritable_setting.namespace_stackable.to_hash
      namespace_inheritable_hash = inheritable_setting.namespace_inheritable.to_hash
      namespace_stackable_hash.merge!(namespace_inheritable_hash)
    end

    def prepare_routes_requirements(route_options_requirements)
      namespace_requirements = inheritable_setting.namespace_stackable[:namespace].filter_map(&:requirements)
      namespace_requirements << route_options_requirements if route_options_requirements.present?
      namespace_requirements.reduce({}, :merge)
    end

    def prepare_version(namespace_inheritable_version)
      return if namespace_inheritable_version.blank?

      namespace_inheritable_version.length == 1 ? namespace_inheritable_version.first : namespace_inheritable_version
    end

    def build_stack
      stack = Grape::Middleware::Stack.new

      content_types = inheritable_setting.namespace_stackable_with_hash(:content_types)
      format = inheritable_setting.namespace_inheritable[:format]

      stack.use Rack::Head
      stack.use Rack::Lint if lint?
      stack.use Grape::Middleware::Error,
                format:,
                content_types:,
                default_status: inheritable_setting.namespace_inheritable[:default_error_status],
                rescue_all: inheritable_setting.namespace_inheritable[:rescue_all],
                rescue_grape_exceptions: inheritable_setting.namespace_inheritable[:rescue_grape_exceptions],
                default_error_formatter: inheritable_setting.namespace_inheritable[:default_error_formatter],
                error_formatters: inheritable_setting.namespace_stackable_with_hash(:error_formatters),
                rescue_options: inheritable_setting.namespace_stackable_with_hash(:rescue_options),
                rescue_handlers:,
                base_only_rescue_handlers: inheritable_setting.namespace_stackable_with_hash(:base_only_rescue_handlers),
                all_rescue_handler: inheritable_setting.namespace_inheritable[:all_rescue_handler],
                grape_exceptions_rescue_handler: inheritable_setting.namespace_inheritable[:grape_exceptions_rescue_handler]

      stack.concat inheritable_setting.namespace_stackable[:middleware]

      if inheritable_setting.namespace_inheritable[:version].present?
        stack.use Grape::Middleware::Versioner.using(inheritable_setting.namespace_inheritable[:version_options][:using]),
                  versions: inheritable_setting.namespace_inheritable[:version].flatten,
                  version_options: inheritable_setting.namespace_inheritable[:version_options],
                  prefix: inheritable_setting.namespace_inheritable[:root_prefix],
                  mount_path: inheritable_setting.namespace_stackable[:mount_path].first
      end

      stack.use Grape::Middleware::Formatter,
                format:,
                default_format: inheritable_setting.namespace_inheritable[:default_format] || :txt,
                content_types:,
                formatters: inheritable_setting.namespace_stackable_with_hash(:formatters),
                parsers: inheritable_setting.namespace_stackable_with_hash(:parsers)

      builder = stack.build
      builder.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
      builder.to_app
    end

    def build_helpers
      helpers = inheritable_setting.namespace_stackable[:helpers]
      return if helpers.empty?

      Module.new { helpers.each { |mod_to_include| include mod_to_include } }
    end

    def build_response_cookies
      response_cookies do |name, value|
        cookie_value = value.is_a?(Hash) ? value : { value: }
        Rack::Utils.set_cookie_header! header, name, cookie_value
      end
    end

    def lint?
      inheritable_setting.namespace_inheritable[:lint] || Grape.config.lint
    end

    def rescue_handlers
      rescue_handlers = inheritable_setting.namespace_reverse_stackable[:rescue_handlers]
      return if rescue_handlers.blank?

      rescue_handlers.each_with_object({}) do |rescue_handler, result|
        result.merge!(rescue_handler) { |_k, s1, _s2| s1 }
      end
    end
  end
end


def mount_in(router)
      if endpoints
        compile!
        return endpoints.each { |e| e.mount_in(router) }
      end

      reset_routes!
      compile!
      routes.each do |route|
        router.append(route.apply(self))
        next if inheritable_setting.namespace_inheritable[:do_not_route_head] || route.request_method != Rack::GET

        route.dup.then do |head_route|
          head_route.convert_to_head_request!
          router.append(head_route.apply(self))
        end
      end
    end


def run_validators(request:)
      validators = inheritable_setting.route[:saved_validations]
      return if validators.empty?

      validation_errors = []

      Grape::Validations::ParamScopeTracker.track do
        ActiveSupport::Notifications.instrument('endpoint_run_validators.grape', endpoint: self, validators:, request:) do
          validators.each do |validator|
            validator.validate(request)
          rescue Grape::Exceptions::Validation => e
            validation_errors << e
            break if validator.fail_fast?
          rescue Grape::Exceptions::ValidationArrayErrors => e
            validation_errors.concat e.errors
            break if validator.fail_fast?
          end
        end
      end

      raise(Grape::Exceptions::ValidationErrors.new(errors: validation_errors, headers: header)) if validation_errors.any?
    end


def to_routes
      route_options = options[:route_options]
      default_route_options = prepare_default_route_attributes(route_options)
      complete_route_options = route_options.merge(default_route_options)
      path_settings = prepare_default_path_settings

      options[:method].flat_map do |method|
        options[:path].map do |path|
          prepared_path = Path.new(path, default_route_options[:namespace], path_settings)
          pattern = Grape::Router::Pattern.new(
            origin: prepared_path.origin,
            suffix: prepared_path.suffix,
            anchor: default_route_options[:anchor],
            params: route_options[:params],
            format: options[:format],
            version: default_route_options[:version],
            requirements: default_route_options[:requirements]
          )
          Grape::Router::Route.new(self, method, pattern, complete_route_options)
        end
      end
    end


def prepare_default_route_attributes(route_options)
      {
        namespace:,
        version: prepare_version(inheritable_setting.namespace_inheritable[:version]),
        requirements: prepare_routes_requirements(route_options[:requirements]),
        prefix: inheritable_setting.namespace_inheritable[:root_prefix],
        anchor: route_options.fetch(:anchor, true),
        settings: inheritable_setting.route.except(:declared_params, :saved_validations),
        forward_match: options[:forward_match]
      }
    end


def build_stack
      stack = Grape::Middleware::Stack.new

      content_types = inheritable_setting.namespace_stackable_with_hash(:content_types)
      format = inheritable_setting.namespace_inheritable[:format]

      stack.use Rack::Head
      stack.use Rack::Lint if lint?
      stack.use Grape::Middleware::Error,
                format:,
                content_types:,
                default_status: inheritable_setting.namespace_inheritable[:default_error_status],
                rescue_all: inheritable_setting.namespace_inheritable[:rescue_all],
                rescue_grape_exceptions: inheritable_setting.namespace_inheritable[:rescue_grape_exceptions],
                default_error_formatter: inheritable_setting.namespace_inheritable[:default_error_formatter],
                error_formatters: inheritable_setting.namespace_stackable_with_hash(:error_formatters),
                rescue_options: inheritable_setting.namespace_stackable_with_hash(:rescue_options),
                rescue_handlers:,
                base_only_rescue_handlers: inheritable_setting.namespace_stackable_with_hash(:base_only_rescue_handlers),
                all_rescue_handler: inheritable_setting.namespace_inheritable[:all_rescue_handler],
                grape_exceptions_rescue_handler: inheritable_setting.namespace_inheritable[:grape_exceptions_rescue_handler]

      stack.concat inheritable_setting.namespace_stackable[:middleware]

      if inheritable_setting.namespace_inheritable[:version].present?
        stack.use Grape::Middleware::Versioner.using(inheritable_setting.namespace_inheritable[:version_options][:using]),
                  versions: inheritable_setting.namespace_inheritable[:version].flatten,
                  version_options: inheritable_setting.namespace_inheritable[:version_options],
                  prefix: inheritable_setting.namespace_inheritable[:root_prefix],
                  mount_path: inheritable_setting.namespace_stackable[:mount_path].first
      end

      stack.use Grape::Middleware::Formatter,
                format:,
                default_format: inheritable_setting.namespace_inheritable[:default_format] || :txt,
                content_types:,
                formatters: inheritable_setting.namespace_stackable_with_hash(:formatters),
                parsers: inheritable_setting.namespace_stackable_with_hash(:parsers)

      builder = stack.build
      builder.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
      builder.to_app
    end


# frozen_string_literal: true

module Grape
  # The API class is the primary entry point for creating Grape APIs. Users
  # should subclass this class in order to build an API.
  class API
    # Class methods that we want to call on the API rather than on the API object
    NON_OVERRIDABLE = %i[base= base_instance? call change! configuration compile! inherit_settings recognize_path reset! routes top_level_setting= top_level_setting].freeze

    Helpers = Grape::DSL::Helpers::BaseHelper

    class Boolean
      def self.build(val)
        return nil if val != true && val != false

        new
      end
    end

    class << self
      extend Forwardable

      attr_accessor :base_instance, :instances

      delegate_missing_to :base_instance

      # This is the interface point between Rack and Grape; it accepts a request
      # from Rack and ultimately returns an array of three values: the status,
      # the headers, and the body. See [the rack specification]
      # (https://github.com/rack/rack/blob/main/SPEC.rdoc) for more.
      # NOTE: This will only be called on an API directly mounted on RACK
      def_delegators :base_instance, :new, :configuration, :call, :change!, :compile!, :recognize_path, :routes

      # Initialize the instance variables on the remountable class, and the base_instance
      # an instance that will be used to create the set up but will not be mounted
      def initial_setup(base_instance_parent)
        @instances = []
        @setup = []
        @base_parent = base_instance_parent
        @base_instance = mount_instance
      end

      # Redefines all methods so that are forwarded to add_setup and be recorded
      def override_all_methods!
        (base_instance.methods - Class.methods - NON_OVERRIDABLE).each do |method_override|
          define_singleton_method(method_override) do |*args, **kwargs, &block|
            add_setup(method: method_override, args:, kwargs:, block:)
          end
        end
      end

      # Configure an API from the outside. If a block is given, it'll pass a
      # configuration hash to the block which you can use to configure your
      # API. If no block is given, returns the configuration hash.
      # The configuration set here is accessible from inside an API with
      # `configuration` as normal.
      def configure
        config = @base_instance.configuration
        if block_given?
          yield config
          self
        else
          config
        end
      end

      # The remountable class can have a configuration hash to provide some dynamic class-level variables.
      # For instance, a description could be done using: `desc configuration[:description]` if it may vary
      # depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
      # too much, you may actually want to provide a new API rather than remount it.
      def mount_instance(configuration: nil)
        Class.new(@base_parent).tap do |instance|
          instance.configuration = Grape::Util::EndpointConfiguration.new(configuration || {})
          instance.base = self
          replay_setup_on(instance)
        end
      end

      private

      # When inherited, will create a list of all instances (times the API was mounted)
      # It will listen to the setup required to mount that endpoint, and replicate it on any new instance
      def inherited(api)
        super

        api.initial_setup(self == Grape::API ? Grape::API::Instance : @base_instance)
        api.override_all_methods!
      end

      # Replays the set up to produce an API as defined in this class, can be called
      # on classes that inherit from Grape::API
      def replay_setup_on(instance)
        @setup.each do |setup_step|
          replay_step_on(instance, **setup_step)
        end
      end

      # Adds a new stage to the set up require to get a Grape::API up and running
      def add_setup(**step)
        @setup << step
        last_response = nil
        @instances.each do |instance|
          last_response = replay_step_on(instance, **step)
        end

        refresh_mount_step if step[:method] != :mount
        last_response
      end

      # Updating all previously mounted classes in the case that new methods have been executed.
      def refresh_mount_step
        @setup.each do |setup_step|
          next if setup_step[:method] != :mount

          refresh_mount_step = setup_step.merge(method: :refresh_mounted_api)
          @setup << refresh_mount_step
          @instances.each do |instance|
            replay_step_on(instance, **refresh_mount_step)
          end
        end
      end

      def replay_step_on(instance, method:, args:, kwargs:, block:)
        return if skip_immediate_run?(instance, args, kwargs)

        eval_args = evaluate_arguments(instance.configuration, *args)
        eval_kwargs = kwargs.deep_transform_values { |v| evaluate_arguments(instance.configuration, v).first }
        response = instance.__send__(method, *eval_args, **eval_kwargs, &block)

        return response if skip_immediate_run?(instance, [response], kwargs)

        evaluate_arguments(instance.configuration, response).first
      end

      # Skips steps that contain arguments to be lazily executed (on re-mount time)
      def skip_immediate_run?(instance, args, kwargs)
        instance.base_instance? &&
          (any_lazy?(args) || args.any? { |arg| arg.is_a?(Hash) && any_lazy?(arg.values) } || any_lazy?(kwargs.values))
      end

      def any_lazy?(args)
        args.any?(Grape::Util::Lazy::Base)
      end

      def evaluate_arguments(configuration, *args)
        args.map do |argument|
          case argument
          when Grape::Util::Lazy::Base
            argument.evaluate_from(configuration)
          when Hash
            argument.transform_values { |value| evaluate_arguments(configuration, value).first }
          when Array
            evaluate_arguments(configuration, *argument)
          else
            argument
          end
        end
      end
    end
  end
end


def evaluate_arguments(configuration, *args)
        args.map do |argument|
          case argument
          when Grape::Util::Lazy::Base
            argument.evaluate_from(configuration)
          when Hash
            argument.transform_values { |value| evaluate_arguments(configuration, value).first }
          when Array
            evaluate_arguments(configuration, *argument)
          else
            argument
          end
        end
      end


# frozen_string_literal: true

module Grape
  class Router
    # Taken from Rails
    #     normalize_path("/foo")  # => "/foo"
    #     normalize_path("/foo/") # => "/foo"
    #     normalize_path("foo")   # => "/foo"
    #     normalize_path("")      # => "/"
    #     normalize_path("/%ab")  # => "/%AB"
    # https://github.com/rails/rails/blob/00cc4ff0259c0185fe08baadaa40e63ea2534f6e/actionpack/lib/action_dispatch/journey/router/utils.rb#L19
    def self.normalize_path(path)
      return '/' unless path
      return path if path == '/'

      # Fast path for the overwhelming majority of paths that don't need to be normalized
      return path if path.start_with?('/') && !(path.end_with?('/') || path.match?(%r{%|//}))

      # Slow path
      encoding = path.encoding
      path = "/#{path}"
      path.squeeze!('/')

      unless path == '/'
        path.delete_suffix!('/')
        path.gsub!(/(%[a-f0-9]{2})/) { ::Regexp.last_match(1).upcase }
      end

      path.force_encoding(encoding)
    end

    def initialize
      @neutral_map = []
      @neutral_regexes = []
      @map = Hash.new { |hash, key| hash[key] = [] }
      @optimized_map = Hash.new { |hash, key| hash[key] = // }
    end

    def compile!
      return if @compiled

      @union = Regexp.union(@neutral_regexes)
      @neutral_regexes = nil
      (Grape::HTTP_SUPPORTED_METHODS + ['*']).each do |method|
        next unless @map.key?(method)

        routes = @map[method]
        optimized_map = routes.map.with_index { |route, index| route.to_regexp(index) }
        @optimized_map[method] = Regexp.union(optimized_map)
      end
      @compiled = true
    end

    def append(route)
      @map[route.request_method] << route
    end

    def associate_routes(greedy_route)
      @neutral_regexes << greedy_route.to_regexp(@neutral_map.length)
      @neutral_map << greedy_route
    end

    def call(env)
      with_optimization do
        input = Router.normalize_path(env[Rack::PATH_INFO])
        method = env[Rack::REQUEST_METHOD]
        response, route = identity(input, method, env)
        response || rotation(input, method, env, route)
      end
    end

    def recognize_path(input)
      any = with_optimization { greedy_match?(input) }
      return if any == default_response

      any.endpoint
    end

    DEFAULT_RESPONSE_HEADERS = Grape::Util::Header.new.merge('X-Cascade' => 'pass').freeze
    DEFAULT_RESPONSE_BODY = ['404 Not Found'].freeze

    private

    def identity(input, method, env)
      route = nil
      response = transaction(input, method, env) do
        route = match?(input, method)
        process_route(route, input, env) if route
      end
      [response, route]
    end

    def rotation(input, method, env, exact_route)
      response = nil
      @map[method].each do |route|
        next if exact_route == route
        next unless route.match?(input)

        response = process_route(route, input, env)
        break unless cascade?(response)
      end
      response
    end

    def transaction(input, method, env)
      response = yield
      return response if halt?(response)

      last_response_cascade = !response.nil?
      last_neighbor_route = greedy_match?(input)

      # If last_neighbor_route exists and request method is OPTIONS,
      # return response by using #include_allow_header.
      return process_route(last_neighbor_route, input, env, include_allow_header: true) if !last_response_cascade && method == Rack::OPTIONS && last_neighbor_route

      route = match?(input, '*')

      return last_neighbor_route.call(env) if last_neighbor_route && last_response_cascade && route

      if route
        route_response = process_route(route, input, env)
        return route_response if halt?(route_response)

        last_response_cascade = !route_response.nil?
      end

      return process_route(last_neighbor_route, input, env, include_allow_header: true) if !last_response_cascade && last_neighbor_route

      nil
    end

    # Returns true if `response` should be returned as-is from the enclosing
    # transaction. Closes the body as a side effect when the response is
    # cascading so callers can safely try the next match.
    def halt?(response)
      return false unless response

      cascade = cascade?(response)
      response[2].close if cascade && response[2].respond_to?(:close)
      !cascade
    end

    def process_route(route, input, env, include_allow_header: false)
      args = env[Grape::Env::GRAPE_ROUTING_ARGS] || { route_info: route }
      route_params = route.params(input)
      env[Grape::Env::GRAPE_ROUTING_ARGS] = route_params.blank? ? args : args.merge(route_params)
      env[Grape::Env::GRAPE_ALLOWED_METHODS] = route.allow_header if include_allow_header
      route.call(env)
    end

    def with_optimization
      compile!
      yield || default_response
    end

    def default_response
      [404, DEFAULT_RESPONSE_HEADERS.dup, DEFAULT_RESPONSE_BODY.dup]
    end

    def match?(input, method)
      @optimized_map[method].match(input) { |m| @map[method].detect { |route| m[route.regexp_capture_index] } }
    end

    def greedy_match?(input)
      @union.match(input) { |m| @neutral_map.detect { |route| m[route.regexp_capture_index] } }
    end

    def cascade?(response)
      response && response[1]['X-Cascade'] == 'pass'
    end
  end
end


def compile!
      return if @compiled

      @union = Regexp.union(@neutral_regexes)
      @neutral_regexes = nil
      (Grape::HTTP_SUPPORTED_METHODS + ['*']).each do |method|
        next unless @map.key?(method)

        routes = @map[method]
        optimized_map = routes.map.with_index { |route, index| route.to_regexp(index) }
        @optimized_map[method] = Regexp.union(optimized_map)
      end
      @compiled = true
    end


def rotation(input, method, env, exact_route)
      response = nil
      @map[method].each do |route|
        next if exact_route == route
        next unless route.match?(input)

        response = process_route(route, input, env)
        break unless cascade?(response)
      end
      response
    end


def transaction(input, method, env)
      response = yield
      return response if halt?(response)

      last_response_cascade = !response.nil?
      last_neighbor_route = greedy_match?(input)

      # If last_neighbor_route exists and request method is OPTIONS,
      # return response by using #include_allow_header.
      return process_route(last_neighbor_route, input, env, include_allow_header: true) if !last_response_cascade && method == Rack::OPTIONS && last_neighbor_route

      route = match?(input, '*')

      return last_neighbor_route.call(env) if last_neighbor_route && last_response_cascade && route

      if route
        route_response = process_route(route, input, env)
        return route_response if halt?(route_response)

        last_response_cascade = !route_response.nil?
      end

      return process_route(last_neighbor_route, input, env, include_allow_header: true) if !last_response_cascade && last_neighbor_route

      nil
    end


# frozen_string_literal: true

module Grape
  class DeclaredParamsHandler
    def initialize(include_missing: true, evaluate_given: false, stringify: false, contract_key_map: nil)
      @include_missing = include_missing
      @evaluate_given = evaluate_given
      @stringify = stringify
      @contract_key_map = contract_key_map
    end

    def call(passed_params, declared_params, route_params, renamed_params)
      recursive_declared(
        passed_params,
        declared_params:,
        route_params:,
        renamed_params:
      )
    end

    private

    def recursive_declared(passed_params, declared_params:, route_params:, renamed_params:, params_nested_path: [])
      res = if passed_params.is_a?(Array)
              passed_params.map do |passed_param|
                recursive_declared(passed_param, declared_params:, params_nested_path:, renamed_params:, route_params:)
              end
            else
              declared_hash(passed_params, declared_params:, params_nested_path:, renamed_params:, route_params:)
            end

      @contract_key_map&.each { |key_map| key_map.write(passed_params, res) }

      res
    end

    def declared_hash(passed_params, declared_params:, params_nested_path:, renamed_params:, route_params:)
      declared_params.each_with_object(passed_params.class.new) do |declared_param_attr, memo|
        next if @evaluate_given && !declared_param_attr.scope.attr_meets_dependency?(passed_params)

        declared_hash_attr(
          passed_params,
          declared_param: declared_param_attr.key,
          params_nested_path:,
          memo:,
          renamed_params:,
          route_params:
        )
      end
    end

    def declared_hash_attr(passed_params, declared_param:, params_nested_path:, memo:, renamed_params:, route_params:)
      if declared_param.is_a?(Hash)
        declared_param.each_pair do |declared_parent_param, declared_children_params|
          next unless @include_missing || passed_params.key?(declared_parent_param)

          memo_key = build_memo_key(params_nested_path, declared_parent_param, renamed_params)
          passed_children_params = passed_params[declared_parent_param] || passed_params.class.new

          params_nested_path_dup = params_nested_path.dup
          params_nested_path_dup << declared_parent_param.to_s

          memo[memo_key] = handle_passed_param(params_nested_path_dup, route_params:, has_passed_children: passed_children_params.any?) do
            recursive_declared(
              passed_children_params,
              declared_params: declared_children_params,
              params_nested_path: params_nested_path_dup,
              renamed_params:,
              route_params:
            )
          end
        end
      else
        # If it is not a Hash then it does not have children.
        # Find its value or set it to nil.
        return unless @include_missing || (passed_params.respond_to?(:key?) && passed_params.key?(declared_param))

        memo_key = build_memo_key(params_nested_path, declared_param, renamed_params)
        passed_param = passed_params[declared_param]

        params_nested_path_dup = params_nested_path.dup
        params_nested_path_dup << declared_param.to_s

        memo[memo_key] = passed_param || handle_passed_param(params_nested_path_dup, route_params:) do
          passed_param
        end
      end
    end

    def build_memo_key(params_nested_path, declared_param, renamed_params)
      rename_path = params_nested_path + [declared_param.to_s]
      renamed_param_name = renamed_params[rename_path]

      param = renamed_param_name || declared_param
      @stringify ? param.to_s : param.to_sym
    end

    def handle_passed_param(params_nested_path, route_params:, has_passed_children: false, &_block)
      return yield if has_passed_children

      key = params_nested_path[0]
      key += "[#{params_nested_path[1..].join('][')}]" if params_nested_path.size > 1

      type = route_params.dig(key, :type)
      has_children = route_params.keys.any? { |k| k != key && k.start_with?("#{key}[") }

      if type == 'Hash' && !has_children
        {}
      elsif type == 'Array' || (type&.start_with?('[') && !type.include?(','))
        []
      elsif type == 'Set' || type&.start_with?('#<Set', 'Set')
        Set.new
      else
        yield
      end
    end
  end
end


def declared_hash(passed_params, declared_params:, params_nested_path:, renamed_params:, route_params:)
      declared_params.each_with_object(passed_params.class.new) do |declared_param_attr, memo|
        next if @evaluate_given && !declared_param_attr.scope.attr_meets_dependency?(passed_params)

        declared_hash_attr(
          passed_params,
          declared_param: declared_param_attr.key,
          params_nested_path:,
          memo:,
          renamed_params:,
          route_params:
        )
      end
    end


def declared_hash_attr(passed_params, declared_param:, params_nested_path:, memo:, renamed_params:, route_params:)
      if declared_param.is_a?(Hash)
        declared_param.each_pair do |declared_parent_param, declared_children_params|
          next unless @include_missing || passed_params.key?(declared_parent_param)

          memo_key = build_memo_key(params_nested_path, declared_parent_param, renamed_params)
          passed_children_params = passed_params[declared_parent_param] || passed_params.class.new

          params_nested_path_dup = params_nested_path.dup
          params_nested_path_dup << declared_parent_param.to_s

          memo[memo_key] = handle_passed_param(params_nested_path_dup, route_params:, has_passed_children: passed_children_params.any?) do
            recursive_declared(
              passed_children_params,
              declared_params: declared_children_params,
              params_nested_path: params_nested_path_dup,
              renamed_params:,
              route_params:
            )
          end
        end
      else
        # If it is not a Hash then it does not have children.
        # Find its value or set it to nil.
        return unless @include_missing || (passed_params.respond_to?(:key?) && passed_params.key?(declared_param))

        memo_key = build_memo_key(params_nested_path, declared_param, renamed_params)
        passed_param = passed_params[declared_param]

        params_nested_path_dup = params_nested_path.dup
        params_nested_path_dup << declared_param.to_s

        memo[memo_key] = passed_param || handle_passed_param(params_nested_path_dup, route_params:) do
          passed_param
        end
      end
    end


def handle_passed_param(params_nested_path, route_params:, has_passed_children: false, &_block)
      return yield if has_passed_children

      key = params_nested_path[0]
      key += "[#{params_nested_path[1..].join('][')}]" if params_nested_path.size > 1

      type = route_params.dig(key, :type)
      has_children = route_params.keys.any? { |k| k != key && k.start_with?("#{key}[") }

      if type == 'Hash' && !has_children
        {}
      elsif type == 'Array' || (type&.start_with?('[') && !type.include?(','))
        []
      elsif type == 'Set' || type&.start_with?('#<Set', 'Set')
        Set.new
      else
        yield
      end
    end


# frozen_string_literal: true

module Grape
  class API
    # The API Instance class, is the engine behind Grape::API. Each class that inherits
    # from this will represent a different API instance
    class Instance
      extend Grape::DSL::Settings
      extend Grape::DSL::Desc
      extend Grape::DSL::Validations
      extend Grape::DSL::Callbacks
      extend Grape::DSL::Logger
      extend Grape::DSL::Middleware
      extend Grape::DSL::RequestResponse
      extend Grape::DSL::Routing
      extend Grape::DSL::Helpers
      extend Grape::Middleware::Auth::DSL

      Boolean = Grape::API::Boolean

      class << self
        extend Forwardable

        attr_accessor :configuration

        def_delegators :@base, :to_s

        def base=(grape_api)
          @base = grape_api
          grape_api.instances << self
        end

        def base_instance?
          self == @base.base_instance
        end

        # A class-level lock to ensure the API is not compiled by multiple
        # threads simultaneously within the same process.
        LOCK = Mutex.new

        # Clears all defined routes, endpoints, etc., on this API.
        def reset!
          reset_endpoints!
          reset_routes!
          reset_validations!
        end

        # This is the interface point between Rack and Grape; it accepts a request
        # from Rack and ultimately returns an array of three values: the status,
        # the headers, and the body. See [the rack specification]
        # (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
        def call(env)
          compile!
          @instance.call(env)
        end

        def compile!
          return if @instance

          LOCK.synchronize { @instance ||= new }
        end

        # see Grape::Router#recognize_path
        def recognize_path(path)
          compile!
          @instance.router.recognize_path(path)
        end

        # Wipe the compiled API so we can recompile after changes were made.
        def change!
          @instance = nil
        end

        protected

        def inherit_settings(other_settings)
          top_level_setting.inherit_from other_settings.point_in_time_copy

          # Propagate any inherited params down to our endpoints, and reset any
          # compiled routes.
          endpoints.each do |e|
            e.inherit_settings(top_level_setting.namespace_stackable)
            e.reset_routes!
          end

          reset_routes!
        end

        private

        def inherited(subclass)
          super
          subclass.reset!
          subclass.logger logger.clone
        end
      end

      attr_reader :router

      # Builds the routes from the defined endpoints, effectively compiling
      # this API into a usable form.
      def initialize
        @router = Router.new
        add_head_not_allowed_methods_and_options_methods
        self.class.endpoints.each do |endpoint|
          endpoint.mount_in(@router)
        end

        @router.compile!
        @router.freeze
      end

      # Handle a request. See Rack documentation for what `env` is.
      def call(env)
        status, headers, response = @router.call(env)
        unless cascade?
          headers = Grape::Util::Header.new.merge(headers)
          headers.delete('X-Cascade')
        end

        [status, headers, response]
      end

      # Some requests may return a HTTP 404 error if grape cannot find a matching
      # route. In this case, Grape::Router adds a X-Cascade header to the response
      # and sets it to 'pass', indicating to grape's parents they should keep
      # looking for a matching route on other resources.
      #
      # In some applications (e.g. mounting grape on rails), one might need to trap
      # errors from reaching upstream. This is effectivelly done by unsetting
      # X-Cascade. Default :cascade is true.
      def cascade?
        namespace_inheritable = self.class.inheritable_setting.namespace_inheritable
        return namespace_inheritable[:cascade] if namespace_inheritable.key?(:cascade)
        return namespace_inheritable[:version_options][:cascade] if namespace_inheritable[:version_options]&.key?(:cascade)

        true
      end

      reset!

      private

      # For every resource add a 'OPTIONS' route that returns an HTTP 204 response
      # with a list of HTTP methods that can be called. Also add a route that
      # will return an HTTP 405 response for any HTTP method that the resource
      # cannot handle.
      def add_head_not_allowed_methods_and_options_methods
        # The paths we collected are prepared (cf. Path#prepare), so they
        # contain already versioning information when using path versioning.
        all_routes = self.class.endpoints.flat_map(&:routes)

        # Disable versioning so adding a route won't prepend versioning
        # informations again.
        without_root_prefix_and_versioning { collect_route_config_per_pattern(all_routes) }
      end

      def collect_route_config_per_pattern(all_routes)
        routes_by_regexp = all_routes.group_by(&:pattern_regexp)
        namespace_inheritable = self.class.inheritable_setting.namespace_inheritable

        # Build the configuration based on the first endpoint and the collection of methods supported.
        routes_by_regexp.each_value do |routes|
          next if routes.any? { |route| route.request_method == '*' }

          last_route = routes.last # Most of the configuration is taken from the last endpoint
          allowed_methods = routes.map(&:request_method)
          allowed_methods |= [Rack::HEAD] if !namespace_inheritable[:do_not_route_head] && allowed_methods.include?(Rack::GET)

          allow_header = namespace_inheritable[:do_not_route_options] ? allowed_methods : [Rack::OPTIONS] | allowed_methods
          last_route.app.options[:options_route_enabled] = true unless namespace_inheritable[:do_not_route_options] || allowed_methods.include?(Rack::OPTIONS)

          greedy_route = Grape::Router::GreedyRoute.new(last_route.pattern, endpoint: last_route.app, allow_header:)
          @router.associate_routes(greedy_route)
        end
      end

      ROOT_PREFIX_VERSIONING_KEY = %i[version version_options root_prefix].freeze
      private_constant :ROOT_PREFIX_VERSIONING_KEY

      # Allows definition of endpoints that ignore the versioning configuration
      # used by the rest of your API.
      def without_root_prefix_and_versioning
        inheritable_setting = self.class.inheritable_setting
        deleted_values = inheritable_setting.namespace_inheritable.delete(*ROOT_PREFIX_VERSIONING_KEY)
        yield
      ensure
        ROOT_PREFIX_VERSIONING_KEY.each_with_index do |key, index|
          inheritable_setting.namespace_inheritable[key] = deleted_values[index]
        end
      end
    end
  end
end


# frozen_string_literal: true

module Grape
  module ErrorFormatter
    class Json < Base
      class << self
        def format_structured_message(structured_message)
          ::Grape::Json.dump(structured_message)
        end

        private

        def wrap_message(message)
          case message
          when Hash
            message
          when Exceptions::ValidationErrors
            message.as_json
          else
            { error: ensure_utf8(message) }
          end
        end

        def ensure_utf8(message)
          return message unless message.respond_to? :encode

          message.encode('UTF-8', invalid: :replace, undef: :replace)
        end
      end
    end
  end
end


# frozen_string_literal: true

module Grape
  module ErrorFormatter
    class Base
      class << self
        def call(message, backtrace, options = {}, env = nil, original_exception = nil)
          merge_backtrace = backtrace.present? && options.dig(:rescue_options, :backtrace)
          merge_original_exception = original_exception && options.dig(:rescue_options, :original_exception)

          wrapped_message = wrap_message(present(message, env))
          if wrapped_message.is_a?(Hash)
            wrapped_message[:backtrace] = backtrace if merge_backtrace
            wrapped_message[:original_exception] = original_exception.inspect if merge_original_exception
          end

          format_structured_message(wrapped_message)
        end

        def present(message, env)
          # error! accepts a message hash with an optional :with key specifying the entity presenter.
          # Extract it here so the presenter can be resolved and the key is not serialized in the response.
          # See spec/integration/grape_entity/entity_spec.rb for examples.
          with = nil
          if message.is_a?(Hash) && message.key?(:with)
            message = message.dup
            with = message.delete(:with)
          end

          presenter = with || env[Grape::Env::API_ENDPOINT].entity_class_for_obj(message)

          unless presenter || env[Grape::Env::GRAPE_ROUTING_ARGS].nil?
            # env['api.endpoint'].route does not work when the error occurs within a middleware
            # the Endpoint does not have a valid env at this moment
            http_codes = env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info].http_codes || []
            found_code = http_codes.find do |http_code|
              (http_code[0].to_i == env[Grape::Env::API_ENDPOINT].status) && http_code[2].respond_to?(:represent)
            end if env[Grape::Env::API_ENDPOINT].request

            presenter = found_code[2] if found_code
          end

          return message unless presenter

          embeds = { env: }
          embeds[:version] = env[Grape::Env::API_VERSION] if env.key?(Grape::Env::API_VERSION)
          presenter.represent(message, embeds).serializable_hash
        end

        def wrap_message(message)
          return message if message.is_a?(Hash)

          { message: }
        end

        def format_structured_message(_structured_message)
          raise NotImplementedError
        end

        private

        def inherited(klass)
          super
          ErrorFormatter.register(klass)
        end
      end
    end
  end
end


def present(message, env)
          # error! accepts a message hash with an optional :with key specifying the entity presenter.
          # Extract it here so the presenter can be resolved and the key is not serialized in the response.
          # See spec/integration/grape_entity/entity_spec.rb for examples.
          with = nil
          if message.is_a?(Hash) && message.key?(:with)
            message = message.dup
            with = message.delete(:with)
          end

          presenter = with || env[Grape::Env::API_ENDPOINT].entity_class_for_obj(message)

          unless presenter || env[Grape::Env::GRAPE_ROUTING_ARGS].nil?
            # env['api.endpoint'].route does not work when the error occurs within a middleware
            # the Endpoint does not have a valid env at this moment
            http_codes = env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info].http_codes || []
            found_code = http_codes.find do |http_code|
              (http_code[0].to_i == env[Grape::Env::API_ENDPOINT].status) && http_code[2].respond_to?(:represent)
            end if env[Grape::Env::API_ENDPOINT].request

            presenter = found_code[2] if found_code
          end

          return message unless presenter

          embeds = { env: }
          embeds[:version] = env[Grape::Env::API_VERSION] if env.key?(Grape::Env::API_VERSION)
          presenter.represent(message, embeds).serializable_hash
        end


# frozen_string_literal: true

module Grape
  module ErrorFormatter
    class Txt < Base
      def self.format_structured_message(structured_message)
        message = structured_message[:message] || Grape::Json.dump(structured_message)
        Array.wrap(message).tap do |final_message|
          if structured_message.key?(:backtrace)
            final_message << 'backtrace:'
            final_message.concat(structured_message[:backtrace])
          end
          if structured_message.key?(:original_exception)
            final_message << 'original exception:'
            final_message << structured_message[:original_exception]
          end
        end.join("\r\n ")
      end
    end
  end
end


# frozen_string_literal: true

module Grape
  module Validations
    # Documents parameters of an endpoint. If documentation isn't needed (for instance, it is an
    # internal API), the class only cleans up attributes to avoid junk in RAM.

    module ParamsDocumentation
      def document_params(attrs, validations, type = nil, values = nil, except_values = nil)
        return validations.except!(:desc, :description, :documentation) if @api.inheritable_setting.namespace_inheritable[:do_not_document]

        documented_attrs = attrs.to_h do |name|
          [full_name(name), extract_details(validations, type, values, except_values)]
        end
        @api.inheritable_setting.namespace_stackable[:params] = documented_attrs
      end

      private

      def extract_details(validations, type, values, except_values)
        {}.tap do |details|
          details[:required] = validations.key?(:presence)
          details[:type] = TypeCache[type] if type
          details[:values] = values if values
          details[:except_values] = except_values if except_values
          details[:default] = validations[:default] if validations.key?(:default)
          if validations.key?(:length)
            details[:min_length] = validations[:length][:min] if validations[:length].key?(:min)
            details[:max_length] = validations[:length][:max] if validations[:length].key?(:max)
          end

          desc = validations.delete(:desc) || validations.delete(:description)
          details[:desc] = desc if desc

          documentation = validations.delete(:documentation)
          details[:documentation] = documentation if documentation
        end
      end

      class TypeCache < Grape::Util::Cache
        def initialize
          super
          @cache = Hash.new do |h, type|
            h[type] = type.to_s
          end
        end
      end
    end
  end
end


# frozen_string_literal: true

module Grape
  module Validations
    class ContractScope
      # Declare the contract to be used for the endpoint's parameters.
      # @param api [API] the API endpoint to modify.
      # @param contract the contract or schema to be used for validation. Optional.
      # @yield a block yielding a new schema class. Optional.
      def initialize(api, contract = nil, &block)
        # When block is passed, the first arg is either schema or nil.
        contract = Dry::Schema.Params(parent: contract, &block) if block

        if contract.respond_to?(:schema)
          # It's a Dry::Validation::Contract, then.
          contract = contract.new
          key_map = contract.schema.key_map
        else
          # Dry::Schema::Processor, hopefully.
          key_map = contract.key_map
        end

        api.inheritable_setting.namespace_stackable[:contract_key_map] = key_map
        api.inheritable_setting.namespace_stackable[:validations] = Validators::ContractScopeValidator.new(schema: contract)
      end
    end
  end
end


# frozen_string_literal: true

module Grape
  module Validations
    class ParamsScope
      attr_reader :parent, :type, :nearest_array_ancestor, :full_path

      def qualifying_params
        ParamScopeTracker.current&.qualifying_params(self)
      end

      include Grape::DSL::Parameters
      include Grape::Validations::ParamsDocumentation

      # There are a number of documentation options on entities that don't have
      # corresponding validators. Since there is nowhere that enumerates them all,
      # we maintain a list of them here and skip looking up validators for them.
      RESERVED_DOCUMENTATION_KEYWORDS = %i[as required param_type is_array format example].freeze

      SPECIAL_JSON = [JSON, Array[JSON]].freeze

      class Attr
        attr_reader :key, :scope

        # Open up a new ParamsScope::Attr
        # @param key [Hash, Symbol] key of attr
        # @param scope [Grape::Validations::ParamsScope] scope of attr
        def initialize(key, scope)
          @key = key
          @scope = scope
        end

        # @return Array[Symbol, Hash[Symbol => Array]] declared_params with symbol instead of Attr
        def self.attrs_keys(declared_params)
          declared_params.map do |declared_param_attr|
            attr_key(declared_param_attr)
          end
        end

        def self.attr_key(declared_param_attr)
          case declared_param_attr
          when self
            attr_key(declared_param_attr.key)
          when Hash
            declared_param_attr.transform_values { |value| attrs_keys(value) }
          else
            declared_param_attr
          end
        end
      end

      # Open up a new ParamsScope, allowing parameter definitions per
      #   Grape::DSL::Params.
      # @param api [API] the API endpoint to modify
      # @param element [Symbol] the element that contains this scope; for
      #   this to be relevant, parent must be set
      # @param element_renamed [Symbol, nil] whenever this scope should
      #   be renamed and to what, given +nil+ no renaming is done
      # @param parent [ParamsScope] the scope containing this scope
      # @param optional [Boolean] whether or not this scope needs to have
      #   any parameters set or not
      # @param type [Class] a type meant to govern this scope (deprecated)
      # @param type [Hash] group options for this scope
      # @param dependent_on [Symbol] if present, this scope should only
      #   validate if this param is present in the parent scope
      # @yield the instance context, open for parameter definitions
      def initialize(api:, element: nil, element_renamed: nil, parent: nil, optional: false, type: nil, group: nil, dependent_on: nil, &block)
        @element          = element
        @element_renamed  = element_renamed
        @parent           = parent
        @api              = api
        @optional         = optional
        @type             = type
        @group            = group
        @dependent_on     = dependent_on
        # Must be an ivar: push_declared_params is dispatched on self during
        # instance_eval, so local variables from initialize are unreachable.
        # configure_declared_params consumes it and clears @declared_params to nil.
        @declared_params = []
        @full_path = build_full_path

        instance_eval(&block) if block

        configure_declared_params
        @nearest_array_ancestor = find_nearest_array_ancestor
        freeze
      end

      def configuration
        config = @api.configuration
        config.is_a?(Grape::Util::Lazy::Base) ? config.evaluate : config
      end

      # @return [Boolean] whether or not this entire scope needs to be
      #   validated
      def should_validate?(parameters)
        scoped_params = params(parameters)

        return false if @optional && (scoped_params.blank? || all_element_blank?(scoped_params))
        return false unless meets_dependency?(scoped_params, parameters)
        return true if @parent.nil?

        @parent.should_validate?(parameters)
      end

      def meets_dependency?(params, request_params)
        return true unless @dependent_on
        return false if @parent.present? && !@parent.meets_dependency?(@parent.params(request_params), request_params)

        if params.is_a?(Array)
          filtered = params.flatten.filter { |param| meets_dependency?(param, request_params) }
          ParamScopeTracker.current&.store_qualifying_params(self, filtered)
          return filtered.present?
        end

        meets_hash_dependency?(params)
      end

      def attr_meets_dependency?(params)
        return true unless @dependent_on
        return false if @parent.present? && !@parent.attr_meets_dependency?(params)

        meets_hash_dependency?(params)
      end

      def meets_hash_dependency?(params)
        # params might be anything what looks like a hash, so it must implement a `key?` method
        return false unless params.respond_to?(:key?)

        @dependent_on.all? do |dependency|
          if dependency.is_a?(Hash)
            key, callable = dependency.first
            callable.call(params[key])
          else
            params[dependency].present?
          end
        end
      end

      # @return [String] the proper attribute name, with nesting considered.
      def full_name(name, index: nil)
        tracker = ParamScopeTracker.current
        if nested?
          # Find our containing element's name, and append ours.
          resolved_index = index || tracker&.index_for(self)
          "#{@parent.full_name(@element)}#{brackets(resolved_index)}#{brackets(name)}"
        elsif lateral?
          # Find the name of the element as if it was at the same nesting level
          # as our parent. We need to forward our index upward to achieve this.
          @parent.full_name(name, index: tracker&.index_for(self))
        else
          # We must be the root scope, so no prefix needed.
          name.to_s
        end
      end

      def brackets(val)
        "[#{val}]" if val
      end

      # @return [Boolean] whether or not this scope is the root-level scope
      def root?
        !@parent
      end

      # A nested scope is contained in one of its parent's elements.
      # @return [Boolean] whether or not this scope is nested
      def nested?
        @parent && @element
      end

      # A lateral scope is subordinate to its parent, but its keys are at the
      # same level as its parent and thus is not contained within an element.
      # @return [Boolean] whether or not this scope is lateral
      def lateral?
        @parent && !@element
      end

      # @return [Boolean] whether or not this scope needs to be present, or can
      #   be blank
      def required?
        !@optional
      end

      protected

      # Adds a parameter declaration to our list of validations.
      # @param attrs [Array] (see Grape::DSL::Parameters#requires)
      def push_declared_params(attrs, **opts)
        opts[:declared_params_scope] = self unless opts.key?(:declared_params_scope)
        return @parent.push_declared_params(attrs, **opts) if lateral?

        push_renamed_param(full_path + [attrs.first], opts[:as]) if opts[:as]
        @declared_params.concat(attrs.map { |attr| ::Grape::Validations::ParamsScope::Attr.new(attr, opts[:declared_params_scope]) })
      end

      private

      def build_full_path
        if nested?
          @parent.full_path + [@element]
        elsif lateral?
          @parent.full_path
        else
          []
        end
      end

      # Add a new parameter which should be renamed when using the +#declared+
      # method.
      #
      # @param path [Array<String, Symbol>] the full path of the parameter
      #   (including the parameter name as last array element)
      # @param new_name [String, Symbol] the new name of the parameter (the
      #   renamed name, with the +as: ...+ semantic)
      def push_renamed_param(path, new_name)
        api_route_setting = @api.inheritable_setting.route
        base = api_route_setting[:renamed_params] || {}
        base[Array(path).map(&:to_s)] = new_name.to_s
        api_route_setting[:renamed_params] = base
      end

      def require_required_and_optional_fields(context, using:, except: nil)
        except_fields = Array.wrap(except)
        using_fields = using.keys.delete_if { |f| except_fields.include?(f) }

        if context == :all
          optional_fields = except_fields
          required_fields = using_fields
        else # context == :none
          required_fields = except_fields
          optional_fields = using_fields
        end
        required_fields.each do |field|
          field_opts = using[field]
          raise ArgumentError, "required field not exist: #{field}" unless field_opts

          requires(field, **field_opts)
        end
        optional_fields.each do |field|
          field_opts = using[field]
          optional(field, **field_opts) if field_opts
        end
      end

      def require_optional_fields(context, using:, except: nil)
        optional_fields = using.keys
        unless context == :all
          except_fields = Array.wrap(except)
          optional_fields.delete_if { |f| except_fields.include?(f) }
        end
        optional_fields.each do |field|
          field_opts = using[field]
          optional(field, **field_opts) if field_opts
        end
      end

      def validate_attributes(attrs, **opts, &block)
        opts[:type] ||= Array if block
        validates(attrs, opts)
      end

      # Returns a new parameter scope, subordinate to the current one and nested
      # under the given element.
      # @param element [Symbol] the parameter name under which this scope is nested
      # @param type [Class] the type governing this scope
      # @param as [Symbol, nil] optional renamed name for the element
      # @param optional [Boolean] whether the parameter this scope is nested under
      #   is optional or not (and hence, whether this block's params will be).
      # @yield parameter scope
      def new_scope(element, type:, as:, optional: false, &)
        # if required params are grouped and no type or unsupported type is provided, raise an error
        if element && !optional
          raise Grape::Exceptions::MissingGroupType if type.nil?
          raise Grape::Exceptions::UnsupportedGroupType unless Grape::Validations::Types.group?(type)
        end

        self.class.new(
          api: @api,
          element:,
          element_renamed: as,
          parent: self,
          optional:,
          type: type || Array,
          group: @group,
          &
        )
      end

      # Returns a new parameter scope, not nested under any current-level param
      # but instead at the same level as the current scope.
      # @param dependent_on [Symbol] if given, specifies that this scope should
      #   only validate if this parameter from the above scope is present
      # @yield parameter scope
      def new_lateral_scope(dependent_on:, &)
        self.class.new(
          api: @api,
          parent: self,
          optional: @optional,
          type: type == Array ? Array : Hash,
          dependent_on:,
          &
        )
      end

      # Returns a new parameter scope, subordinate to the current one, sharing
      # the given group options with all parameters defined within.
      # @param group [Hash] common options to merge into each parameter in the scope
      # @yield parameter scope
      def new_group_scope(group, &)
        self.class.new(api: @api, parent: self, group:, &)
      end

      # Pushes declared params to parent or settings, then clears @declared_params.
      # Clearing here (rather than in initialize) keeps the lifecycle ownership in
      # one place: this method both consumes and invalidates the ivar so that
      # push_declared_params cannot be called on the frozen scope later.
      def configure_declared_params
        push_renamed_param(full_path, @element_renamed) if @element_renamed

        if nested?
          @parent.push_declared_params [{ @element => @declared_params }]
        else
          @api.inheritable_setting.namespace_stackable[:declared_params] = @declared_params
        end
      ensure
        @declared_params = nil
      end

      def find_nearest_array_ancestor
        scope = @parent
        scope = scope.parent while scope && scope.type != Array
        scope
      end

      def validates(attrs, validations)
        coerce_type = infer_coercion(validations)
        required = validations.key?(:presence)
        default = validations[:default]
        values = validations[:values].is_a?(Hash) ? validations.dig(:values, :value) : validations[:values]
        except_values = validations[:except_values].is_a?(Hash) ? validations.dig(:except_values, :value) : validations[:except_values]

        # NB. values and excepts should be nil, Proc, Array, or Range.
        # Specifically, values should NOT be a Hash
        # use values or excepts to guess coerce type when stated type is Array
        coerce_type = guess_coerce_type(coerce_type, values, except_values)

        # default value should be present in values array, if both exist and are not procs
        check_incompatible_option_values(default, values, except_values)

        # type should be compatible with values array, if both exist
        validate_value_coercion(coerce_type, values, except_values)

        document_params attrs, validations, coerce_type, values, except_values

        opts = derive_validator_options(validations).freeze

        # Validate for presence before any other validators
        validates_presence(validations, attrs, opts)

        # Before we run the rest of the validators, let's handle
        # whatever coercion so that we are working with correctly
        # type casted values
        coerce_type validations.extract!(:coerce, :coerce_with, :coerce_message), attrs, required, opts

        validations.each do |type, options|
          # Don't try to look up validators for documentation params that don't have one.
          next if RESERVED_DOCUMENTATION_KEYWORDS.include?(type)

          validate(type, options, attrs, required, opts)
        end
      end

      # Validate and comprehend the +:type+, +:types+, and +:coerce_with+
      # options that have been supplied to the parameter declaration.
      # The +:type+ and +:types+ options will be removed from the
      # validations list, replaced appropriately with +:coerce+ and
      # +:coerce_with+ options that will later be passed to
      # {Validators::CoerceValidator}. The type that is returned may be
      # used for documentation and further validation of parameter
      # options.
      #
      # @param validations [Hash] list of validations supplied to the
      #   parameter declaration
      # @return [class-like] type to which the parameter will be coerced
      # @raise [ArgumentError] if the given type options are invalid
      def infer_coercion(validations)
        raise ArgumentError, ':type may not be supplied with :types' if validations.key?(:type) && validations.key?(:types)

        validations[:coerce] = (options_key?(:type, :value, validations) ? validations[:type][:value] : validations[:type]) if validations.key?(:type)
        validations[:coerce_message] = (options_key?(:type, :message, validations) ? validations[:type][:message] : nil) if validations.key?(:type)
        validations[:coerce] = (options_key?(:types, :value, validations) ? validations[:types][:value] : validations[:types]) if validations.key?(:types)
        validations[:coerce_message] = (options_key?(:types, :message, validations) ? validations[:types][:message] : nil) if validations.key?(:types)

        validations.delete(:types) if validations.key?(:types)

        coerce_type = validations[:coerce]

        # Special case - when the argument is a single type that is a
        # variant-type collection.
        if Types.multiple?(coerce_type) && validations.key?(:type)
          validations[:coerce] = Types::VariantCollectionCoercer.new(
            coerce_type,
            validations.delete(:coerce_with)
          )
        end
        validations.delete(:type)

        coerce_type
      end

      # Enforce correct usage of :coerce_with parameter.
      # We do not allow coercion without a type, nor with
      # +JSON+ as a type since this defines its own coercion
      # method.
      def check_coerce_with(validations)
        return unless validations.key?(:coerce_with)
        # type must be supplied for coerce_with..
        raise ArgumentError, 'must supply type for coerce_with' unless validations.key?(:coerce)

        # but not special JSON types, which
        # already imply coercion method
        return unless SPECIAL_JSON.include?(validations[:coerce])

        raise ArgumentError, 'coerce_with disallowed for type: JSON'
      end

      # Add type coercion validation to this scope,
      # if any has been specified.
      # This validation has special handling since it is
      # composited from more than one +requires+/+optional+
      # parameter, and needs to be run before most other
      # validations.
      def coerce_type(validations, attrs, required, opts)
        check_coerce_with(validations)

        # Falsy check (not key?) is intentional: when a remountable API is first
        # evaluated on its base instance (no configuration supplied yet),
        # configuration[:some_type] evaluates to nil. Skipping instantiation
        # here is correct — the real mounted instance will replay this step with
        # the actual type value.
        return unless validations[:coerce]

        coerce_options = {
          type: validations[:coerce],
          method: validations[:coerce_with],
          message: validations[:coerce_message]
        }
        validate('coerce', coerce_options, attrs, required, opts)
      end

      def guess_coerce_type(coerce_type, *values_list)
        return coerce_type unless coerce_type == Array

        values_list.each do |values|
          next if !values || values.is_a?(Proc)
          return values.first.class if values.is_a?(Range) || !values.empty?
        end
        coerce_type
      end

      def check_incompatible_option_values(default, values, except_values)
        return unless default && !default.is_a?(Proc)

        raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values) if values && !values.is_a?(Proc) && !Array(default).all? { |def_val| values.include?(def_val) }

        return unless except_values && !except_values.is_a?(Proc) && Array(default).any? { |def_val| except_values.include?(def_val) }

        raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, except_values)
      end

      def validate(type, options, attrs, required, opts)
        validator_class = Validations.require_validator(type)
        validator_instance = validator_class.new(
          attrs,
          options,
          required,
          self,
          opts
        )
        @api.inheritable_setting.namespace_stackable[:validations] = validator_instance
      end

      def validate_value_coercion(coerce_type, *values_list)
        return unless coerce_type

        coerce_type = coerce_type.first if coerce_type.is_a?(Enumerable)
        values_list.each do |values|
          next if !values || values.is_a?(Proc)

          value_types = values.is_a?(Range) ? [values.begin, values.end].compact : values
          value_types = value_types.map { |type| Grape::API::Boolean.build(type) } if coerce_type == Grape::API::Boolean
          raise Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values) unless value_types.all?(coerce_type)
        end
      end

      def options_key?(type, key, validations)
        validations[type].respond_to?(:key?) && validations[type].key?(key) && !validations[type][key].nil?
      end

      def all_element_blank?(scoped_params)
        scoped_params.respond_to?(:all?) && scoped_params.all?(&:blank?)
      end

      # Validators don't have access to each other and they don't need, however,
      # some validators might influence others, so their options should be shared
      def derive_validator_options(validations)
        allow_blank = validations[:allow_blank]

        {
          allow_blank: allow_blank.is_a?(Hash) ? allow_blank[:value] : allow_blank,
          fail_fast: validations.delete(:fail_fast) || false
        }
      end

      def validates_presence(validations, attrs, opts)
        return unless validations.key?(:presence) && validations[:presence]

        validate('presence', validations.delete(:presence), attrs, true, opts)
        validations.delete(:message) if validations.key?(:message)
      end
    end
  end
end


def initialize(api:, element: nil, element_renamed: nil, parent: nil, optional: false, type: nil, group: nil, dependent_on: nil, &block)
        @element          = element
        @element_renamed  = element_renamed
        @parent           = parent
        @api              = api
        @optional         = optional
        @type             = type
        @group            = group
        @dependent_on     = dependent_on
        # Must be an ivar: push_declared_params is dispatched on self during
        # instance_eval, so local variables from initialize are unreachable.
        # configure_declared_params consumes it and clears @declared_params to nil.
        @declared_params = []
        @full_path = build_full_path

        instance_eval(&block) if block

        configure_declared_params
        @nearest_array_ancestor = find_nearest_array_ancestor
        freeze
      end


def meets_dependency?(params, request_params)
        return true unless @dependent_on
        return false if @parent.present? && !@parent.meets_dependency?(@parent.params(request_params), request_params)

        if params.is_a?(Array)
          filtered = params.flatten.filter { |param| meets_dependency?(param, request_params) }
          ParamScopeTracker.current&.store_qualifying_params(self, filtered)
          return filtered.present?
        end

        meets_hash_dependency?(params)
      end


def meets_hash_dependency?(params)
        # params might be anything what looks like a hash, so it must implement a `key?` method
        return false unless params.respond_to?(:key?)

        @dependent_on.all? do |dependency|
          if dependency.is_a?(Hash)
            key, callable = dependency.first
            callable.call(params[key])
          else
            params[dependency].present?
          end
        end
      end


def full_name(name, index: nil)
        tracker = ParamScopeTracker.current
        if nested?
          # Find our containing element's name, and append ours.
          resolved_index = index || tracker&.index_for(self)
          "#{@parent.full_name(@element)}#{brackets(resolved_index)}#{brackets(name)}"
        elsif lateral?
          # Find the name of the element as if it was at the same nesting level
          # as our parent. We need to forward our index upward to achieve this.
          @parent.full_name(name, index: tracker&.index_for(self))
        else
          # We must be the root scope, so no prefix needed.
          name.to_s
        end
      end


def require_required_and_optional_fields(context, using:, except: nil)
        except_fields = Array.wrap(except)
        using_fields = using.keys.delete_if { |f| except_fields.include?(f) }

        if context == :all
          optional_fields = except_fields
          required_fields = using_fields
        else # context == :none
          required_fields = except_fields
          optional_fields = using_fields
        end
        required_fields.each do |field|
          field_opts = using[field]
          raise ArgumentError, "required field not exist: #{field}" unless field_opts

          requires(field, **field_opts)
        end
        optional_fields.each do |field|
          field_opts = using[field]
          optional(field, **field_opts) if field_opts
        end
      end


def require_optional_fields(context, using:, except: nil)
        optional_fields = using.keys
        unless context == :all
          except_fields = Array.wrap(except)
          optional_fields.delete_if { |f| except_fields.include?(f) }
        end
        optional_fields.each do |field|
          field_opts = using[field]
          optional(field, **field_opts) if field_opts
        end
      end


def new_scope(element, type:, as:, optional: false, &)
        # if required params are grouped and no type or unsupported type is provided, raise an error
        if element && !optional
          raise Grape::Exceptions::MissingGroupType if type.nil?
          raise Grape::Exceptions::UnsupportedGroupType unless Grape::Validations::Types.group?(type)
        end

        self.class.new(
          api: @api,
          element:,
          element_renamed: as,
          parent: self,
          optional:,
          type: type || Array,
          group: @group,
          &
        )
      end


def new_lateral_scope(dependent_on:, &)
        self.class.new(
          api: @api,
          parent: self,
          optional: @optional,
          type: type == Array ? Array : Hash,
          dependent_on:,
          &
        )
      end


def configure_declared_params
        push_renamed_param(full_path, @element_renamed) if @element_renamed

        if nested?
          @parent.push_declared_params [{ @element => @declared_params }]
        else
          @api.inheritable_setting.namespace_stackable[:declared_params] = @declared_params
        end
      ensure
        @declared_params = nil
      end


def validates(attrs, validations)
        coerce_type = infer_coercion(validations)
        required = validations.key?(:presence)
        default = validations[:default]
        values = validations[:values].is_a?(Hash) ? validations.dig(:values, :value) : validations[:values]
        except_values = validations[:except_values].is_a?(Hash) ? validations.dig(:except_values, :value) : validations[:except_values]

        # NB. values and excepts should be nil, Proc, Array, or Range.
        # Specifically, values should NOT be a Hash
        # use values or excepts to guess coerce type when stated type is Array
        coerce_type = guess_coerce_type(coerce_type, values, except_values)

        # default value should be present in values array, if both exist and are not procs
        check_incompatible_option_values(default, values, except_values)

        # type should be compatible with values array, if both exist
        validate_value_coercion(coerce_type, values, except_values)

        document_params attrs, validations, coerce_type, values, except_values

        opts = derive_validator_options(validations).freeze

        # Validate for presence before any other validators
        validates_presence(validations, attrs, opts)

        # Before we run the rest of the validators, let's handle
        # whatever coercion so that we are working with correctly
        # type casted values
        coerce_type validations.extract!(:coerce, :coerce_with, :coerce_message), attrs, required, opts

        validations.each do |type, options|
          # Don't try to look up validators for documentation params that don't have one.
          next if RESERVED_DOCUMENTATION_KEYWORDS.include?(type)

          validate(type, options, attrs, required, opts)
        end
      end


def infer_coercion(validations)
        raise ArgumentError, ':type may not be supplied with :types' if validations.key?(:type) && validations.key?(:types)

        validations[:coerce] = (options_key?(:type, :value, validations) ? validations[:type][:value] : validations[:type]) if validations.key?(:type)
        validations[:coerce_message] = (options_key?(:type, :message, validations) ? validations[:type][:message] : nil) if validations.key?(:type)
        validations[:coerce] = (options_key?(:types, :value, validations) ? validations[:types][:value] : validations[:types]) if validations.key?(:types)
        validations[:coerce_message] = (options_key?(:types, :message, validations) ? validations[:types][:message] : nil) if validations.key?(:types)

        validations.delete(:types) if validations.key?(:types)

        coerce_type = validations[:coerce]

        # Special case - when the argument is a single type that is a
        # variant-type collection.
        if Types.multiple?(coerce_type) && validations.key?(:type)
          validations[:coerce] = Types::VariantCollectionCoercer.new(
            coerce_type,
            validations.delete(:coerce_with)
          )
        end
        validations.delete(:type)

        coerce_type
      end


def coerce_type(validations, attrs, required, opts)
        check_coerce_with(validations)

        # Falsy check (not key?) is intentional: when a remountable API is first
        # evaluated on its base instance (no configuration supplied yet),
        # configuration[:some_type] evaluates to nil. Skipping instantiation
        # here is correct — the real mounted instance will replay this step with
        # the actual type value.
        return unless validations[:coerce]

        coerce_options = {
          type: validations[:coerce],
          method: validations[:coerce_with],
          message: validations[:coerce_message]
        }
        validate('coerce', coerce_options, attrs, required, opts)
      end