lib/graphql/schema/validator/length_validator.rb



# frozen_string_literal: true

module GraphQL
  class Schema
    class Validator
      # Use this to enforce a `.length` restriction on incoming values. It works for both Strings and Lists.
      #
      # @example Allow no more than 10 IDs
      #
      #   argument :ids, [ID], required: true, validates: { length: { maximum: 10 } }
      #
      # @example Require three selections
      #
      #   argument :ice_cream_preferences, [ICE_CREAM_FLAVOR], required: true, validates: { length: { is: 3 } }
      #
      class LengthValidator < Validator
        # @param maximum [Integer]
        # @param too_long [String] Used when `maximum` is exceeded or value is greater than `within`
        # @param minimum [Integer]
        # @param too_short [String] Used with value is less than `minimum` or less than `within`
        # @param is [Integer] Exact length requirement
        # @param wrong_length [String] Used when value doesn't match `is`
        # @param within [Range] An allowed range (becomes `minimum:` and `maximum:` under the hood)
        # @param message [String]
        def initialize(
          maximum: nil, too_long: "%{validated} is too long (maximum is %{count})",
          minimum: nil, too_short: "%{validated} is too short (minimum is %{count})",
          is: nil, within: nil, wrong_length: "%{validated} is the wrong length (should be %{count})",
          message: nil,
          **default_options
        )
          if within && (minimum || maximum)
            raise ArgumentError, "`length: { ... }` may include `within:` _or_ `minimum:`/`maximum:`, but not both"
          end
          # Under the hood, `within` is decomposed into `minimum` and `maximum`
          @maximum = maximum || (within && within.max)
          @too_long = message || too_long
          @minimum = minimum || (within && within.min)
          @too_short = message || too_short
          @is = is
          @wrong_length = message || wrong_length
          super(**default_options)
        end

        def validate(_object, _context, value)
          if @maximum && value.length > @maximum
            partial_format(@too_long, { count: @maximum })
          elsif @minimum && value.length < @minimum
            partial_format(@too_short, { count: @minimum })
          elsif @is && value.length != @is
            partial_format(@wrong_length, { count: @is })
          end
        end
      end
    end
  end
end