class Avro::SchemaValidator
Constants
- BOOLEAN_VALUES
- COMPLEX_TYPES
- DEFAULT_VALIDATION_OPTIONS
- INT_RANGE
- LONG_RANGE
- PATH_SEPARATOR
- RECURSIVE_SIMPLE_VALIDATION_OPTIONS
- ROOT_IDENTIFIER
- RUBY_CLASS_TO_AVRO_TYPE
- TypeMismatchError
Public Class Methods
validate!(expected_schema, logical_datum, options = DEFAULT_VALIDATION_OPTIONS)
click to toggle source
# File lib/avro/schema_validator.rb 74 def validate!(expected_schema, logical_datum, options = DEFAULT_VALIDATION_OPTIONS) 75 result = Result.new 76 if options.fetch(:recursive, true) 77 validate_recursive(expected_schema, logical_datum, ROOT_IDENTIFIER, result, options) 78 else 79 validate_simple(expected_schema, logical_datum, ROOT_IDENTIFIER, result, options) 80 end 81 fail ValidationError, result if result.failure? 82 result 83 end
Private Class Methods
actual_value_message(value)
click to toggle source
# File lib/avro/schema_validator.rb 226 def actual_value_message(value) 227 avro_type = if value.is_a?(Integer) 228 ruby_integer_to_avro_type(value) 229 else 230 ruby_to_avro_type(value.class) 231 end 232 if value.nil? 233 avro_type 234 else 235 "#{avro_type} with value #{value.inspect}" 236 end 237 end
deeper_path_for_hash(sub_key, path)
click to toggle source
# File lib/avro/schema_validator.rb 220 def deeper_path_for_hash(sub_key, path) 221 deeper_path = +"#{path}#{PATH_SEPARATOR}#{sub_key}" 222 deeper_path.squeeze!(PATH_SEPARATOR) 223 deeper_path.freeze 224 end
enum_message(symbols, datum)
click to toggle source
# File lib/avro/schema_validator.rb 168 def enum_message(symbols, datum) 169 "expected enum with values #{symbols}, got #{actual_value_message(datum)}" 170 end
first_compatible_type(datum, expected_schema, path, failures, options = {})
click to toggle source
# File lib/avro/schema_validator.rb 208 def first_compatible_type(datum, expected_schema, path, failures, options = {}) 209 expected_schema.schemas.find do |schema| 210 # Avoid expensive validation if we're just validating a nil 211 next datum.nil? if schema.type_sym == :null 212 213 result = Result.new 214 validate_recursive(schema, datum, path, result, options) 215 failures << { type: schema.type_sym, result: result } if result.failure? 216 !result.failure? 217 end 218 end
fixed_string_message(size, datum)
click to toggle source
# File lib/avro/schema_validator.rb 164 def fixed_string_message(size, datum) 165 "expected fixed with size #{size}, got \"#{datum}\" with size #{datum.bytesize}" 166 end
resolve_datum(expected_schema, logical_datum, encoded)
click to toggle source
# File lib/avro/schema_validator.rb 150 def resolve_datum(expected_schema, logical_datum, encoded) 151 if encoded 152 logical_datum 153 else 154 expected_schema.type_adapter.encode(logical_datum) rescue nil 155 end 156 end
ruby_integer_to_avro_type(value)
click to toggle source
# File lib/avro/schema_validator.rb 243 def ruby_integer_to_avro_type(value) 244 INT_RANGE.cover?(value) ? 'int' : 'long' 245 end
ruby_to_avro_type(ruby_class)
click to toggle source
# File lib/avro/schema_validator.rb 239 def ruby_to_avro_type(ruby_class) 240 RUBY_CLASS_TO_AVRO_TYPE.fetch(ruby_class, ruby_class) 241 end
validate_array(expected_schema, datum, path, result, options)
click to toggle source
# File lib/avro/schema_validator.rb 172 def validate_array(expected_schema, datum, path, result, options) 173 fail TypeMismatchError unless datum.is_a?(Array) 174 datum.each_with_index do |d, i| 175 validate_recursive(expected_schema.items, d, "#{path}[#{i}]", result, options) 176 end 177 end
validate_map(expected_schema, datum, path, result, options)
click to toggle source
# File lib/avro/schema_validator.rb 179 def validate_map(expected_schema, datum, path, result, options) 180 fail TypeMismatchError unless datum.is_a?(Hash) 181 datum.keys.each do |k| 182 result.add_error(path, "unexpected key type '#{ruby_to_avro_type(k.class)}' in map") unless k.is_a?(String) 183 end 184 datum.each do |k, v| 185 deeper_path = deeper_path_for_hash(k, path) 186 validate_recursive(expected_schema.values, v, deeper_path, result, options) 187 end 188 end
validate_recursive(expected_schema, logical_datum, path, result, options)
click to toggle source
# File lib/avro/schema_validator.rb 87 def validate_recursive(expected_schema, logical_datum, path, result, options) 88 datum = resolve_datum(expected_schema, logical_datum, options[:encoded]) 89 90 validate_simple(expected_schema, datum, path, result, RECURSIVE_SIMPLE_VALIDATION_OPTIONS) 91 92 case expected_schema.type_sym 93 when :array 94 validate_array(expected_schema, datum, path, result, options) 95 when :map 96 validate_map(expected_schema, datum, path, result, options) 97 when :union 98 validate_union(expected_schema, datum, path, result, options) 99 when :record, :error, :request 100 fail TypeMismatchError unless datum.is_a?(Hash) 101 expected_schema.fields.each do |field| 102 deeper_path = deeper_path_for_hash(field.name, path) 103 nested_value = datum.key?(field.name) ? datum[field.name] : datum[field.name.to_sym] 104 validate_recursive(field.type, nested_value, deeper_path, result, options) 105 end 106 if options[:fail_on_extra_fields] 107 datum_fields = datum.keys.map(&:to_s) 108 schema_fields = expected_schema.fields.map(&:name) 109 (datum_fields - schema_fields).each do |extra_field| 110 result.add_error(path, "extra field '#{extra_field}' - not in schema") 111 end 112 end 113 end 114 rescue TypeMismatchError 115 result.add_error(path, "expected type #{expected_schema.type_sym}, got #{actual_value_message(datum)}") 116 end
validate_simple(expected_schema, logical_datum, path, result, options)
click to toggle source
# File lib/avro/schema_validator.rb 118 def validate_simple(expected_schema, logical_datum, path, result, options) 119 datum = resolve_datum(expected_schema, logical_datum, options[:encoded]) 120 validate_type(expected_schema) 121 122 case expected_schema.type_sym 123 when :null 124 fail TypeMismatchError unless datum.nil? 125 when :boolean 126 fail TypeMismatchError unless BOOLEAN_VALUES.include?(datum) 127 when :string, :bytes 128 fail TypeMismatchError unless datum.is_a?(String) 129 when :int 130 fail TypeMismatchError unless datum.is_a?(Integer) 131 result.add_error(path, "out of bound value #{datum}") unless INT_RANGE.cover?(datum) 132 when :long 133 fail TypeMismatchError unless datum.is_a?(Integer) 134 result.add_error(path, "out of bound value #{datum}") unless LONG_RANGE.cover?(datum) 135 when :float, :double 136 fail TypeMismatchError unless datum.is_a?(Float) || datum.is_a?(Integer) || datum.is_a?(BigDecimal) 137 when :fixed 138 if datum.is_a? String 139 result.add_error(path, fixed_string_message(expected_schema.size, datum)) unless datum.bytesize == expected_schema.size 140 else 141 result.add_error(path, "expected fixed with size #{expected_schema.size}, got #{actual_value_message(datum)}") 142 end 143 when :enum 144 result.add_error(path, enum_message(expected_schema.symbols, datum)) unless expected_schema.symbols.include?(datum) 145 end 146 rescue TypeMismatchError 147 result.add_error(path, "expected type #{expected_schema.type_sym}, got #{actual_value_message(datum)}") 148 end
validate_type(expected_schema)
click to toggle source
# File lib/avro/schema_validator.rb 158 def validate_type(expected_schema) 159 unless Avro::Schema::VALID_TYPES_SYM.include?(expected_schema.type_sym) 160 fail "Unexpected schema type #{expected_schema.type_sym} #{expected_schema.inspect}" 161 end 162 end
validate_union(expected_schema, datum, path, result, options)
click to toggle source
# File lib/avro/schema_validator.rb 190 def validate_union(expected_schema, datum, path, result, options) 191 if expected_schema.schemas.size == 1 192 validate_recursive(expected_schema.schemas.first, datum, path, result, options) 193 return 194 end 195 failures = [] 196 compatible_type = first_compatible_type(datum, expected_schema, path, failures, options) 197 return unless compatible_type.nil? 198 199 complex_type_failed = failures.detect { |r| COMPLEX_TYPES.include?(r[:type]) } 200 if complex_type_failed 201 complex_type_failed[:result].errors.each { |error| result << error } 202 else 203 types = expected_schema.schemas.map { |s| "'#{s.type_sym}'" }.join(', ') 204 result.add_error(path, "expected union of [#{types}], got #{actual_value_message(datum)}") 205 end 206 end