openc-json_schema 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- OWJiYzM4NmZlYzYxNzU5MmNjODQ0NTI3ODhmODcyNTAyZGU2MTZhNQ==
4
+ YmE0MDA3M2IxMWZhMTlhZGU5MDdhNmMzNzJmMjA3MzYzZGI5MjE5Mg==
5
5
  data.tar.gz: !binary |-
6
- NzFhYzk0MGQ0ODZlMWQ4Mjg4Njg0YjZkNWVlYWZiZDcyMGVhZjQzNw==
6
+ YjQyN2Q2YWYyMjgyYmJiZWJlN2YxMDk3MzU4ZWUzM2E5ZWMzZGI5ZA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NjM0ZTVlZDRjZGVlY2NkMWQyZjQ1YWNiZDdmYzg1MmJjNTllZmNhOTgwYWNj
10
- M2NlNGE0OGM0MzJhY2I5MmE0ZDRmZWIzMDQzZjkwYjYwZmJmNTA5YzRlMzk1
11
- OGQyZjZmZDdjMGI4YWE1ODYzM2JkZGNmOWM5OGIwMTllY2VmNDE=
9
+ MTBjYTFhZTIyOWMyNzUzOTZkNTJkZTI5OTU2NzY4NWU0N2FiNzkxNWJiNjQ3
10
+ MTViOTg0ODVjZjg0YTY3MjdiMzExMzZjMjQxNDQyOWE2ZDJlNzI1NTk4Mjg2
11
+ ODI3YmRkZGJlMzg3NmMzZTBkNmM5ZTFmYTJkOTI0MmI4Njc5Yjg=
12
12
  data.tar.gz: !binary |-
