dato_json_schema 0.20.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 735c70cc15ebcd63ca8bf57f522e3530a5188edde5604477485248ec8492311f
4
+ data.tar.gz: ef9fa70ea29692a09b7b18b947305beb245060dcb67f30ecef35590d1c649fcf
5
+ SHA512:
6
+ metadata.gz: da29a2be214acabeda042ab5d2a170711054b676087c56e568b41d83170cad46e63280e911cd4a01c5f59092418a9e507ef5bc2c80c7164ddd5680e065c029fe
7
+ data.tar.gz: b28c3d0fc1db24f3a11ecff03fe74c1006bfeaa97421ec2ce39bc94a03d364ea5926448a11cc13dcb58551daf2361f1bc087a18097175893d462d686a7d72336
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014-2015 Brandur and contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # json_schema
2
+
3
+ A JSON Schema V4 and Hyperschema V4 parser and validator.
4
+
5
+ Validate some data based on a JSON Schema:
6
+
7
+ ```
8
+ gem install json_schema
9
+ validate-schema schema.json data.json
10
+ ```
11
+
12
+ ## Programmatic
13
+
14
+ ``` ruby
15
+ require "json"
16
+ require "json_schema"
17
+
18
+ # parse the schema - raise SchemaError if it's invalid
19
+ schema_data = JSON.parse(File.read("schema.json"))
20
+ schema = JsonSchema.parse!(schema_data)
21
+
22
+ # expand $ref nodes - raise SchemaError if unable to resolve
23
+ schema.expand_references!
24
+
25
+ # validate some data - raise ValidationError if it doesn't conform
26
+ data = JSON.parse(File.read("data.json"))
27
+ schema.validate!(data)
28
+
29
+ # iterate through hyperschema links
30
+ schema.links.each do |link|
31
+ puts "#{link.method} #{link.href}"
32
+ end
33
+
34
+ # abort on first error, instead of listing them all:
35
+ schema.validate!(data, fail_fast: true)
36
+ ```
37
+
38
+ Errors have a `message` (for humans), and `type` (for machines).
39
+ `ValidationError`s also include a `path`, a JSON pointer to the location in
40
+ the supplied document which violated the schema. See [errors](docs/errors.md)
41
+ for more info.
42
+
43
+ Non-bang methods return a two-element array, with `true`/`false` at index 0
44
+ to indicate pass/fail, and an array of errors at index 1 (if any).
45
+
46
+ Passing `fail_fast: true` (default: `false`) will cause the validator to abort
47
+ on the first error it encounters and report just that. Even on fully valid data
48
+ this can offer some speed improvement, since it doesn't have to collect error
49
+ messages that might be later discarded (think of e.g. the `anyOf` directive).
50
+
51
+ ## Development
52
+
53
+ Run the test suite with:
54
+
55
+ ```
56
+ rake
57
+ ```
58
+
59
+ Or run specific suites or tests with:
60
+
61
+ ```
62
+ ruby -Ilib -Itest test/json_schema/validator_test.rb
63
+ ruby -Ilib -Itest test/json_schema/validator_test.rb -n /anyOf/
64
+ ```
65
+
66
+ ## Release
67
+
68
+ 1. Update the version in `json_schema.gemspec` as appropriate for [semantic
69
+ versioning](http://semver.org) and add details to `CHANGELOG`.
70
+ 2. `git commit` those changes with a message like "Bump version to x.y.z".
71
+ 3. Run the `release` task:
72
+
73
+ ```
74
+ bundle exec rake release
75
+ ```
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "optparse"
4
+ require_relative "../lib/commands/validate_schema"
5
+
6
+ def print_usage!
7
+ $stderr.puts "Usage: validate-schema <schema> <data>, ..."
8
+ $stderr.puts " validate-schema -d <data>, ..."
9
+ end
10
+
11
+ command = Commands::ValidateSchema.new
12
+
13
+ parser = OptionParser.new { |opts|
14
+ opts.on("-d", "--detect", "Detect schema from $schema") do
15
+ command.detect = true
16
+
17
+ # mix in common schemas for convenience
18
+ command.extra_schemas += ["schema.json", "hyper-schema.json"].
19
+ map { |f| File.expand_path(f, __FILE__ + "/../../schemas") }
20
+ end
21
+ opts.on("-s", "--schema SCHEMA", "Additional schema to use for references") do |s|
22
+ command.extra_schemas << s
23
+ end
24
+ opts.on("-f", "--fail-fast", "Abort after encountering the first validation error") do |s|
25
+ command.fail_fast = true
26
+ end
27
+ }
28
+
29
+ parser.parse!
30
+ success = command.run(ARGV.dup)
31
+
32
+ if success
33
+ command.messages.each { |m| $stdout.puts(m) }
34
+ elsif !command.errors.empty?
35
+ command.errors.each { |e| $stderr.puts(e) }
36
+ exit(1)
37
+ else
38
+ print_usage!
39
+ exit(1)
40
+ end
@@ -0,0 +1,130 @@
1
+ require "json"
2
+ require "yaml"
3
+ require_relative "../json_schema"
4
+
5
+ module Commands
6
+ class ValidateSchema
7
+ attr_accessor :detect
8
+ attr_accessor :fail_fast
9
+ attr_accessor :extra_schemas
10
+
11
+ attr_accessor :errors
12
+ attr_accessor :messages
13
+
14
+ def initialize
15
+ @detect = false
16
+ @fail_fast = false
17
+ @extra_schemas = []
18
+
19
+ @errors = []
20
+ @messages = []
21
+ end
22
+
23
+ def run(argv)
24
+ return false if !initialize_store
25
+
26
+ if !detect
27
+ return false if !(schema_file = argv.shift)
28
+ return false if !(schema = parse(schema_file))
29
+ end
30
+
31
+ # if there are no remaining files in arguments, also a problem
32
+ return false if argv.count < 1
33
+
34
+ argv.each do |data_file|
35
+ if !(data = read_file(data_file))
36
+ return false
37
+ end
38
+
39
+ if detect
40
+ if !(schema_uri = data["$schema"])
41
+ @errors = ["#{data_file}: No $schema tag for detection."]
42
+ return false
43
+ end
44
+
45
+ if !(schema = @store.lookup_schema(schema_uri))
46
+ @errors = ["#{data_file}: Unknown $schema, try specifying one with -s."]
47
+ return false
48
+ end
49
+ end
50
+
51
+ valid, errors = schema.validate(data, fail_fast: fail_fast)
52
+
53
+ if valid
54
+ @messages += ["#{data_file} is valid."]
55
+ else
56
+ @errors = map_schema_errors(data_file, errors)
57
+ end
58
+ end
59
+
60
+ @errors.empty?
61
+ end
62
+
63
+ private
64
+
65
+ def initialize_store
66
+ @store = JsonSchema::DocumentStore.new
67
+ extra_schemas.each do |extra_schema|
68
+ if !(extra_schema = parse(extra_schema))
69
+ return false
70
+ end
71
+ @store.add_schema(extra_schema)
72
+ end
73
+ true
74
+ end
75
+
76
+ # Builds a JSON Reference + message like "/path/to/file#/path/to/data".
77
+ def map_schema_errors(file, errors)
78
+ errors.map { |m| "#{file}#{m}" }
79
+ end
80
+
81
+ def parse(file)
82
+ if !(schema_data = read_file(file))
83
+ return nil
84
+ end
85
+
86
+ parser = JsonSchema::Parser.new
87
+ if !(schema = parser.parse(schema_data))
88
+ @errors = map_schema_errors(file, parser.errors)
89
+ return nil
90
+ end
91
+
92
+ expander = JsonSchema::ReferenceExpander.new
93
+ if !expander.expand(schema, store: @store)
94
+ @errors = map_schema_errors(file, expander.errors)
95
+ return nil
96
+ end
97
+
98
+ schema
99
+ end
100
+
101
+ def read_file(file)
102
+ contents = File.read(file)
103
+
104
+ # Perform an empty check because boath YAML and JSON's load will return
105
+ # `nil` in the case of an empty file, which will otherwise produce
106
+ # confusing results.
107
+ if contents.empty?
108
+ @errors = ["#{file}: File is empty."]
109
+ nil
110
+ else
111
+ if File.extname(file) == ".yaml"
112
+ YAML.load(contents)
113
+ else
114
+ JSON.load(contents)
115
+ end
116
+ end
117
+ rescue Errno::ENOENT
118
+ @errors = ["#{file}: No such file or directory."]
119
+ nil
120
+ rescue JSON::ParserError
121
+ # Ruby's parsing exceptions aren't too helpful, just point user to
122
+ # a better tool
123
+ @errors = ["#{file}: Invalid JSON. Try to validate using `jsonlint`."]
124
+ nil
125
+ rescue Psych::SyntaxError
126
+ @errors = ["#{file}: Invalid YAML."]
127
+ nil
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,7 @@
1
+ require_relative "json_pointer/evaluator"
2
+
3
+ module JsonPointer
4
+ def self.evaluate(data, path)
5
+ Evaluator.new(data).evaluate(path)
6
+ end
7
+ end
@@ -0,0 +1,80 @@
1
+ module JsonPointer
2
+ # Evaluates a JSON pointer within a JSON document.
3
+ #
4
+ # Note that this class is designed to evaluate references across a plain JSON
5
+ # data object _or_ an instance of `JsonSchema::Schema`, so the constructor's
6
+ # `data` argument can be of either type.
7
+ class Evaluator
8
+ def initialize(data)
9
+ @data = data
10
+ end
11
+
12
+ def evaluate(original_path)
13
+ path = original_path
14
+
15
+ # the leading # can either be included or not
16
+ path = path[1..-1] if path[0] == "#"
17
+
18
+ # special case on "" or presumably "#"
19
+ if path.empty?
20
+ return @data
21
+ end
22
+
23
+ if path[0] != "/"
24
+ raise ArgumentError, %{Path must begin with a leading "/": #{original_path}.}
25
+ end
26
+
27
+ path_parts = split(path)
28
+ evaluate_segment(@data, path_parts)
29
+ end
30
+
31
+ private
32
+
33
+ def evaluate_segment(data, path_parts)
34
+ if path_parts.empty?
35
+ data
36
+ elsif data == nil
37
+ # spec doesn't define how to handle this, so we'll return `nil`
38
+ nil
39
+ else
40
+ key = transform_key(path_parts.shift)
41
+ if data.is_a?(Array)
42
+ unless key =~ /^\d+$/
43
+ raise ArgumentError, %{Key operating on an array must be a digit or "-": #{key}.}
44
+ end
45
+ evaluate_segment(data[key.to_i], path_parts)
46
+ else
47
+ evaluate_segment(data[key], path_parts)
48
+ end
49
+ end
50
+ end
51
+
52
+ # custom split method to account for blank segments
53
+ def split(path)
54
+ parts = []
55
+ last_index = 0
56
+ while index = path.index("/", last_index)
57
+ if index == last_index
58
+ parts << ""
59
+ else
60
+ parts << path[last_index...index]
61
+ end
62
+ last_index = index + 1
63
+ end
64
+ # and also get that last segment
65
+ parts << path[last_index..-1]
66
+ # it should begin with a blank segment from the leading "/"; kill that
67
+ parts.shift
68
+ parts
69
+ end
70
+
71
+ def transform_key(key)
72
+ # ~ has special meaning to JSON pointer to allow keys containing "/", so
73
+ # perform some transformations first as defined by the spec
74
+ # first as defined by the spec
75
+ key = key.gsub('~1', '/')
76
+ key = key.gsub('~0', '~')
77
+ key
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,58 @@
1
+ require "uri"
2
+ require_relative "json_pointer"
3
+
4
+ module JsonReference
5
+ def self.reference(ref)
6
+ Reference.new(ref)
7
+ end
8
+
9
+ class Reference
10
+ include Comparable
11
+
12
+ attr_accessor :pointer
13
+ attr_accessor :uri
14
+
15
+ def initialize(ref)
16
+ # Note that the #to_s of `nil` is an empty string.
17
+ @uri = nil
18
+
19
+ # given a simple fragment without '#', resolve as a JSON Pointer only as
20
+ # per spec
21
+ if ref.include?("#")
22
+ uri, @pointer = ref.split('#')
23
+ if uri && !uri.empty?
24
+ @uri = URI.parse(uri)
25
+ end
26
+ @pointer ||= ""
27
+ else
28
+ @pointer = ref
29
+ end
30
+
31
+ # normalize pointers by prepending "#" and stripping trailing "/"
32
+ @pointer = "#" + @pointer
33
+ @pointer = @pointer.chomp("/")
34
+ end
35
+
36
+ def <=>(other)
37
+ to_s <=> other.to_s
38
+ end
39
+
40
+ def inspect
41
+ "\#<JsonReference::Reference #{to_s}>"
42
+ end
43
+
44
+ # Given the document addressed by #uri, resolves the JSON Pointer part of
45
+ # the reference.
46
+ def resolve_pointer(data)
47
+ JsonPointer.evaluate(data, @pointer)
48
+ end
49
+
50
+ def to_s
51
+ if @uri
52
+ "#{@uri.to_s}#{@pointer}"
53
+ else
54
+ @pointer
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,31 @@
1
+ require_relative "json_schema/attributes"
2
+ require_relative "json_schema/configuration"
3
+ require_relative "json_schema/document_store"
4
+ require_relative "json_schema/error"
5
+ require_relative "json_schema/parser"
6
+ require_relative "json_schema/reference_expander"
7
+ require_relative "json_schema/schema"
8
+ require_relative "json_schema/validator"
9
+
10
+ module JsonSchema
11
+ def self.configure
12
+ yield configuration
13
+ end
14
+
15
+ def self.configuration
16
+ @configuration ||= Configuration.new
17
+ end
18
+
19
+ def self.parse(data)
20
+ parser = Parser.new
21
+ if schema = parser.parse(data)
22
+ [schema, nil]
23
+ else
24
+ [nil, parser.errors]
25
+ end
26
+ end
27
+
28
+ def self.parse!(data)
29
+ Parser.new.parse!(data)
30
+ end
31
+ end