json-ld-validate 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 94bf8b975edaeefd4bb0226ca8994c7ff06a1a45339567ddba1fca5bccd6eea0
4
- data.tar.gz: 23292145af308aeeb80585e061c8576517bdaa505337fac5a185a3d4e22118de
3
+ metadata.gz: 24f7e49ba2ad97d5ec2c2746ac541e17167eea91a9c618a364e3c866e979500a
4
+ data.tar.gz: 31a0660a604c9713e91648086fe6945078df30cba88da859eb00a579b6fc6589
5
5
  SHA512:
6
- metadata.gz: ae77e588fc9a057f83e92d3697bee755fadeb8f48f5b8fc3847f74aad10393699f46c45463364ad7ef5ad4eafc459cb8896d9e2fb5624a48a91eb8a7974b95b3
7
- data.tar.gz: 32465d54a3d390ca5d2efe06d1d24711edc9ed47fa891ae033f4747bbb23e33d897d13cf68510d8357824ad0e0987dfde3ebba235285846ceba0ab4b24f0e642
6
+ metadata.gz: 006dc0932bce4be731d5078afbf4d98bee88b2b63af946bbfd3e3614cc956baa09e163810b23a1cb16aecbf5aeedb6d405c82354f3e7949a02d080131c8b7c9f
7
+ data.tar.gz: 5ceaccc40e461010b5037ebf713f6df68f7d9a3028a3c749db7bd979a23ad9edcd434eb1f12ea9b691882b2c0a6e2c30ed2b1829dfc534baa2e5a1e4900194be
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json/ld'
4
-
5
3
  module JsonldValidate
6
4
  module Matchers
7
5
  class BeValidJsonLd
@@ -1,8 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json/ld'
4
+ require_relative 'xsd_type_validator'
5
+
3
6
  module JsonldValidate
4
7
  class Validator
5
- attr_accessor :content, :errors
8
+ include XsdTypeValidator
9
+
10
+ attr_reader :content, :errors
6
11
 
7
12
  def initialize(content)
8
13
  @content = content
@@ -10,30 +15,119 @@ module JsonldValidate
10
15
  end
11
16
 
12
17
  def valid?
18
+ @errors = []
13
19
  return false if content.nil?
14
20
  return true if content.empty?
15
21
 
16
- doc
17
- return false unless errors.empty?
18
-
19
- validates_no_unknown_field
20
-
22
+ validate_document
21
23
  errors.empty?
22
24
  end
23
25
 
24
26
  private
25
27
 
26
- def doc
27
- @doc ||= JSON::LD::API.compact(JSON::LD::API.expand(content), content['@context'])
28
+ attr_reader :doc, :expanded_doc
29
+
30
+ def validate_document
31
+ if content.is_a?(Array)
32
+ validate_array_items
33
+ return
34
+ end
35
+
36
+ return if hash_missing_context? && (@errors << 'missing @context')
37
+
38
+ build_expanded_doc
39
+ build_doc
40
+ return unless errors.empty?
41
+
42
+ validate_typed_values
43
+ find_unknown_fields(content, doc, [])
44
+ end
45
+
46
+ def validate_array_items
47
+ content.each_with_index do |item, i|
48
+ item_validator = self.class.new(item)
49
+ next if item_validator.valid?
50
+
51
+ if item_validator.errors.empty?
52
+ @errors << "#{i}: invalid document"
53
+ else
54
+ item_validator.errors.each { |e| @errors << "#{i}.#{e}" }
55
+ end
56
+ end
57
+ end
58
+
59
+ def hash_missing_context?
60
+ content.is_a?(Hash) && (!content.key?('@context') || content['@context'].nil?)
61
+ end
62
+
63
+ def build_doc
64
+ return if expanded_doc.nil?
65
+
66
+ @doc = JSON::LD::API.compact(expanded_doc, content['@context'])
67
+ end
68
+
69
+ def build_expanded_doc
70
+ @expanded_doc = clean_expanded_doc(JSON::LD::API.expand(content))
28
71
  rescue JSON::LD::JsonLdError::LoadingDocumentFailed
29
72
  @errors << 'failed loading document'
73
+ rescue JSON::LD::JsonLdError => e
74
+ @errors << e.to_s
75
+ end
76
+
77
+ def find_unknown_fields(original, compacted, path)
78
+ return unless original.is_a?(Hash)
79
+ return report_all_fields(original, path) unless compacted.is_a?(Hash)
80
+
81
+ report_unknown_keys(original, compacted, path)
82
+ recurse_known_keys(original, compacted, path)
30
83
  end
31
84
 
32
- def validates_no_unknown_field
33
- return if doc.keys.length == content.keys.length
85
+ def report_all_fields(original, path)
86
+ original.each_key do |key|
87
+ next if key.start_with?('@')
88
+
89
+ errors << "found unknown field: #{(path + [key]).join('.')}"
90
+ end
91
+ end
92
+
93
+ def report_unknown_keys(original, compacted, path)
94
+ (original.keys - compacted.keys).each do |field|
95
+ errors << "found unknown field: #{(path + [field]).join('.')}"
96
+ end
97
+ end
98
+
99
+ def recurse_known_keys(original, compacted, path)
100
+ (original.keys & compacted.keys).each do |key|
101
+ next if key.start_with?('@')
102
+
103
+ recurse_field(original[key], compacted[key], path + [key])
104
+ end
105
+ end
106
+
107
+ def recurse_field(orig_val, comp_val, path)
108
+ if orig_val.is_a?(Array)
109
+ comp_val = [] unless comp_val.is_a?(Array)
110
+ orig_val.each_with_index do |item, i|
111
+ find_unknown_fields(item, comp_val[i], path + [i.to_s])
112
+ end
113
+ elsif orig_val.is_a?(Hash)
114
+ find_unknown_fields(orig_val, comp_val, path)
115
+ elsif orig_val != comp_val
116
+ @errors << "invalid value at #{path.join('.')}: #{orig_val.inspect}"
117
+ end
118
+ end
34
119
 
