class Avro::SchemaValidator

Constants

COMPLEX_TYPES
INT_RANGE
LONG_RANGE
PATH_SEPARATOR
ROOT_IDENTIFIER
TypeMismatchError

Public Class Methods

validate!(expected_schema, logical_datum, encoded = false) click to toggle source
   # File lib/avro/schema_validator.rb
65 def validate!(expected_schema, logical_datum, encoded = false)
66   result = Result.new
67   validate_recursive(expected_schema, logical_datum, ROOT_IDENTIFIER, result, encoded)
68   fail ValidationError, result if result.failure?
69   result
70 end

Private Class Methods

actual_value_message(value) click to toggle source
    # File lib/avro/schema_validator.rb
173 def actual_value_message(value)
174   avro_type = if value.class == Integer
175                 ruby_integer_to_avro_type(value)
176               else
177                 ruby_to_avro_type(value.class)
178               end
179   if value.nil?
180     avro_type
181   else
182     "#{avro_type} with value #{value.inspect}"
183   end
184 end
deeper_path_for_hash(sub_key, path) click to toggle source
    # File lib/avro/schema_validator.rb
167 def deeper_path_for_hash(sub_key, path)
168   "#{path}#{PATH_SEPARATOR}#{sub_key}".squeeze(PATH_SEPARATOR)
169 end
ruby_integer_to_avro_type(value) click to toggle source
    # File lib/avro/schema_validator.rb
197 def ruby_integer_to_avro_type(value)
198   INT_RANGE.cover?(value) ? 'int' : 'long'
199 end
ruby_to_avro_type(ruby_class) click to toggle source
    # File lib/avro/schema_validator.rb
186 def ruby_to_avro_type(ruby_class)
187   {
188     NilClass => 'null',
189     String => 'string',
190     Fixnum => 'int',
191     Bignum => 'long',
192     Float => 'float',
193     Hash => 'record'
194   }.fetch(ruby_class, ruby_class)
195 end
validate_array(expected_schema, datum, path, result) click to toggle source
    # File lib/avro/schema_validator.rb
125 def validate_array(expected_schema, datum, path, result)
126   fail TypeMismatchError unless datum.is_a?(Array)
127   datum.each_with_index do |d, i|
128     validate_recursive(expected_schema.items, d, path + "[#{i}]", result)
129   end
130 end
validate_map(expected_schema, datum, path, result) click to toggle source
    # File lib/avro/schema_validator.rb
132 def validate_map(expected_schema, datum, path, result)
133   datum.keys.each do |k|
134     result.add_error(path, "unexpected key type '#{ruby_to_avro_type(k.class)}' in map") unless k.is_a?(String)
135   end
136   datum.each do |k, v|
137     deeper_path = deeper_path_for_hash(k, path)
138     validate_recursive(expected_schema.values, v, deeper_path, result)
139   end
140 end
validate_possible_types(datum, expected_schema, path) click to toggle source
    # File lib/avro/schema_validator.rb
159 def validate_possible_types(datum, expected_schema, path)
160   expected_schema.schemas.map do |schema|
161     result = Result.new
162     validate_recursive(schema, datum, path, result)
163     { type: schema.type_sym, result: result }
164   end
165 end
validate_recursive(expected_schema, logical_datum, path, result, encoded = false) click to toggle source
    # File lib/avro/schema_validator.rb
 74 def validate_recursive(expected_schema, logical_datum, path, result, encoded = false)
 75   datum = if encoded
 76             logical_datum
 77           else
 78             expected_schema.type_adapter.encode(logical_datum) rescue nil
 79           end
 80 
 81   case expected_schema.type_sym
 82   when :null
 83     fail TypeMismatchError unless datum.nil?
 84   when :boolean
 85     fail TypeMismatchError unless [true, false].include?(datum)
 86   when :string, :bytes
 87     fail TypeMismatchError unless datum.is_a?(String)
 88   when :int
 89     fail TypeMismatchError unless datum.is_a?(Integer)
 90     result.add_error(path, "out of bound value #{datum}") unless INT_RANGE.cover?(datum)
 91   when :long
 92     fail TypeMismatchError unless datum.is_a?(Integer)
 93     result.add_error(path, "out of bound value #{datum}") unless LONG_RANGE.cover?(datum)
 94   when :float, :double
 95     fail TypeMismatchError unless [Float, Integer].any?(&datum.method(:is_a?))
 96   when :fixed
 97     if datum.is_a? String
 98       message = "expected fixed with size #{expected_schema.size}, got \"#{datum}\" with size #{datum.size}"
 99       result.add_error(path, message) unless datum.bytesize == expected_schema.size
100     else
101       result.add_error(path, "expected fixed with size #{expected_schema.size}, got #{actual_value_message(datum)}")
102     end
103   when :enum
104     message = "expected enum with values #{expected_schema.symbols}, got #{actual_value_message(datum)}"
105     result.add_error(path, message) unless expected_schema.symbols.include?(datum)
106   when :array
107     validate_array(expected_schema, datum, path, result)
108   when :map
109     validate_map(expected_schema, datum, path, result)
110   when :union
111     validate_union(expected_schema, datum, path, result)
112   when :record, :error, :request
113     fail TypeMismatchError unless datum.is_a?(Hash)
114     expected_schema.fields.each do |field|
115       deeper_path = deeper_path_for_hash(field.name, path)
116       validate_recursive(field.type, datum[field.name], deeper_path, result)
117     end
118   else
119     fail "Unexpected schema type #{expected_schema.type_sym} #{expected_schema.inspect}"
120   end
121 rescue TypeMismatchError
122   result.add_error(path, "expected type #{expected_schema.type_sym}, got #{actual_value_message(datum)}")
123 end
validate_union(expected_schema, datum, path, result) click to toggle source
    # File lib/avro/schema_validator.rb
142 def validate_union(expected_schema, datum, path, result)
143   if expected_schema.schemas.size == 1
144     validate_recursive(expected_schema.schemas.first, datum, path, result)
145     return
146   end
147   types_and_results = validate_possible_types(datum, expected_schema, path)
148   failures, successes = types_and_results.partition { |r| r[:result].failure? }
149   return if successes.any?
150   complex_type_failed = failures.detect { |r| COMPLEX_TYPES.include?(r[:type]) }
151   if complex_type_failed
152     complex_type_failed[:result].errors.each { |error| result << error }
153   else
154     types = expected_schema.schemas.map { |s| "'#{s.type_sym}'" }.join(', ')
155     result.add_error(path, "expected union of [#{types}], got #{actual_value_message(datum)}")
156   end
157 end