13
- NTE0OWZhNWMwNTJkOGYxOGFmN2M0NWY2NzJlZWViYzczODM4YWZiMjc2YzE1
14
- MTgwZmQ0MTc0ZGNiYjBhZDFhNzI4NDM1NWQ0ZTlkMTA4MDRiZmM4MTljMWY3
15
- YTFkOWFhYWFmOWMxY2UzOGE3NzY2OWEwNGM4NjdiYThkZTg5YjU=
13
+ OTBjNjliN2EzOWUzZmRhMDcyMzk0ZDkyMzc4OWFkNzZjMzFhNjUxYTM0ZTNh
14
+ MjFiODNlYTVlMTVkYzdhMDgxNjY0MzllMThjZTRkOGFjM2ZhYTMxMWQ4NTA1
15
+ N2RmNmVjNWJlYWJkNmRkMGM2N2VhZTZlNzMwYjQ0YWFhMjk1ZWQ=
data/.gitignore CHANGED
@@ -1 +1,2 @@
1
1
  spec/tmp/*.json
2
+ coverage
data/Gemfile CHANGED
@@ -2,3 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in openc-json-schema.gemspec
4
4
  gemspec
5
+
6
+ gem 'json-schema', :git => 'git://github.com/ruby-json-schema/json-schema.git', :ref => 'aded4d798a48545184dae7ae0a3bb41ec2794c88'
7
+
8
+ gem 'simplecov', :require => false
data/Gemfile.lock CHANGED
@@ -1,16 +1,22 @@
1
+ GIT
2
+ remote: git://github.com/ruby-json-schema/json-schema.git
3
+ revision: aded4d798a48545184dae7ae0a3bb41ec2794c88
4
+ ref: aded4d798a48545184dae7ae0a3bb41ec2794c88
5
+ specs:
6
+ json-schema (2.5.0)
7
+ addressable (~> 2.3)
8
+
1
9
  PATH
2
10
  remote: .
3
11
  specs:
4
- openc-json_schema (0.0.3)
5
- json-schema (= 2.5.0)
12
+ openc-json_schema (0.0.4)
6
13
 
7
14
  GEM
8
15
  remote: https://rubygems.org/
9
16
  specs:
10
17
  addressable (2.3.7)
11
18
  diff-lcs (1.2.5)
12
- json-schema (2.5.0)
13
- addressable (~> 2.3)
19
+ multi_json (1.10.1)
14
20
  rake (10.4.2)
15
21
  rspec (3.1.0)
16
22
  rspec-core (~> 3.1.0)
@@ -24,12 +30,18 @@ GEM
24
30
  rspec-mocks (3.1.3)
25
31
  rspec-support (~> 3.1.0)
26
32
  rspec-support (3.1.2)
33
+ simplecov (0.7.1)
34
+ multi_json (~> 1.0)
35
+ simplecov-html (~> 0.7.1)
36
+ simplecov-html (0.7.1)
27
37
 
28
38
  PLATFORMS
29
39
  ruby
30
40
 
31
41
  DEPENDENCIES
32
42
  bundler (~> 1.7)
43
+ json-schema!
33
44
  openc-json_schema!
34
45
  rake (~> 10.0)
35
46
  rspec (~> 3.0)
47
+ simplecov
@@ -7,8 +7,8 @@ module Openc
7
7
  module JsonSchema
8
8
  extend self
9
9
 
10
- def validate(schema_or_filename, schema_dir, record)
11
- Validator.validate(schema_or_filename, schema_dir, record)
10
+ def validate(schema_path, record)
11
+ Validator.validate(schema_path, record)
12
12
  end
13
13
  end
14
14
  end
@@ -3,64 +3,100 @@ module Openc
3
3
  module Validator
4
4
  extend self
5
5
 
6
- def validate(schema_or_filename, schema_dir, record)
7
- case schema_or_filename
8
- when Hash
9
- schema = schema_or_filename
10
- when String
11
- filename = schema_or_filename
12
- schema = JSON.parse(File.read(File.join(schema_dir, filename)))
13
- else
14
- raise TypeError.new("Unexpected value for schema_or_filename: #{schema_or_filename.class}")
15
- end
16
-
17
- # We must change directory for the relative paths in schemas to make sense.
18
- errors = Dir.chdir(schema_dir) do
19
- JSON::Validator.fully_validate(schema, record, :errors_as_objects => true)
20
- end
6
+ def validate(schema_path, record)
7
+ validator = JSON::Validator.new(
8
+ schema_path,
9
+ record,
10
+ :record_errors => true,
11
+ :errors_as_objects => true,
12
+ :validate_schema => true
13
+ )
14
+ errors = validator.validate
21
15
 
22
16
  # For now, we just handle the first error.
23
17
  error = errors[0]
24
18
  return if error.nil?
25
19
 
26
- case error[:failed_attribute]
27
- when 'Required'
28
- match = error[:message].match(/required property of '(.*)'/)
29
- missing_property = match[1]
30
- path = fragment_to_path("#{error[:fragment]}/#{missing_property}")
20
+ convert_error(extract_error(error, record, validator))
21
+ end
31
22
 
32
- {:type => :missing, :path => path}
33
- when 'OneOf'
23
+ def extract_error(error, record, validator)
24
+ if error[:failed_attribute] == 'OneOf'
34
25
  if error[:message].match(/did not match any/)
35
26
  path_elements = fragment_to_path(error[:fragment]).split('.')
36
27
 
37
- record_fragment = record
38
- schema_fragment = schema
28
+ json_schema = validator.instance_variable_get(:@base_schema)
29
+ schema = json_schema.schema
39
30
 
40
31
  path_elements.each do |element|
41
- record_fragment = record_fragment[element]
42
- schema_fragment = schema_fragment['properties'][element]
32
+ record = record[element]
33
+ schema = schema['properties'][element]
34
+
35
+ if (ref = schema['$ref'])
36
+ schema_uri = validator.absolutize_ref_uri(ref, json_schema.uri)
37
+ json_schema = JSON::Validator.schema_reader.read(schema_uri)
38
+ schema = json_schema.schema
39
+ end
43
40
  end
44
41
 
45
- one_of_schemas = schema_fragment['oneOf']
42
+ one_of_schemas = schema['oneOf']
46
43
 
47
- unless one_of_schemas.nil?
48
- one_of_schemas.each do |s|
49
- s['properties'].each do |k, v|
50
- next if v['enum'].nil?
44
+ schemas_for_type_with_ix = case record
45
+ when Hash
46
+ one_of_schemas.each_with_index.reject {|s, ix| s['properties'].nil?}
47
+ when String
48
+ one_of_schemas.each_with_index.select {|s, ix| s['type'] == 'string' || (s['type'].nil? && s['properties'].nil?)}
49
+ when Integer
50
+ one_of_schemas.each_with_index.select {|s, ix| s['type'] == 'integer'}
51
+ when Array
52
+ one_of_schemas.each_with_index.select {|s, ix| s['type'] == 'array'}
53
+ else
54
+ raise "Unexpected type: #{record}"
55
+ end
51
56
 
52
- if v['enum'].include?(record_fragment[k])
53
- error1 = validate(s, schema_dir, record_fragment)
54
- return error1.merge(:path => "#{path_elements.join('.')}.#{error1[:path]}")
57
+ case schemas_for_type_with_ix.size
58
+ when 0
59
+ return error
60
+ when 1
61
+ ix = schemas_for_type_with_ix[0][1]
62
+ return error[:errors][:"oneof_#{ix}"][0]
63
+ else
64
+ if record.is_a?(Hash)
65
+ schemas_for_type_with_ix.each do |s, ix|
66
+ s['properties'].each do |k, v|
67
+ next if v['enum'].nil?
68
+
69
+ if v['enum'].include?(record[k])
70
+ return error[:errors][:"oneof_#{ix}"][0]
71
+ end
55
72
  end
56
73
  end
74
+ else
75
+ return error
57
76
  end
58
77
  end
78
+ end
79
+ end
80
+
81
+ error
82
+ end
83
+
84
+ def convert_error(error)
85
+ case error[:failed_attribute]
86
+ when 'Required'
87
+ match = error[:message].match(/required property of '(.*)'/)
88
+ missing_property = match[1]
89
+ path = fragment_to_path("#{error[:fragment]}/#{missing_property}")
59
90
 
91
+ {:type => :missing, :path => path}
92
+ when 'OneOf'
93
+ if error[:message].match(/did not match any/)
60
94
  {:type => :one_of_no_matches, :path => fragment_to_path(error[:fragment])}
61
95
  else
62
96
  {:type => :one_of_many_matches, :path => fragment_to_path(error[:fragment])}
63
97
  end
98
+ when 'AnyOf'
99
+ {:type => :any_of_no_matches, :path => fragment_to_path(error[:fragment])}
64
100
  when 'MinLength'
65
101
  match = error[:message].match(/minimum string length of (\d+) in/)
66
102
  min_length = match[1].to_i
@@ -77,6 +113,10 @@ module Openc
77
113
  match = error[:message].match(/the following values: ([\w\s,]+) in schema/)
78
114
  allowed_values = match[1].split(',').map(&:strip)
79
115
  {:type => :enum_mismatch, :path => fragment_to_path(error[:fragment]), :allowed_values => allowed_values}
116
+ when 'AdditionalProperties'
117
+ match = error[:message].match(/contains additional properties \["(.*)"\] outside of the schema/)
118
+ extra_properties = match[1].split('", "')
119
+ {:type => :extra_properties, :path => fragment_to_path(error[:fragment]), :extra_properties => extra_properties}
80
120
  else
81
121
  if error[:message].match(/must be of format yyyy-mm-dd/)
82
122
  {:type => :format_mismatch, :path => fragment_to_path(error[:fragment]), :expected_format => 'yyyy-mm-dd'}
@@ -1,5 +1,5 @@
1
1
  module Openc
2
2
  module JsonSchema
3
- VERSION = '0.0.3'
3
+ VERSION = '0.0.4'
4
4
  end
5
5
  end
@@ -21,5 +21,5 @@ Gem::Specification.new do |spec|
21
21
  spec.add_development_dependency "rake", "~> 10.0"
22
22
  spec.add_development_dependency "rspec", "~> 3.0"
23
23
 
24
- spec.add_dependency "json-schema", "2.5.0"
24
+ # spec.add_dependency "json-schema", "2.5.0"
25
25
  end
@@ -277,6 +277,25 @@ describe Openc::JsonSchema do
277
277
  )
278
278
  end
279
279
 
280
+ specify 'when additional properties are present but disallowed' do
281
+ schema = {
282
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
283
+ 'type' => 'object',
284
+ 'properties' => {
285
+ 'aaa' => {'type' => 'number'}
286
+ },
287
+ 'additionalProperties' => false
288
+ }
289
+
290
+ record = {'aaa' => 1, 'bbb' => 2, 'ccc' => 3}
291
+
292
+ expect([schema, record]).to fail_validation_with(
293
+ :type => :extra_properties,
294
+ :path => '',
295
+ :extra_properties => ['bbb', 'ccc']
296
+ )
297
+ end
298
+
280
299
  specify 'when property of wrong format' do
281
300
  schema = {
282
301
  '$schema' => 'http://json-schema.org/draft-04/schema#',
@@ -329,7 +348,72 @@ describe Openc::JsonSchema do
329
348
  end
330
349
  end
331
350
 
332
- specify 'when schema includes oneOfs which contain $refs' do
351
+ context 'when schema includes nested $refs' do
352
+ specify 'when data is valid' do
353
+ schema_path = 'spec/schemas/fff.json'
354
+ record = {'fff' => {'ggg' => {'hhh' => 123}}}
355
+ expect([schema_path, record]).to be_valid
356
+ end
357
+
358
+ specify 'when data is invalid' do
359
+ schema_path = 'spec/schemas/fff.json'
360
+ record = {'fff' => {'ggg' => {'hhh' => '123'}}}
361
+ expect([schema_path, record]).to fail_validation_with(
362
+ :type => :type_mismatch,
363
+ :path => 'fff.ggg.hhh',
364
+ :allowed_types => ['number']
365
+ )
366
+ end
367
+
368
+ context 'and schema is an included schema' do
369
+ specify 'when data is valid' do
370
+ schema_path = 'spec/schemas/includes/ggg.json'
371
+ record = {'ggg' => {'hhh' => 123}}
372
+ expect([schema_path, record]).to be_valid
373
+ end
374
+
375
+ specify 'when data is invalid' do
376
+ schema_path = 'spec/schemas/includes/ggg.json'
377
+ record = {'ggg' => {'hhh' => '123'}}
378
+ expect([schema_path, record]).to fail_validation_with(
379
+ :type => :type_mismatch,
380
+ :path => 'ggg.hhh',
381
+ :allowed_types => ['number']
382
+ )
383
+ end
384
+ end
385
+
386
+ context 'and there is a $ref to something in an outer directory' do
387
+ specify 'when data is valid' do
388
+ schema_path = 'spec/schemas/iii.json'
389
+ record = {'iii' => {'jjj' => {'kkk' => 123}}}
390
+ expect([schema_path, record]).to be_valid
391
+ end
392
+
393
+ specify 'when data is invalid' do
394
+ schema_path = 'spec/schemas/iii.json'
395
+ record = {'iii' => {'jjj' => {'kkk' => '123'}}}
396
+ expect([schema_path, record]).to fail_validation_with(
397
+ :type => :type_mismatch,
398
+ :path => 'iii.jjj.kkk',
399
+ :allowed_types => ['number']
400
+ )
401
+ end
402
+ end
403
+ end
404
+
405
+ specify 'when schema includes oneOfs which contain $refs directly' do
406
+ schema_path = 'spec/schemas/lll.json'
407
+ record = {
408
+ 'mmm' => []
409
+ }
410
+ expect([schema_path, record]).to fail_validation_with(
411
+ :type => :one_of_no_matches,
412
+ :path => 'mmm'
413
+ )
414
+ end
415
+
416
+ specify 'when schema includes oneOfs which contain $refs indirectly' do
333
417
  schema_path = 'spec/schemas/ccc.json'
334
418
  record = {
335
419
  'ccc' => {
@@ -345,5 +429,32 @@ describe Openc::JsonSchema do
345
429
  :allowed_types => ['number'],
346
430
  )
347
431
  end
432
+
433
+ specify '' do
434
+ schema = {
435
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
436
+ 'type' => 'object',
437
+ 'properties' => {
438
+ 'aaa' => {
439
+ 'oneOf' => [
440
+ {
441
+ 'type' => 'string',
442
+ 'format' => 'date'
443
+ },
444
+ {
445
+ 'type' => 'integer',
446
+ 'maxLength' => 2
447
+ }
448
+ ]
449
+ }
450
+ }
451
+ }
452
+ record = {'aaa' => 'not-a-date'}
453
+ expect([schema, record]).to fail_validation_with(
454
+ :type => :format_mismatch,
455
+ :path => 'aaa',
456
+ :expected_format => 'yyyy-mm-dd'
457
+ )
458
+ end
348
459
  end
349
460
  end
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-04/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "fff": {"$ref": "includes/ggg.json"}
6
+ }
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-04/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "iii": {"$ref": "includes/jjj.json"}
6
+ }
7
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "type": "object",
3
+ "properties": {
4
+ "ggg": {"$ref": "hhh.json"}
5
+ }
6
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "type": "object",
3
+ "properties": {
4
+ "hhh": {
5
+ "type": "number"
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "type": "object",
3
+ "properties": {
4
+ "jjj": {"$ref": "../kkk.json"}
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "oneOf": [
3
+ {"type": "integer"},
4
+ {"type": "string"}
5
+ ]
6
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "type": "object",
3
+ "properties": {
4
+ "kkk": {
5
+ "type": "number"
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-04/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "mmm": {
6
+ "$ref": "includes/mmm.json"
7
+ }
8
+ },
9
+ "additionalProperties": false
10
+ }
11
+
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,6 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
1
4
  require 'openc/json_schema'
2
5
 
3
6
  def get_error(schema_or_path, record)
@@ -5,17 +8,15 @@ def get_error(schema_or_path, record)
5
8
  when Hash
6
9
  json_data = schema_or_path.to_json
7
10
  filename = Digest::MD5.hexdigest(json_data) + '.json'
8
- schema_dir = "spec/tmp"
9
- File.open(File.join(schema_dir, filename), 'w') {|f| f.write(json_data)}
10
- schema_or_filename = schema_or_path
11
+ schema_path = File.join('spec', 'tmp', filename)
12
+ File.open(schema_path, 'w') {|f| f.write(json_data)}
11
13
  when String
12
- schema_or_filename = File.basename(schema_or_path)
13
- schema_dir = File.dirname(schema_or_path)
14
+ schema_path = schema_or_path
14
15
  else
15
16
  raise
16
17
  end
17
18
 
18
- error = Openc::JsonSchema.validate(schema_or_filename, schema_dir, record)
19
+ error = Openc::JsonSchema.validate(schema_path, record)
19
20
  end
20
21
 
21
22
  RSpec::Matchers.define(:fail_validation_with) do |expected|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openc-json_schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - OpenCorporates
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-05 00:00:00.000000000 Z
11
+ date: 2015-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - ~>
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
- - !ruby/object:Gem::Dependency
56
- name: json-schema
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - '='
60
- - !ruby/object:Gem::Version
61
- version: 2.5.0
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - '='
67
- - !ruby/object:Gem::Version
68
- version: 2.5.0
69
55
  description:
70
56
  email: info@opencorporates.com
71
57
  executables: []
@@ -87,9 +73,17 @@ files:
87
73
  - spec/openc_json_schema_spec.rb
88
74
  - spec/schemas/aaa.json
89
75
  - spec/schemas/ccc.json
76
+ - spec/schemas/fff.json
77
+ - spec/schemas/iii.json
90
78
  - spec/schemas/includes/bbb.json
91
79
  - spec/schemas/includes/ddd.json
92
80
  - spec/schemas/includes/eee.json
81
+ - spec/schemas/includes/ggg.json
82
+ - spec/schemas/includes/hhh.json
83
+ - spec/schemas/includes/jjj.json
84
+ - spec/schemas/includes/mmm.json
85
+ - spec/schemas/kkk.json
86
+ - spec/schemas/lll.json
93
87
  - spec/spec_helper.rb
94
88
  - spec/tmp/.gitkeep
95
89
  homepage:
@@ -120,9 +114,17 @@ test_files:
120
114
  - spec/openc_json_schema_spec.rb
121
115
  - spec/schemas/aaa.json
122
116
  - spec/schemas/ccc.json
117
+ - spec/schemas/fff.json
118
+ - spec/schemas/iii.json
123
119
  - spec/schemas/includes/bbb.json
124
120
  - spec/schemas/includes/ddd.json
125
121
  - spec/schemas/includes/eee.json
122
+ - spec/schemas/includes/ggg.json
123
+ - spec/schemas/includes/hhh.json
124
+ - spec/schemas/includes/jjj.json
125
+ - spec/schemas/includes/mmm.json
126
+ - spec/schemas/kkk.json
127
+ - spec/schemas/lll.json
126
128
  - spec/spec_helper.rb
127
129
  - spec/tmp/.gitkeep
128
130
  has_rdoc: