Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validation max, min length of string. Or max, min value of Number #766

Open
Kyokatarz opened this issue Jun 30, 2022 · 4 comments
Open

Validation max, min length of string. Or max, min value of Number #766

Kyokatarz opened this issue Jun 30, 2022 · 4 comments

Comments

@Kyokatarz
Copy link

Maybe I missed something from the documentation, but currently there is no way to validate this

@mathieujobin
Copy link
Collaborator

You could easily add one, for example, I have this one in my application. I'm not adding it to the gem because I don't have tests for it.

module Apipie::Validator
  class DateValidator < BaseValidator

    def self.build(param_description, argument, _options, _block)
      new(param_description) if argument == :date
    end

    def validate(value)
      pattern = /^\d{4}-\d{2}-\d{2}$/
      if param_description.options[:required]
        value.to_s.match(pattern)
      else
        value.to_s.match(pattern) || value.nil?
      end
    end

    def process_value(value)
      Date.parse(value)
    end

    def description
      "Must be a valid iso8601 date (YYYY-MM-DD)"
    end

  end
end

but it would be great to add new validator (with tests) to this gem..

@RyanTG
Copy link

RyanTG commented Sep 28, 2022

So if you want to set a max value for a param, custom validator is the best way to achieve this?

While I assume this is "easy", I don't really understand this custom validator schema too well, and if anyone has an example of a "max value" custom validator, I'd appreciate it.

@mathieujobin
Copy link
Collaborator

I don't have one checking for the maximum, but I have these defined.
hopefully that helps.

module Apipie::Validator
  class StreamValidator < BaseValidator

    def self.build(param_description, argument, options, block)
      if argument == :attachment
        self.new(param_description)
      end
    end

    def validate(value)
      if param_description.options[:required]
        value.nil? || value.respond_to?(:read)
      else
        true
      end
    end

    def description
      "Must be a file attachment"
    end

  end
end

module Apipie::Validator
  class NumberValidator < BaseValidator

    def self.build(param_description, argument, options, block)
      if argument == :number
        self.new(param_description)
      end
    end

    def validate(value)
      if param_description.options[:required]
        if value.to_s =~ /^\d+$/
          true
        elsif param_description.options[:allow_me]
          value == 'me'
        else
          false
        end
      else
        true
      end
    end

    def process_value(value)
      if value.kind_of?(Array) && param_description.options[:allow_array]
        value.map(&:to_i)
      else
        value.to_i
      end
    end

    def description
      "Must be a number"
    end

  end
end
module Apipie::Validator
  class DateValidator < BaseValidator

    def self.build(param_description, argument, _options, _block)
      new(param_description) if argument == :date
    end

    def validate(value)
      pattern = /^\d{4}-\d{2}-\d{2}$/
      if param_description.options[:required]
        value.to_s.match(pattern)
      else
        value.to_s.match(pattern) || value.nil?
      end
    end

    def process_value(value)
      Date.parse(value)
    end

    def description
      "Must be a valid iso8601 date (YYYY-MM-DD)"
    end

  end
end
module Apipie::Validator
  class FloatValidator < BaseValidator

    def self.build(param_description, argument, options, block)
      if argument == :float
        self.new(param_description)
      end
    end

    def validate(value)
      if param_description.options[:required]
        value.to_s.match(/^\d+(?:\.\d+)?$/)
      else
        true
      end
    end

    def description
      "Must be a floating point number"
    end

  end
end
module Apipie::Validator
  class BooleanValidator < BaseValidator

    def self.build(param_description, argument, options, block)
      self.new(param_description) if argument == :boolean
    end

    def validate(value)
      value.to_s.match(/^(true|false)$/) ||
        value.is_a?(TrueClass) ||
        value.is_a?(FalseClass) ||
        ignore_value?(value)
    end

    def description
      "Must be either true or false"
    end

    private

    def ignore_value?(value)
      param_description.options[:required] ? false : value.nil?
    end

  end
end
class Apipie::Validator::CollectionValidator < Apipie::Validator::BaseValidator
  def self.build(param_description, argument, _options, _block)
    if argument == :collection
      new(param_description, argument)
    end
  end

  def initialize(param_description, _argument, _options = {})
    super(param_description)
    @items_type = param_description.options[:of]
  end

  def validate(values)
    return false unless process_value(values).respond_to?(:each) && !process_value(values).is_a?(String)
    values.all? { |v| valid_value?(v) }
  end

  def process_value(values)
    return if values.blank? && !param_description.required
    values.map do |value|
      sub_validator.process_value(value)
    end
  end

  def description
    "Must be an array of #{items_type}"
  end

  def expected_type
    "array"
  end

  private

  def sub_validator
    @sub_validator ||= Apipie::Validator::BaseValidator.find(sub_param_description, items_type, {}, nil)
  end

  attr_reader :items_type

  def sub_param_description
    Apipie::ParamDescription.new(param_description.method_description,
                                 param_description.name,
                                 items_type)
  end

  def valid_value?(value)
    sub_validator.validate(value)
  end
end
module Apipie::Validator
  class TimestampValidator < BaseValidator

    def self.build(param_description, argument, options, block)
      self.new(param_description) if argument == :timestamp
    end

    def validate(value)
      if param_description.options[:required]
        value.to_s.match(/^\d+$/)
      else
        value.to_s.match(/^\d+$/) || value.nil?
      end
    end

    def description
      "Must be a valid timestamp"
    end

  end
end

@RyanTG
Copy link

RyanTG commented Sep 28, 2022

Greatly appreciate it, thank you :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants