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