request_handler 0.10.0 → 0.11.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 +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +136 -10
- data/lib/request_handler/base.rb +6 -6
- data/lib/request_handler/body_parser.rb +38 -7
- data/lib/request_handler/schema_parser.rb +3 -3
- data/lib/request_handler/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d0758f2d7885ad1d5bc730ab95fbc00428effed
|
4
|
+
data.tar.gz: bc11401b7013aa968bd7d22a4d86b674fedf147f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e9d5ab0fd020c635be86bfd6c028768c9c6eb6c9e401be243bb3118c7a4e98f1af7db529cf44d5e41fcd178562c805042cc8a60d96a26ce5554cfcc2004da0d8
|
7
|
+
data.tar.gz: 5273a2cbf95cb99f33d243f174832a9597a27b0280785b0ea39f5a4ee0d6e3fd8bf2478b80b4e170d92658c5d41901e3b78fda8224c2190c7a7c2c6cbda58a91
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -90,17 +90,17 @@ class DemoHandler < RequestHandler::Base
|
|
90
90
|
options(->(_handler, _request) { { foo: "bar" } })
|
91
91
|
# options({foo: "bar"}) # also works for hash options instead of procs
|
92
92
|
end
|
93
|
+
end
|
93
94
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
end
|
95
|
+
def to_dto
|
96
|
+
OpenStruct.new(
|
97
|
+
body: body_params,
|
98
|
+
page: page_params,
|
99
|
+
include: include_params,
|
100
|
+
filter: filter_params,
|
101
|
+
sort: sort_params,
|
102
|
+
headers: headers
|
103
|
+
)
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
@@ -146,6 +146,132 @@ include_options = [:posts__comments]
|
|
146
146
|
sort_options = SortOption.new(:posts__published_on, :asc)
|
147
147
|
```
|
148
148
|
|
149
|
+
### Included relations
|
150
|
+
|
151
|
+
Sometimes you want to create a single resource with its relations in a single
|
152
|
+
request, ensuring that everything or nothing at all is created. However, the
|
153
|
+
current JSON API specification does not mention anything about how to achieve
|
154
|
+
this at all, it is expected that all associated resources already exist.
|
155
|
+
`request_handler` attempts to solve this problem by allowing the request body
|
156
|
+
to contain an `included` array with all the resources that have to be created.
|
157
|
+
|
158
|
+
#### Example
|
159
|
+
|
160
|
+
With this request handler:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
class CreateQuestionHandler < RequestHandler::Base
|
164
|
+
options do
|
165
|
+
body do
|
166
|
+
schema(
|
167
|
+
Dry::Validation.JSON do
|
168
|
+
required(:id).filled(:str?)
|
169
|
+
required(:type).filled(:str?)
|
170
|
+
required(:content).filled(:str?)
|
171
|
+
|
172
|
+
optional(:media).schema do
|
173
|
+
required(:id).filled(:str?)
|
174
|
+
required(:type).filled(:str?)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
)
|
178
|
+
|
179
|
+
included do
|
180
|
+
media(
|
181
|
+
Dry::Validation.JSON do
|
182
|
+
required(:id).filled(:str?)
|
183
|
+
required(:type).filled(:str?)
|
184
|
+
required(:url).filled(:str?)
|
185
|
+
|
186
|
+
optional(:categories).schema do
|
187
|
+
required(:id).filled(:str?)
|
188
|
+
required(:type).filled(:str?)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def to_dto
|
197
|
+
# see the resulting body_params below
|
198
|
+
{ body: body_params }
|
199
|
+
end
|
200
|
+
end
|
201
|
+
```
|
202
|
+
|
203
|
+
The following JSON object including its included items is validated with the
|
204
|
+
defined schema:
|
205
|
+
|
206
|
+
``` json
|
207
|
+
{
|
208
|
+
"data": {
|
209
|
+
"id": "1",
|
210
|
+
"type": "questions",
|
211
|
+
"attributes": {
|
212
|
+
"content": "How much is the fish?"
|
213
|
+
},
|
214
|
+
"relationships": {
|
215
|
+
"media": {
|
216
|
+
"data": {
|
217
|
+
"id": "image-123456",
|
218
|
+
"type": "media"
|
219
|
+
}
|
220
|
+
}
|
221
|
+
}
|
222
|
+
}
|
223
|
+
},
|
224
|
+
"included": [
|
225
|
+
{
|
226
|
+
"id": "image-123456",
|
227
|
+
"type": "media",
|
228
|
+
"attributes": {
|
229
|
+
"url": "https://example.com/fish.jpg"
|
230
|
+
},
|
231
|
+
"relationships": {
|
232
|
+
"categories": {
|
233
|
+
"data": {
|
234
|
+
"id": "123",
|
235
|
+
"type": "categories"
|
236
|
+
}
|
237
|
+
}
|
238
|
+
}
|
239
|
+
}
|
240
|
+
]
|
241
|
+
}
|
242
|
+
```
|
243
|
+
|
244
|
+
The resulting `body_params` will be this:
|
245
|
+
|
246
|
+
``` ruby
|
247
|
+
[
|
248
|
+
# The first object is the main resource object, i.e. the one that is about to
|
249
|
+
# be created
|
250
|
+
{
|
251
|
+
id: '1',
|
252
|
+
type: 'questions',
|
253
|
+
content: 'How much is the fish?'
|
254
|
+
media: [
|
255
|
+
{
|
256
|
+
id: 'image-123456',
|
257
|
+
type: 'media'
|
258
|
+
}
|
259
|
+
]
|
260
|
+
},
|
261
|
+
# The remaining objects are every included object, validated with the schema
|
262
|
+
# defined above
|
263
|
+
{
|
264
|
+
id: 'image-123456',
|
265
|
+
type: 'media',
|
266
|
+
url: 'https://example.com/fish.jpg',
|
267
|
+
categories: {
|
268
|
+
id: '123',
|
269
|
+
type: 'categories'
|
270
|
+
}
|
271
|
+
}
|
272
|
+
]
|
273
|
+
```
|
274
|
+
|
149
275
|
### Configuration
|
150
276
|
|
151
277
|
The default logger and separator can be changed globally by using
|
data/lib/request_handler/base.rb
CHANGED
@@ -95,12 +95,12 @@ module RequestHandler
|
|
95
95
|
end
|
96
96
|
|
97
97
|
def parse_body_params
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
).run
|
98
|
+
BodyParser.new(
|
99
|
+
request: request,
|
100
|
+
schema: lookup!('body.schema'),
|
101
|
+
schema_options: execute_options(lookup('body.options')),
|
102
|
+
included_schemas: lookup('body.included')
|
103
|
+
).run
|
104
104
|
end
|
105
105
|
|
106
106
|
def parse_fieldsets_params
|
@@ -3,14 +3,21 @@ require 'request_handler/schema_parser'
|
|
3
3
|
require 'request_handler/error'
|
4
4
|
module RequestHandler
|
5
5
|
class BodyParser < SchemaParser
|
6
|
-
def initialize(request:, schema:, schema_options: {})
|
6
|
+
def initialize(request:, schema:, schema_options: {}, included_schemas: {})
|
7
7
|
raise MissingArgumentError, :"request.body" => 'is missing' if request.body.nil?
|
8
8
|
super(schema: schema, schema_options: schema_options)
|
9
9
|
@request = request
|
10
|
+
@included_schemas = included_schemas
|
10
11
|
end
|
11
12
|
|
12
13
|
def run
|
13
|
-
|
14
|
+
body, *included = flattened_request_body
|
15
|
+
unless included_schemas?
|
16
|
+
raise SchemaValidationError, included: 'must be empty' unless included.empty?
|
17
|
+
return validate_schema(body)
|
18
|
+
end
|
19
|
+
|
20
|
+
validate_schemas(body, included)
|
14
21
|
end
|
15
22
|
|
16
23
|
private
|
@@ -19,10 +26,13 @@ module RequestHandler
|
|
19
26
|
body = request_body.fetch('data') do
|
20
27
|
raise ExternalArgumentError, body: 'must contain data'
|
21
28
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
29
|
+
[flatten_resource!(body), *parse_included]
|
30
|
+
end
|
31
|
+
|
32
|
+
def flatten_resource!(resource)
|
33
|
+
resource.merge!(resource.delete('attributes') { {} })
|
34
|
+
relationships = flatten_relationship_resource_linkages(resource.delete('relationships') { {} })
|
35
|
+
resource.merge!(relationships)
|
26
36
|
end
|
27
37
|
|
28
38
|
def flatten_relationship_resource_linkages(relationships)
|
@@ -33,6 +43,13 @@ module RequestHandler
|
|
33
43
|
end
|
34
44
|
end
|
35
45
|
|
46
|
+
def parse_included
|
47
|
+
included = request_body.fetch('included') { [] }
|
48
|
+
included.each do |hsh|
|
49
|
+
flatten_resource!(hsh)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
36
53
|
def request_body
|
37
54
|
b = request.body
|
38
55
|
b.rewind
|
@@ -40,6 +57,20 @@ module RequestHandler
|
|
40
57
|
b.empty? ? {} : MultiJson.load(b)
|
41
58
|
end
|
42
59
|
|
43
|
-
|
60
|
+
def included_schemas?
|
61
|
+
!(included_schemas.nil? || included_schemas.empty?)
|
62
|
+
end
|
63
|
+
|
64
|
+
def validate_schemas(body, included)
|
65
|
+
schemas = [validate_schema(body)]
|
66
|
+
included_schemas.each do |type, schema|
|
67
|
+
included.select { |inc| inc['type'] == type.to_s }.each do |inc|
|
68
|
+
schemas << validate_schema(inc, with: schema)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
schemas
|
72
|
+
end
|
73
|
+
|
74
|
+
attr_reader :request, :included_schemas
|
44
75
|
end
|
45
76
|
end
|
@@ -14,14 +14,14 @@ module RequestHandler
|
|
14
14
|
|
15
15
|
private
|
16
16
|
|
17
|
-
def validate_schema(data)
|
17
|
+
def validate_schema(data, with: schema)
|
18
18
|
raise MissingArgumentError, data: 'is missing' if data.nil?
|
19
|
-
validator = validate(data)
|
19
|
+
validator = validate(data, schema: with)
|
20
20
|
validation_failure?(validator)
|
21
21
|
validator.output
|
22
22
|
end
|
23
23
|
|
24
|
-
def validate(data)
|
24
|
+
def validate(data, schema:)
|
25
25
|
if schema_options.empty?
|
26
26
|
schema.call(data)
|
27
27
|
else
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: request_handler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Eger
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-
|
12
|
+
date: 2017-03-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: dry-validation
|