lurker 0.6.2 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +1 -1
- data/Gemfile +1 -0
- data/features/dereferencing_through_inlining.feature +102 -0
- data/features/partials.feature +3 -1
- data/features/step_definitions/additional_cli_steps.rb +19 -0
- data/features/support/files_helper.rb +7 -0
- data/lib/lurker/endpoint.rb +85 -50
- data/lib/lurker/json/concerns/validatable.rb +47 -0
- data/lib/lurker/json/orderer.rb +19 -0
- data/lib/lurker/json/parser/expertise.rb +30 -0
- data/lib/lurker/json/parser/plain_strategy.rb +39 -0
- data/lib/lurker/json/parser/typed_strategy.rb +71 -0
- data/lib/lurker/json/parser.rb +73 -0
- data/lib/lurker/json/reader.rb +28 -0
- data/lib/lurker/json/schema/attribute.rb +115 -0
- data/lib/lurker/json/schema/extensions.rb +19 -0
- data/lib/lurker/json/schema/list.rb +51 -0
- data/lib/lurker/json/schema/object.rb +67 -0
- data/lib/lurker/json/schema/reference.rb +34 -0
- data/lib/lurker/{endpoint → json/schema}/response_codes.rb +13 -16
- data/lib/lurker/json/schema/tuple/all_of.rb +17 -0
- data/lib/lurker/json/schema/tuple/any_of.rb +17 -0
- data/lib/lurker/json/schema/tuple/one_of.rb +17 -0
- data/lib/lurker/json/schema/tuple.rb +38 -0
- data/lib/lurker/json/schema.rb +122 -0
- data/lib/lurker/json/writter.rb +58 -0
- data/lib/lurker/presenters/endpoint_presenter.rb +2 -2
- data/lib/lurker/presenters/schema_presenter.rb +4 -1
- data/lib/lurker/presenters/service_presenter.rb +8 -2
- data/lib/lurker/service.rb +4 -4
- data/lib/lurker/version.rb +1 -1
- data/lib/lurker.rb +19 -8
- data/spec/lurker/json/list_spec.rb +101 -0
- data/spec/lurker/json/schema_spec.rb +126 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/matchers/json_attribute.rb +27 -0
- data/spec/support/matchers/json_object.rb +33 -0
- data/templates/generate_stuff.rb +19 -10
- data.tar.gz.sig +2 -4
- metadata +33 -9
- metadata.gz.sig +0 -0
- data/lib/lurker/endpoint/http_parameters.rb +0 -77
- data/lib/lurker/schema.rb +0 -89
- data/lib/lurker/schema_modifier/array.rb +0 -28
- data/lib/lurker/schema_modifier/atom.rb +0 -97
- data/lib/lurker/schema_modifier/hash.rb +0 -30
- data/lib/lurker/schema_modifier.rb +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c59974fdb662a2fbadcb01539d0fe9a1b919da2c
|
4
|
+
data.tar.gz: 476c5cad93b78d96f397fae62ed32ae19ad30c21
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2a7af02fa6d092a639b79f5f903c373c33b40cc3b8ad621aa69eb6ebdd056998b66cfdb57d93be644dff1c0dd2e3cf554aec85a30fd61bc65038b6060a1ace9
|
7
|
+
data.tar.gz: d2afae3c32e2adce210fb74144e0078ea8db8241b7aebeb0afeb474ec67dcf16083ad8153fc405fde8924af7d4986602575078d836be4c5b2c1e5d7a20f28fd3
|
checksums.yaml.gz.sig
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
�ƴs���:�vsT�\�k���Ȑj<Dk�9�s:d�������L��{o�z���W�_�W���aA�za�G��q#Mz^)8K㊥����r_~����%��Մ��H���'�0���51rÝRy�NW��ݧm�FZ��#��a� I9�g��b�:Kj��,i|�g\C�}��F��Ѥs��A�;��*���P�iM7�5aܶ��/ ����U���7A�>/K���Sh�Ņm; 5;��P�oX�3��
|
data/Gemfile
CHANGED
@@ -0,0 +1,102 @@
|
|
1
|
+
Feature: $ref defererencing through inlining
|
2
|
+
Background:
|
3
|
+
Given a checked file "lurker/api/v3/users/__id-PATCH.json.yml" with:
|
4
|
+
"""yml
|
5
|
+
---
|
6
|
+
description: user updating
|
7
|
+
prefix: users management
|
8
|
+
requestParameters:
|
9
|
+
description: ''
|
10
|
+
type: object
|
11
|
+
additionalProperties: false
|
12
|
+
required: []
|
13
|
+
properties:
|
14
|
+
user:
|
15
|
+
"$ref": "../../../definitions/user_request_parameters.json#/"
|
16
|
+
responseCodes:
|
17
|
+
- status: 200
|
18
|
+
successful: true
|
19
|
+
description: ''
|
20
|
+
responseParameters:
|
21
|
+
"$ref": "../../../definitions/user.json#/"
|
22
|
+
extensions:
|
23
|
+
method: PATCH
|
24
|
+
path_info: "/api/v3/users/1"
|
25
|
+
path_params:
|
26
|
+
id: '1'
|
27
|
+
controller: api/v3/users
|
28
|
+
action: update
|
29
|
+
|
30
|
+
"""
|
31
|
+
And a file named "lurker/definitions/user_request_parameters.json" with:
|
32
|
+
"""json
|
33
|
+
{
|
34
|
+
"properties": {
|
35
|
+
"name": {
|
36
|
+
"type": "string",
|
37
|
+
"example": "Bob"
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
"""
|
42
|
+
And a file named "lurker/definitions/user.json.yml" with:
|
43
|
+
"""yml
|
44
|
+
---
|
45
|
+
properties:
|
46
|
+
id:
|
47
|
+
type: integer
|
48
|
+
example: 1
|
49
|
+
name:
|
50
|
+
type: string
|
51
|
+
example: razum2um
|
52
|
+
surname:
|
53
|
+
type: string
|
54
|
+
example: Unknown
|
55
|
+
"""
|
56
|
+
And a file named "spec/controllers/api/v3/users_controller_spec.rb" with:
|
57
|
+
"""ruby
|
58
|
+
require "spec_helper"
|
59
|
+
|
60
|
+
describe Api::V3::UsersController, :lurker do
|
61
|
+
render_views
|
62
|
+
|
63
|
+
let(:user) do
|
64
|
+
User.where(name: 'razum2um', surname: 'Unknown').first_or_create!
|
65
|
+
end
|
66
|
+
|
67
|
+
it "updates a user surname as string" do
|
68
|
+
patch :update, id: user.id, user: { surname: 'Marley' }
|
69
|
+
expect(response).to be_success
|
70
|
+
end
|
71
|
+
end
|
72
|
+
"""
|
73
|
+
|
74
|
+
Scenario: json schema left $ref keyword as is using "users/update"
|
75
|
+
When I run `bin/rspec spec/controllers/api/v3/users_controller_spec.rb`
|
76
|
+
Then the example should pass
|
77
|
+
Then a file named "lurker/api/v3/users/__id-PATCH.json.yml" should exist
|
78
|
+
Then the checked file "lurker/api/v3/users/__id-PATCH.json.yml" should not change
|
79
|
+
Then a file named "lurker/definitions/user_request_parameters.json" should exist
|
80
|
+
Then the file "lurker/definitions/user_request_parameters.json" should contain exactly:
|
81
|
+
"""json
|
82
|
+
{
|
83
|
+
"description": "",
|
84
|
+
"type": "object",
|
85
|
+
"additionalProperties": false,
|
86
|
+
"required": [
|
87
|
+
|
88
|
+
],
|
89
|
+
"properties": {
|
90
|
+
"name": {
|
91
|
+
"description": "",
|
92
|
+
"type": "string",
|
93
|
+
"example": "Bob"
|
94
|
+
},
|
95
|
+
"surname": {
|
96
|
+
"description": "",
|
97
|
+
"type": "string",
|
98
|
+
"example": "Marley"
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
102
|
+
"""
|
data/features/partials.feature
CHANGED
@@ -20,6 +20,9 @@ Feature: partials
|
|
20
20
|
name:
|
21
21
|
type: string
|
22
22
|
example: razum2um
|
23
|
+
surname:
|
24
|
+
type: string
|
25
|
+
example: Unknown
|
23
26
|
"""
|
24
27
|
And a file named "lurker/definitions/repo.json.yml" with:
|
25
28
|
"""yml
|
@@ -94,4 +97,3 @@ Feature: partials
|
|
94
97
|
create index.html
|
95
98
|
create api/v1/users/__user_id/repos-POST.html
|
96
99
|
"""
|
97
|
-
|
@@ -1,3 +1,10 @@
|
|
1
|
+
Given /^a checked file "([^"]*)" with:$/ do |file_name, file_content|
|
2
|
+
write_file(file_name, file_content)
|
3
|
+
|
4
|
+
@files ||= {}
|
5
|
+
in_current_dir { @files[md5(file_name)] = checksum(file_name) }
|
6
|
+
end
|
7
|
+
|
1
8
|
Given /^an empty directory named "([^"]*)"$/ do |dir_name|
|
2
9
|
FileUtils.rm_rf File.expand_path("../../../tmp/lurker_app/#{dir_name}", __FILE__)
|
3
10
|
create_dir(dir_name)
|
@@ -83,3 +90,15 @@ Then(/^I should see JSON response with "([^"]*)"$/) do |name|
|
|
83
90
|
expect(page).to have_content name
|
84
91
|
end
|
85
92
|
end
|
93
|
+
|
94
|
+
Then /(?:a|the) checked file "([^"]*)" should not change$/ do |file_name|
|
95
|
+
in_current_dir do
|
96
|
+
expect(@files.try(:[], md5(file_name))).to eq checksum(file_name)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
Then /(?:a|the) checked file "([^"]*)" should change$/ do |file_name|
|
101
|
+
in_current_dir do
|
102
|
+
expect(@files.try(:[], md5(file_name))).not_to eq checksum(file_name)
|
103
|
+
end
|
104
|
+
end
|
data/lib/lurker/endpoint.rb
CHANGED
@@ -8,24 +8,43 @@ module Lurker
|
|
8
8
|
class Endpoint
|
9
9
|
include Lurker::Utils
|
10
10
|
|
11
|
+
PREFIX = 'prefix'.freeze
|
12
|
+
ACTION = 'action'.freeze
|
13
|
+
CONTROLLER = 'controller'.freeze
|
14
|
+
EXTENSIONS = 'extensions'.freeze
|
15
|
+
PATH_PARAMS = 'path_params'.freeze
|
16
|
+
QUERY_PARAMS = 'query_params'.freeze
|
17
|
+
DEPRECATED = 'deprecated'.freeze
|
18
|
+
DESCRIPTION = 'description'.freeze
|
19
|
+
RESPONSE_CODES = 'responseCodes'.freeze
|
20
|
+
REQUEST_PARAMETERS = 'requestParameters'.freeze
|
21
|
+
RESPONSE_PARAMETERS = 'responseParameters'.freeze
|
22
|
+
DESCRPTIONS = {
|
23
|
+
'index' => 'listing',
|
24
|
+
'show' => '',
|
25
|
+
'edit' => 'editing',
|
26
|
+
'create' => 'creation',
|
27
|
+
'update' => 'updating',
|
28
|
+
'destroy' => 'descruction'
|
29
|
+
}.freeze
|
30
|
+
|
11
31
|
attr_reader :schema, :service, :endpoint_path, :extensions
|
12
|
-
attr_reader :request_parameters, :response_parameters, :response_codes
|
13
|
-
attr_accessor :errors
|
14
32
|
|
15
33
|
def initialize(endpoint_path, extensions = {}, service = Lurker::Service.default_service)
|
16
34
|
@endpoint_path = endpoint_path
|
17
35
|
@extensions = extensions
|
18
36
|
@service = service
|
19
|
-
@errors = []
|
20
37
|
@persisted = false
|
21
38
|
@schema = File.exist?(endpoint_path) ? load_schema : build_schema
|
22
|
-
|
23
|
-
|
39
|
+
@request_errors = []
|
40
|
+
@response_errors = []
|
24
41
|
end
|
25
42
|
|
26
43
|
def persist!
|
27
|
-
|
28
|
-
|
44
|
+
finalize_schema!
|
45
|
+
|
46
|
+
Lurker::Json::Orderer.reorder(schema) unless persisted?
|
47
|
+
Lurker::Json::Writter.write(schema, endpoint_path)
|
29
48
|
|
30
49
|
@persisted = true
|
31
50
|
end
|
@@ -42,21 +61,30 @@ module Lurker
|
|
42
61
|
end
|
43
62
|
|
44
63
|
def consume_request(params, successful = true)
|
45
|
-
|
46
|
-
|
64
|
+
parameters = stringify_keys(params)
|
65
|
+
|
66
|
+
if persisted?
|
67
|
+
@request_errors = request_parameters.validate(parameters)
|
68
|
+
@request_errors.unshift('Request') unless @request_errors.empty?
|
69
|
+
end
|
70
|
+
|
71
|
+
request_parameters.merge!(parameters) if successful
|
47
72
|
end
|
48
73
|
|
49
74
|
def consume_response(params, status_code, successful = true)
|
75
|
+
parameters = stringify_keys(params)
|
76
|
+
|
50
77
|
if persisted?
|
51
78
|
response_codes.validate!(status_code, successful)
|
52
|
-
|
79
|
+
|
80
|
+
@response_errors = response_parameters.validate(parameters)
|
81
|
+
@response_errors.unshift('Response') unless @response_errors.empty?
|
53
82
|
|
54
83
|
return
|
55
84
|
end
|
56
85
|
|
57
|
-
response_parameters.
|
58
|
-
response_codes.
|
59
|
-
status_code, successful)
|
86
|
+
response_parameters.merge!(parameters) if successful
|
87
|
+
response_codes.merge!(status_code, successful)
|
60
88
|
end
|
61
89
|
|
62
90
|
def verb
|
@@ -65,44 +93,46 @@ module Lurker
|
|
65
93
|
|
66
94
|
def path
|
67
95
|
@path ||= endpoint_path.
|
68
|
-
gsub(service.service_dir,
|
96
|
+
gsub(service.service_dir, '').
|
69
97
|
match(/\/?(.*)[-\/][A-Z]+\.json(\.yml)?(\.erb)?$/)[1]
|
70
98
|
end
|
71
99
|
|
72
100
|
# properties
|
73
101
|
|
74
102
|
def deprecated?
|
75
|
-
@schema[
|
103
|
+
@schema[DEPRECATED]
|
76
104
|
end
|
77
105
|
|
78
106
|
def prefix
|
79
|
-
@schema[
|
107
|
+
@schema[PREFIX]
|
80
108
|
end
|
81
109
|
|
82
110
|
def description
|
83
|
-
@schema[
|
111
|
+
@schema[DESCRIPTION]
|
84
112
|
end
|
85
113
|
|
86
114
|
def url_params
|
87
|
-
(schema
|
115
|
+
(@schema[EXTENSIONS][PATH_PARAMS] || {}).reject { |k, _| %w(action controller format).include? k }
|
88
116
|
end
|
89
117
|
|
90
118
|
def query_params
|
91
|
-
(schema
|
119
|
+
(@schema[EXTENSIONS][QUERY_PARAMS] || {})
|
92
120
|
end
|
93
121
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
@response_codes = ResponseCodes.new(schema)
|
122
|
+
def request_parameters
|
123
|
+
@schema[REQUEST_PARAMETERS]
|
124
|
+
end
|
98
125
|
|
99
|
-
|
100
|
-
|
126
|
+
def response_parameters
|
127
|
+
@schema[RESPONSE_PARAMETERS]
|
128
|
+
end
|
101
129
|
|
102
|
-
|
103
|
-
|
130
|
+
def response_codes
|
131
|
+
@schema[RESPONSE_CODES]
|
104
132
|
end
|
105
133
|
|
134
|
+
protected
|
135
|
+
|
106
136
|
def persisted?
|
107
137
|
!!@persisted
|
108
138
|
end
|
@@ -110,43 +140,48 @@ module Lurker
|
|
110
140
|
def load_schema
|
111
141
|
@persisted = true
|
112
142
|
|
113
|
-
Lurker::
|
114
|
-
|
115
|
-
stringify_keys(extensions)
|
116
|
-
)
|
143
|
+
reader = Lurker::Json::Reader.new(endpoint_path)
|
144
|
+
schemify(reader.payload)
|
117
145
|
end
|
118
146
|
|
119
147
|
def build_schema
|
120
148
|
@persisted = false
|
121
149
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
}
|
128
|
-
|
129
|
-
)
|
130
|
-
end
|
131
|
-
|
132
|
-
def
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
YAML.load(erb)
|
137
|
-
else
|
138
|
-
YAML.load_file(fname)
|
150
|
+
payload = {
|
151
|
+
DESCRIPTION => '',
|
152
|
+
PREFIX => '',
|
153
|
+
REQUEST_PARAMETERS => {},
|
154
|
+
RESPONSE_CODES => [],
|
155
|
+
RESPONSE_PARAMETERS => {}
|
156
|
+
}
|
157
|
+
schemify(payload)
|
158
|
+
end
|
159
|
+
|
160
|
+
def schemify(payload)
|
161
|
+
Lurker::Json::Parser.plain(uri: endpoint_path).parse(payload).tap do |schm|
|
162
|
+
ext = Lurker::Json::Extensions.new(stringify_keys extensions)
|
163
|
+
schm.merge!(EXTENSIONS => ext)
|
139
164
|
end
|
140
165
|
end
|
141
166
|
|
167
|
+
def finalize_schema!
|
168
|
+
path_params = schema[EXTENSIONS][PATH_PARAMS] || {}
|
169
|
+
subject = path_params[CONTROLLER].to_s.split(/\//).last.to_s
|
170
|
+
description = DESCRPTIONS[path_params[ACTION]]
|
171
|
+
|
172
|
+
schema[DESCRIPTION] = "#{subject.singularize} #{description}".strip if schema[DESCRIPTION].blank?
|
173
|
+
schema[PREFIX] = "#{subject} management" if schema[PREFIX].blank?
|
174
|
+
end
|
175
|
+
|
142
176
|
def raise_errors!
|
143
|
-
return if
|
177
|
+
return if @response_errors.empty?
|
144
178
|
|
145
|
-
errors = (
|
179
|
+
errors = (@request_errors | @response_errors) * "\n"
|
146
180
|
exception = Lurker::ValidationError.new(word_wrap errors)
|
147
181
|
if (example = Lurker::Spy.current.block).respond_to?(:metadata) && (metadata = example.metadata).respond_to?(:location, true)
|
148
182
|
exception.set_backtrace [metadata.send(:location)]
|
149
183
|
end
|
184
|
+
|
150
185
|
raise exception
|
151
186
|
end
|
152
187
|
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Lurker
|
2
|
+
module Json
|
3
|
+
module Concerns
|
4
|
+
module Validatable
|
5
|
+
ID = 'id'.freeze
|
6
|
+
TYPE = 'type'.freeze
|
7
|
+
OBJECT = 'object'.freeze
|
8
|
+
PROPERTIES = 'properties'.freeze
|
9
|
+
ADDITIONAL_PROPERTIES = 'additionalProperties'.freeze
|
10
|
+
|
11
|
+
def validate(data)
|
12
|
+
Lurker::Validator.new(to_validation_schema, data,
|
13
|
+
record_errors: true).validate.map { |error| "- #{error}" }
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_validation_schema
|
17
|
+
set_additional_properties_false_on(to_hash).tap do |schema|
|
18
|
+
schema[ID] = "file://#{uri}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def set_additional_properties_false_on(object)
|
25
|
+
case object
|
26
|
+
when Hash
|
27
|
+
copy = object.dup
|
28
|
+
|
29
|
+
if object[TYPE] == OBJECT || object.key?(PROPERTIES)
|
30
|
+
copy[ADDITIONAL_PROPERTIES] ||= false
|
31
|
+
end
|
32
|
+
|
33
|
+
object.each do |key, value|
|
34
|
+
next if key == ADDITIONAL_PROPERTIES
|
35
|
+
copy[key] = set_additional_properties_false_on(value)
|
36
|
+
end
|
37
|
+
|
38
|
+
copy
|
39
|
+
when Array
|
40
|
+
copy = object.map { |value| set_additional_properties_false_on(value) }
|
41
|
+
else object
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Lurker
|
2
|
+
module Json
|
3
|
+
class Orderer
|
4
|
+
EXTENSIONS = 'extensions'.freeze
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def reorder(schema)
|
8
|
+
new.reorder(schema)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def reorder(schema)
|
13
|
+
extensions = schema.delete(EXTENSIONS).try(:reorder!)
|
14
|
+
schema.reorder!
|
15
|
+
schema[EXTENSIONS] = extensions
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Lurker
|
2
|
+
module Json
|
3
|
+
class Parser
|
4
|
+
module Expertise
|
5
|
+
REF = '$ref'.freeze
|
6
|
+
TYPE = 'type'.freeze
|
7
|
+
ANYOF = 'anyOf'.freeze
|
8
|
+
ALLOF = 'allOf'.freeze
|
9
|
+
ONEOF = 'oneOf'.freeze
|
10
|
+
ITEMS = 'items'.freeze
|
11
|
+
PROPERTIES = 'properties'.freeze
|
12
|
+
|
13
|
+
module_function
|
14
|
+
|
15
|
+
def type_defined?(hash)
|
16
|
+
return false unless hash.is_a?(Hash)
|
17
|
+
|
18
|
+
hash.key? TYPE
|
19
|
+
end
|
20
|
+
|
21
|
+
def type_supposed?(hash)
|
22
|
+
return false unless hash.is_a?(Hash)
|
23
|
+
|
24
|
+
hash.key?(ANYOF) || hash.key?(ALLOF) || hash.key?(ONEOF) ||
|
25
|
+
hash.key?(ITEMS) || hash.key?(PROPERTIES) || hash.key?(REF)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Lurker
|
2
|
+
module Json
|
3
|
+
class Parser
|
4
|
+
class PlainStrategy
|
5
|
+
include Lurker::Json::Parser::Expertise
|
6
|
+
|
7
|
+
attr_reader :schema_options
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
@schema_options = options.dup
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse(payload)
|
14
|
+
case payload
|
15
|
+
when Lurker::Json::Schema
|
16
|
+
payload
|
17
|
+
when Hash
|
18
|
+
return parse_as_typed(payload) if type_defined?(payload) ||
|
19
|
+
type_supposed?(payload)
|
20
|
+
|
21
|
+
Lurker::Json::Schema.new(payload, schema_options)
|
22
|
+
when Array
|
23
|
+
payload.map do |schema|
|
24
|
+
Lurker::Json::Parser.plain(schema_options).parse(schema)
|
25
|
+
end
|
26
|
+
else
|
27
|
+
payload
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def parse_as_typed(payload)
|
34
|
+
Lurker::Json::Parser.typed(schema_options).parse(payload)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Lurker
|
2
|
+
module Json
|
3
|
+
class Parser
|
4
|
+
class TypedStrategy
|
5
|
+
include Lurker::Json::Parser::Expertise
|
6
|
+
|
7
|
+
ANYOF = 'anyOf'.freeze
|
8
|
+
ALLOF = 'allOf'.freeze
|
9
|
+
ONEOF = 'oneOf'.freeze
|
10
|
+
ITEMS = 'items'.freeze
|
11
|
+
TYPE = 'type'.freeze
|
12
|
+
ARRAY = 'array'.freeze
|
13
|
+
OBJECT = 'object'.freeze
|
14
|
+
PROPERTIES = 'properties'.freeze
|
15
|
+
|
16
|
+
attr_reader :schema_options
|
17
|
+
|
18
|
+
def initialize(options)
|
19
|
+
@schema_options = options.dup
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse(payload)
|
23
|
+
case payload
|
24
|
+
when Lurker::Json::Schema
|
25
|
+
payload
|
26
|
+
when Hash
|
27
|
+
return create_by_type(payload) if type_defined?(payload)
|
28
|
+
return create_by_supposition(payload) if type_supposed?(payload)
|
29
|
+
|
30
|
+
Lurker::Json::Object.new(payload, schema_options)
|
31
|
+
when Array
|
32
|
+
Lurker::Json::List.new(payload, schema_options)
|
33
|
+
else
|
34
|
+
Lurker::Json::Attribute.new(payload, schema_options)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def create_by_supposition(payload)
|
41
|
+
if payload.key?(ITEMS)
|
42
|
+
Lurker::Json::List.new(payload, schema_options)
|
43
|
+
elsif payload.key?(PROPERTIES)
|
44
|
+
Lurker::Json::Object.new(payload, schema_options)
|
45
|
+
elsif payload.key?(ANYOF)
|
46
|
+
Lurker::Json::Tuple::AnyOf.new(payload, schema_options)
|
47
|
+
elsif payload.key?(ALLOF)
|
48
|
+
Lurker::Json::Tuple::AllOf.new(payload, schema_options)
|
49
|
+
elsif payload.key?(ONEOF)
|
50
|
+
Lurker::Json::Tuple::OneOf.new(payload, schema_options)
|
51
|
+
elsif payload.key?(REF)
|
52
|
+
Lurker::Json::Reference.new(payload, schema_options)
|
53
|
+
else
|
54
|
+
raise "Unknown type supposition for #{payload}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_by_type(payload)
|
59
|
+
case payload[TYPE]
|
60
|
+
when OBJECT
|
61
|
+
Lurker::Json::Object.new(payload, schema_options)
|
62
|
+
when ARRAY
|
63
|
+
Lurker::Json::List.new(payload, schema_options)
|
64
|
+
else
|
65
|
+
Lurker::Json::Attribute.new(payload, schema_options)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Lurker
|
2
|
+
module Json
|
3
|
+
class Parser
|
4
|
+
class << self
|
5
|
+
def plain(options = {})
|
6
|
+
new(options).plain
|
7
|
+
end
|
8
|
+
|
9
|
+
def typed(options = {})
|
10
|
+
new(options).typed
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
@root_schema = options[:root_schema]
|
16
|
+
@parent_schema = options[:parent_schema]
|
17
|
+
@parent_property = options[:parent_property]
|
18
|
+
@uri = options[:uri] || @parent_schema.try(:uri)
|
19
|
+
@strategy = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse(payload)
|
23
|
+
parse_once { @strategy.new(schema_options_once).parse(payload) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse_property(property, payload)
|
27
|
+
options = schema_options_once.merge!(parent_property: property)
|
28
|
+
parse_once { @strategy.new(options).parse(payload) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def plain(options = {})
|
32
|
+
@options = schema_options.merge!(options)
|
33
|
+
@strategy = strategy_klass(:plain)
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def typed(options = {})
|
38
|
+
@options = schema_options.merge!(options)
|
39
|
+
@strategy = strategy_klass(:typed)
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def parse_once(&block)
|
46
|
+
raise 'Define parsing strategy [plain|typed] before using' unless @strategy.present?
|
47
|
+
|
48
|
+
result = block.call
|
49
|
+
@strategy = nil
|
50
|
+
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
54
|
+
def schema_options_once
|
55
|
+
options = @options.present? ? @options.dup : schema_options
|
56
|
+
@options = {}
|
57
|
+
|
58
|
+
options
|
59
|
+
end
|
60
|
+
|
61
|
+
def strategy_klass(name)
|
62
|
+
"lurker/json/parser/#{name}_strategy".camelize.constantize
|
63
|
+
end
|
64
|
+
|
65
|
+
def schema_options
|
66
|
+
{
|
67
|
+
uri: @uri, root_schema: @root_schema,
|
68
|
+
parent_schema: @parent_schema, parent_property: @parent_property
|
69
|
+
}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|