Apipie/apipie-rails

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

Kyokatarz opened this issue · 4 comments

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

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..

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.

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

Greatly appreciate it, thank you :D