halidator 0.3.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/halidate +15 -2
- data/lib/halidate/hal.json +100 -0
- data/lib/halidate/json_schema.rb +18 -0
- data/lib/halidate/pure_ruby.rb +105 -0
- data/lib/halidator.rb +18 -110
- metadata +38 -3
data/bin/halidate
CHANGED
@@ -1,10 +1,23 @@
|
|
1
1
|
#! /usr/bin/env ruby
|
2
|
-
|
2
|
+
require 'optparse'
|
3
3
|
require_relative '../lib/halidator'
|
4
4
|
|
5
|
+
options = {}
|
6
|
+
OptionParser.new do |o|
|
7
|
+
o.on('-j', '--json-schema', "Validate using the HAL JSON schema") { |b| options[:json_schema] = true }
|
8
|
+
o.on('-h', '--help', 'Output this help and exit') { puts o; exit }
|
9
|
+
o.on('-d', '--debug', "Output debugging info") {$DEBUG = true}
|
10
|
+
o.parse!
|
11
|
+
end
|
12
|
+
|
5
13
|
input = ARGF.read
|
6
14
|
|
7
|
-
|
15
|
+
if options[:json_schema]
|
16
|
+
hal = Halidator.new(input, :json_schema)
|
17
|
+
else
|
18
|
+
hal = Halidator.new(input)
|
19
|
+
end
|
20
|
+
|
8
21
|
res = hal.valid?
|
9
22
|
if true == res
|
10
23
|
$stdout.puts "true"
|
@@ -0,0 +1,100 @@
|
|
1
|
+
{
|
2
|
+
"id": "resource",
|
3
|
+
"type": "object",
|
4
|
+
"properties": {
|
5
|
+
"_links": {
|
6
|
+
"type": "object",
|
7
|
+
"title": "Hyperlink",
|
8
|
+
"description": "Represents a hyperlink from the containing resource to a URI.",
|
9
|
+
"additionalProperties": {
|
10
|
+
"type": [
|
11
|
+
{
|
12
|
+
"id": "link",
|
13
|
+
"type": "object",
|
14
|
+
"properties": {
|
15
|
+
"href": {
|
16
|
+
"type": "string",
|
17
|
+
"title": "URI of the target resource",
|
18
|
+
"description": "Either a URI [RFC3986] or URI Template [RFC6570] of the target resource."
|
19
|
+
},
|
20
|
+
"templated": {
|
21
|
+
"type": "boolean",
|
22
|
+
"optional": true,
|
23
|
+
"default": false,
|
24
|
+
"title": "URI Template",
|
25
|
+
"description": "Is true when the link object's href property is a URI Template. Defaults to false."
|
26
|
+
},
|
27
|
+
"type": {
|
28
|
+
"type": "string",
|
29
|
+
"pattern": "^(application|audio|example|image|message|model|multipart|text|video)\\/[a-zA-Z0-9!#\\$&\\.\\+-\\^_]{1,127}$",
|
30
|
+
"optional": true,
|
31
|
+
"title": "Media type indication of the target resource",
|
32
|
+
"description": "When present, used as a hint to indicate the media type expected when dereferencing the target resource."
|
33
|
+
},
|
34
|
+
"name": {
|
35
|
+
"type": "string",
|
36
|
+
"optional": true,
|
37
|
+
"title": "Secondary key",
|
38
|
+
"description": "When present, may be used as a secondary key for selecting link objects that contain the same relation type."
|
39
|
+
},
|
40
|
+
"profile": {
|
41
|
+
"type": "string",
|
42
|
+
"format": "uri",
|
43
|
+
"optional": true,
|
44
|
+
"title": "Additional semantics of the target resource",
|
45
|
+
"description": "A URI that, when dereferenced, results in a profile to allow clients to learn about additional semantics (constraints, conventions, extensions) that are associated with the target resource representation, in addition to those defined by the HAL media type and relations."
|
46
|
+
},
|
47
|
+
"title": {
|
48
|
+
"type": "string",
|
49
|
+
"optional": true,
|
50
|
+
"title": "Human-readable identifier",
|
51
|
+
"description": "When present, is used to label the destination of a link such that it can be used as a human-readable identifier (e.g. a menu entry) in the language indicated by the Content-Language header (if present)."
|
52
|
+
},
|
53
|
+
"hreflang": {
|
54
|
+
"type": "string",
|
55
|
+
"pattern": "^([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-([a-zA-Z0-9]{5,8}|[0-9][a-zA-Z0-9]{3}))*([0-9A-WY-Za-wy-z](-[a-zA-Z0-9]{2,8}){1,})*(x-[a-zA-Z0-9]{2,8})?)|(x-[a-zA-Z0-9]{2,8})|(en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE)|(art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang)$",
|
56
|
+
"optional": true,
|
57
|
+
"title": "Language indication of the target resource [RFC5988]",
|
58
|
+
"description": "When present, is a hint in RFC5646 format indicating what the language of the result of dereferencing the link should be. Note that this is only a hint; for example, it does not override the Content-Language header of a HTTP response obtained by actually following the link."
|
59
|
+
}
|
60
|
+
},
|
61
|
+
"additionalProperties": false
|
62
|
+
},
|
63
|
+
{
|
64
|
+
"type": "array",
|
65
|
+
"items": [
|
66
|
+
{
|
67
|
+
"$ref": "link"
|
68
|
+
}
|
69
|
+
],
|
70
|
+
"uniqueItems": true,
|
71
|
+
"additionalProperties": false
|
72
|
+
}
|
73
|
+
]
|
74
|
+
},
|
75
|
+
"optional": true
|
76
|
+
},
|
77
|
+
"_embedded": {
|
78
|
+
"type": "object",
|
79
|
+
"additionalProperties": {
|
80
|
+
"type": [
|
81
|
+
{
|
82
|
+
"$ref": "resource"
|
83
|
+
},
|
84
|
+
{
|
85
|
+
"type": "array",
|
86
|
+
|
87
|
+
"items": [
|
88
|
+
{
|
89
|
+
"$ref": "resource"
|
90
|
+
}
|
91
|
+
],
|
92
|
+
"uniqueItems": true,
|
93
|
+
"additionalProperties": false
|
94
|
+
}
|
95
|
+
]
|
96
|
+
},
|
97
|
+
"optional": true
|
98
|
+
}
|
99
|
+
}
|
100
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'json-schema'
|
2
|
+
|
3
|
+
module Halidate
|
4
|
+
module JsonSchema
|
5
|
+
def validate_json_as_hal
|
6
|
+
@errors = JSON::Validator.fully_validate(@json, schema)
|
7
|
+
@errors.empty?
|
8
|
+
end
|
9
|
+
|
10
|
+
def hal_json_schema_file
|
11
|
+
File.dirname(__FILE__) + "/hal.json"
|
12
|
+
end
|
13
|
+
|
14
|
+
def schema
|
15
|
+
@schema ||= File.open(hal_json_schema_file){|f| JSON.parse(f.read)}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Halidate
|
2
|
+
module PureRuby
|
3
|
+
def validate_json_as_hal
|
4
|
+
meets_minimal_JSON_representation? && links_all_valid? && embedded_valid?
|
5
|
+
end
|
6
|
+
|
7
|
+
def links_all_valid?
|
8
|
+
_links.all? do |k, v|
|
9
|
+
debug "\n\n", k, v
|
10
|
+
|
11
|
+
case v
|
12
|
+
when Array # is an array of links
|
13
|
+
v.all?{|x| link_valid?(x)}
|
14
|
+
else
|
15
|
+
link_valid?(v)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def link_valid?(link)
|
21
|
+
debug " #{link}"
|
22
|
+
|
23
|
+
unless link['href']
|
24
|
+
errors << "no href in #{link}"
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
unless template_valid?(link)
|
28
|
+
errors << "invalid template for #{link}"
|
29
|
+
return false
|
30
|
+
end
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def template_valid?(link)
|
35
|
+
return true unless link['templated'] == true
|
36
|
+
|
37
|
+
pairs = 0
|
38
|
+
res = link['href'].each_char.all? do |c|
|
39
|
+
if '{' == c
|
40
|
+
pairs += 1
|
41
|
+
pairs == 1
|
42
|
+
elsif '}' == c
|
43
|
+
pairs -= 1
|
44
|
+
pairs == 0
|
45
|
+
else
|
46
|
+
true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
res && (pairs == 0) && link['href'].include?('{')
|
50
|
+
end
|
51
|
+
|
52
|
+
def embedded_valid?
|
53
|
+
return true if _embedded.nil?
|
54
|
+
|
55
|
+
_embedded.all? do |resource_type, resource|
|
56
|
+
case resource
|
57
|
+
when Array
|
58
|
+
resource.all?{|x| Halidator.new(x).valid?}
|
59
|
+
else
|
60
|
+
Halidator.new(resource).valid?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def meets_minimal_JSON_representation?
|
66
|
+
has_links && links_has_self && self_has_href
|
67
|
+
end
|
68
|
+
|
69
|
+
def _embedded
|
70
|
+
@json['_embedded']
|
71
|
+
end
|
72
|
+
|
73
|
+
def _links
|
74
|
+
@json['_links']
|
75
|
+
end
|
76
|
+
|
77
|
+
def has_links
|
78
|
+
if _links
|
79
|
+
true
|
80
|
+
else
|
81
|
+
errors << '_links does not exist'
|
82
|
+
false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def links_has_self
|
87
|
+
if _links['self']
|
88
|
+
true
|
89
|
+
else
|
90
|
+
errors << "no self in #{_links.inspect}"
|
91
|
+
false
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def self_has_href
|
96
|
+
if _links['self']['href']
|
97
|
+
true
|
98
|
+
else
|
99
|
+
errors << "no href in #{_links['self']}"
|
100
|
+
false
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
data/lib/halidator.rb
CHANGED
@@ -1,137 +1,45 @@
|
|
1
1
|
require 'json'
|
2
|
+
require_relative './halidate/pure_ruby'
|
3
|
+
require_relative './halidate/json_schema'
|
2
4
|
|
3
5
|
class Halidator
|
4
6
|
attr_accessor :errors
|
5
|
-
def initialize(hal)
|
7
|
+
def initialize(hal, engine = :pure_ruby)
|
6
8
|
case hal
|
7
9
|
when String
|
8
10
|
@json_string = hal
|
11
|
+
parse_json
|
9
12
|
else
|
10
13
|
@json = hal
|
11
14
|
end
|
12
|
-
|
13
15
|
@errors = []
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
result
|
16
|
+
if engine == :json_schema
|
17
|
+
extend Halidate::JsonSchema
|
18
|
+
else
|
19
|
+
extend Halidate::PureRuby
|
20
|
+
end
|
20
21
|
end
|
21
22
|
|
22
23
|
def parse_json
|
23
|
-
return true if @json
|
24
|
-
|
25
24
|
@json = JSON.parse(@json_string)
|
26
25
|
end
|
27
26
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
def links_all_valid?
|
33
|
-
_links.all? do |k, v|
|
34
|
-
if $DEBUG
|
35
|
-
puts "\n\n", k, v
|
36
|
-
end
|
37
|
-
case v
|
38
|
-
when Array # is an array of links
|
39
|
-
v.all?{|x| link_valid?(x)}
|
40
|
-
else
|
41
|
-
link_valid?(v)
|
42
|
-
end
|
43
|
-
end
|
27
|
+
def valid?
|
28
|
+
result = validate_json_as_hal
|
29
|
+
show_errors
|
30
|
+
result
|
44
31
|
end
|
45
32
|
|
46
|
-
def
|
33
|
+
def debug(*str)
|
47
34
|
if $DEBUG
|
48
|
-
puts
|
35
|
+
$stderr.puts str
|
49
36
|
end
|
50
|
-
unless link['href']
|
51
|
-
errors << "no href in #{link}"
|
52
|
-
return false
|
53
|
-
end
|
54
|
-
unless template_valid?(link)
|
55
|
-
errors << "invalid template for #{link}"
|
56
|
-
return false
|
57
|
-
end
|
58
|
-
true
|
59
|
-
end
|
60
|
-
|
61
|
-
def template_valid?(link)
|
62
|
-
return true unless link['templated'] == true
|
63
|
-
|
64
|
-
pairs = 0
|
65
|
-
res = link['href'].each_char.all? do |c|
|
66
|
-
if '{' == c
|
67
|
-
pairs += 1
|
68
|
-
pairs == 1
|
69
|
-
elsif '}' == c
|
70
|
-
pairs -= 1
|
71
|
-
pairs == 0
|
72
|
-
else
|
73
|
-
true
|
74
|
-
end
|
75
|
-
end
|
76
|
-
res && (pairs == 0) && link['href'].include?('{')
|
77
|
-
end
|
78
|
-
|
79
|
-
def embedded_valid?
|
80
|
-
return true if _embedded.nil?
|
81
|
-
|
82
|
-
_embedded.all? do |resource_type, resource|
|
83
|
-
case resource
|
84
|
-
when Array
|
85
|
-
resource.all?{|x| Halidator.new(x).valid?}
|
86
|
-
else
|
87
|
-
Halidator.new(resource).valid?
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def meets_minimal_JSON_representation?
|
93
|
-
has_links && links_has_self && self_has_href
|
94
37
|
end
|
95
38
|
|
96
39
|
def show_errors
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
end
|
101
|
-
|
102
|
-
def _embedded
|
103
|
-
@json['_embedded']
|
104
|
-
end
|
105
|
-
|
106
|
-
def _links
|
107
|
-
@json['_links']
|
108
|
-
end
|
109
|
-
|
110
|
-
def has_links
|
111
|
-
if _links
|
112
|
-
true
|
113
|
-
else
|
114
|
-
errors << '_links does not exist'
|
115
|
-
false
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def links_has_self
|
120
|
-
if _links['self']
|
121
|
-
true
|
122
|
-
else
|
123
|
-
errors << "no self in #{_links.inspect}"
|
124
|
-
false
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def self_has_href
|
129
|
-
if _links['self']['href']
|
130
|
-
true
|
131
|
-
else
|
132
|
-
errors << "no href in #{_links['self']}"
|
133
|
-
false
|
134
|
-
end
|
40
|
+
debug ["\nERRORS-VVVVVVVVVVVVVVVVVV",
|
41
|
+
*errors,
|
42
|
+
"ERRORS-^^^^^^^^^^^^^^^^^^"]
|
135
43
|
end
|
136
44
|
|
137
45
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: halidator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,40 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-10-
|
13
|
-
dependencies:
|
12
|
+
date: 2012-10-25 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: json
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.6.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.6.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: json-schema
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.0.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.0.0
|
14
46
|
description: A small library to validate hal+json
|
15
47
|
email: larrick@gmail.com
|
16
48
|
executables:
|
@@ -19,6 +51,9 @@ extensions: []
|
|
19
51
|
extra_rdoc_files: []
|
20
52
|
files:
|
21
53
|
- ./lib/halidator.rb
|
54
|
+
- ./lib/halidate/hal.json
|
55
|
+
- ./lib/halidate/pure_ruby.rb
|
56
|
+
- ./lib/halidate/json_schema.rb
|
22
57
|
- bin/halidate
|
23
58
|
homepage: https://github.com/deathbob/halidator
|
24
59
|
licenses: []
|