ota42y/openapi_parser

View on GitHub
lib/openapi_parser/schema_validator/string_validator.rb

Summary

Maintainability
B
4 hrs
Test Coverage
class OpenAPIParser::SchemaValidator
  class StringValidator < Base
    include ::OpenAPIParser::SchemaValidator::Enumable

    def initialize(validator, coerce_value, datetime_coerce_class)
      super(validator, coerce_value)
      @datetime_coerce_class = datetime_coerce_class
    end

    def coerce_and_validate(value, schema, **_keyword_args)
      return OpenAPIParser::ValidateError.build_error_result(value, schema) unless value.kind_of?(String)

      value, err = check_enum_include(value, schema)
      return [nil, err] if err

      value, err = pattern_validate(value, schema)
      return [nil, err] if err

      value, err = validate_max_min_length(value, schema)
      return [nil, err] if err

      value, err = validate_email_format(value, schema)
      return [nil, err] if err

      value, err = validate_uuid_format(value, schema)
      return [nil, err] if err

      value, err = validate_date_format(value, schema)
      return [nil, err] if err

      value, err = validate_datetime_format(value, schema)
      return [nil, err] if err

      [value, nil]
    end

    private

      # @param [OpenAPIParser::Schemas::Schema] schema
      def pattern_validate(value, schema)
        # pattern support string only so put this
        return [value, nil] unless schema.pattern
        return [value, nil] if value =~ /#{schema.pattern}/

        [nil, OpenAPIParser::InvalidPattern.new(value, schema.pattern, schema.object_reference, schema.example)]
      end

      def validate_max_min_length(value, schema)
        return [nil, OpenAPIParser::MoreThanMaxLength.new(value, schema.object_reference)] if schema.maxLength && value.size > schema.maxLength
        return [nil, OpenAPIParser::LessThanMinLength.new(value, schema.object_reference)] if schema.minLength && value.size < schema.minLength

        [value, nil]
      end

      def validate_email_format(value, schema)
        return [value, nil] unless schema.format == 'email'

        # match? method is good performance.
        # So when we drop ruby 2.3 support we use match? method because this method add ruby 2.4
        #return [value, nil] if value.match?(URI::MailTo::EMAIL_REGEXP)
        return [value, nil] if value.match(URI::MailTo::EMAIL_REGEXP)

        return [nil, OpenAPIParser::InvalidEmailFormat.new(value, schema.object_reference)]
      end

      def validate_uuid_format(value, schema)
        return [value, nil] unless schema.format == 'uuid'

        return [value, nil] if value.match(/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}/)

        return [nil, OpenAPIParser::InvalidUUIDFormat.new(value, schema.object_reference)]
      end

      def validate_date_format(value, schema)
        return [value, nil] unless schema.format == 'date'

        begin
          parsed_date = Date.iso8601(value)
        rescue ArgumentError
          return [nil, OpenAPIParser::InvalidDateFormat.new(value, schema.object_reference)]
        end

        unless parsed_date.strftime('%Y-%m-%d') == value
          return [nil, OpenAPIParser::InvalidDateFormat.new(value, schema.object_reference)]
        end

        return [value, nil]
      end

      def validate_datetime_format(value, schema)
        return [value, nil] unless schema.format == 'date-time'

        begin
          if @datetime_coerce_class.nil?
            # validate only
            DateTime.rfc3339(value)
            [value, nil]
          else
            # validate and coerce
            if @datetime_coerce_class == Time
              [DateTime.rfc3339(value).to_time, nil]
            else
              [@datetime_coerce_class.rfc3339(value), nil]
            end
          end
        rescue ArgumentError
          # when rfc3339(value) failed
          [nil, OpenAPIParser::InvalidDateTimeFormat.new(value, schema.object_reference)]
        end
      end
  end
end