35
- unknown_fields = content.keys - doc.keys
36
- errors << "found unknown fields: #{unknown_fields.join(', ')}"
120
+ def clean_expanded_doc(obj)
121
+ case obj
122
+ when Array
123
+ obj.map { |item| clean_expanded_doc(item) }
124
+ when Hash
125
+ obj.each_with_object({}) do |(k, v), h|
126
+ h[k] = clean_expanded_doc(v) unless k.start_with?('_:')
127
+ end
128
+ else
129
+ obj
130
+ end
37
131
  end
38
132
  end
39
133
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JsonldValidate
4
- VERSION = '0.0.2'
4
+ VERSION = '0.1.0'
5
5
  end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JsonldValidate
4
+ module XsdTypeValidator
5
+ XSD = 'http://www.w3.org/2001/XMLSchema#'
6
+
7
+ INTEGER_TYPES = %w[
8
+ integer int long short byte
9
+ unsignedInt unsignedLong unsignedShort unsignedByte
10
+ nonNegativeInteger positiveInteger nonPositiveInteger negativeInteger
11
+ ].to_set { |t| "#{XSD}#{t}" }.freeze
12
+
13
+ DECIMAL_TYPES = %w[decimal double float].to_set { |t| "#{XSD}#{t}" }.freeze
14
+
15
+ BOOLEAN_TYPE = "#{XSD}boolean".freeze
16
+
17
+ def validate_typed_values
18
+ return unless expanded_doc
19
+
20
+ iri_to_term, vocab = build_iri_map
21
+ expanded_doc.each { |node| check_node_types(node, iri_to_term, vocab, []) }
22
+ end
23
+
24
+ def build_iri_map
25
+ ctx = JSON::LD::Context.parse(content['@context'])
26
+ map = ctx.term_definitions.each_with_object({}) do |(term, defn), h|
27
+ h[defn.id] = term if defn.id
28
+ end
29
+ [map, ctx.vocab]
30
+ rescue StandardError
31
+ [{}, nil]
32
+ end
33
+
34
+ def check_node_types(node, iri_to_term, vocab, path)
35
+ return unless node.is_a?(Hash)
36
+
37
+ node.each do |iri, values|
38
+ next if iri.start_with?('@')
39
+
40
+ term = compact_term(iri, iri_to_term, vocab)
41
+ Array(values).each_with_index do |value, i|
42
+ child_path = Array(values).length > 1 ? path + [term, i.to_s] : path + [term]
43
+ check_typed_value(value, child_path, iri_to_term, vocab)
44
+ end
45
+ end
46
+ end
47
+
48
+ def check_typed_value(value, path, iri_to_term, vocab)
49
+ return unless value.is_a?(Hash)
50
+
51
+ if value.key?('@value') && value.key?('@type')
52
+ report_type_mismatch(value['@value'], value['@type'], path)
53
+ else
54
+ check_node_types(value, iri_to_term, vocab, path)
55
+ end
56
+ end
57
+
58
+ def report_type_mismatch(val, type_iri, path)
59
+ return if valid_xsd_value?(val, type_iri)
60
+
61
+ @errors << "invalid type for #{path.join('.')}: #{val.inspect}"
62
+ end
63
+
64
+ def valid_xsd_value?(val, type_iri)
65
+ if INTEGER_TYPES.include?(type_iri)
66
+ val.is_a?(Integer)
67
+ elsif DECIMAL_TYPES.include?(type_iri)
68
+ val.is_a?(Numeric)
69
+ elsif type_iri == BOOLEAN_TYPE
70
+ [true, false].include?(val)
71
+ else
72
+ true
73
+ end
74
+ end
75
+
76
+ def compact_term(iri, iri_to_term, vocab)
77
+ return iri_to_term[iri] if iri_to_term.key?(iri)
78
+ return iri.delete_prefix(vocab) if vocab && iri.start_with?(vocab)
79
+
80
+ iri
81
+ end
82
+ end
83
+ end
@@ -4,5 +4,4 @@ module JsonldValidate
4
4
  end
5
5
 
6
6
  require 'jsonld_validate/validator'
7
- require 'jsonld_validate/matchers'
8
7
  require 'jsonld_validate/version'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json-ld-validate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Damien MATHIEU
@@ -23,20 +23,6 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '3.0'
26
- - !ruby/object:Gem::Dependency
27
- name: rspec-core
28
- requirement: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - "~>"
31
- - !ruby/object:Gem::Version
32
- version: '3.0'
33
- type: :runtime
34
- prerelease: false
35
- version_requirements: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '3.0'
40
26
  description: JSON-LD documents validation
41
27
  email:
42
28
  - 42@dmathieu.com
@@ -51,6 +37,7 @@ files:
51
37
  - lib/jsonld_validate/matchers/be_valid_json_ld.rb
52
38
  - lib/jsonld_validate/validator.rb
53
39
  - lib/jsonld_validate/version.rb
40
+ - lib/jsonld_validate/xsd_type_validator.rb
54
41
  homepage: https://codeberg.org/dmathieu/json-ld-validate
55
42
  licenses:
56
43
  - AGPL
@@ -70,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
57
  - !ruby/object:Gem::Version
71
58
  version: '0'
72
59
  requirements: []
73
- rubygems_version: 4.0.3
60
+ rubygems_version: 4.0.6
74
61
  specification_version: 4
75
62
  summary: JSON-LD documents validation
76
63
  test_files: []