Skip to content

Validators

Matt Muller edited this page Oct 5, 2021 · 4 revisions

Validators are classes that provide a method for validating Ruby input types against the Smithy model. Validators will have a 1:1 mapping to Smithy input structure shapes.

Each input structure shape will have a code generated validator. Seahorse::Validator is used to validate input when given the value, the Ruby types to check against, and context for any error messaging.

module SampleService
  module Validators

    class CreateHighScoreInput
      def self.validate!(input, context:)
        # Complex type
        HighScoreParams.validate!(input[:high_score], context: "#{context}[:high_score]")
      end
    end

    class HighScoreParams
      def self.validate!(input, context:)
        # Simple type
        Seahorse::Validator.validate!(input[:game], String, context: "#{context}[:game]")
        Seahorse::Validator.validate!(input[:score], Integer, context: "#{context}[:score]")
      end
    end

  end
end

Lists and sets are validated to be an Array or Set. Elements are validated using the index for context:

class SimpleList
  def self.validate!(input, context:)
    Seahorse::Validator.validate!(input, Array, context: context)
    input.each_with_index do |element, index|
      Seahorse::Validator.validate!(element, String, context: "#{context}[#{index}]")
    end
  end
end

Maps are validated to be a Hash. The hash keys will be validated to be either a String or Symbol. The value is validated using the key for context.

class SimpleMap
  def self.validate!(input, context:)
    Seahorse::Validator.validate!(input, Hash, context: context)
    input.each do |key, value|
      Seahorse::Validator.validate!(key, String, Symbol, context: "#{context}.keys")
      Seahorse::Validator.validate!(value, Integer, context: "#{context}[:#{key}]")
    end
  end
end

For unions, the input’s type is checked and the appropriate validate! method is called with that input’s value.

class EventStream
  def self.validate!(input, context:)
    case input
    when Types::EventStream::Start
      Start.validate!(input.__getobj__, context: context)
    when Types::EventStream::End
      End.validate!(input.__getobj__, context: context)
    when Types::EventStream::Log
      Log.validate!(input.__getobj__, context: context)
    when Types::EventStream::Unknown
      nil
    else
      raise ArgumentError,
            "Expected #{context} to be a union member of "\
            "Types::EventStream, got #{input.class}."
    end
  end

  class Start
    def self.validate!(input, context:)
      StructuredEvent.validate!(input, context: context)
    end
  end

  class End
    def self.validate!(input, context:)
      StructuredEvent.validate!(input, context: context)
    end
  end

  class Log
    def self.validate!(input, context:)
      Seahorse::Validator.validate!(input, String, context: context)
    end
  end
end

class StructuredEvent
  def self.validate!(input, context:)
    Seahorse::Validator.validate!(input[:message], String, context: "#{context}[:message]")
  end
end

The validators are used in Client middleware for each operation. A middleware validator for CreateHighScore might look like:

stack.use(
  Seahorse::Middleware::Validate,
  validator: Validators::CreateHighScoreInput,
  validate_input: options.fetch(:validate_input, @validate_input)
)

If validate_input is false, the validate! method for CreateHighScoreInput will not be called in the middleware. The validate_input option is true by default.