request_handler 0.12.0 → 0.13.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/.travis.yml +3 -3
- data/CHANGELOG.md +5 -0
- data/README.md +3 -127
- data/lib/request_handler/base.rb +1 -2
- data/lib/request_handler/body_parser.rb +3 -5
- data/lib/request_handler/json_api_document_parser.rb +5 -32
- data/lib/request_handler/multipart_parser.rb +13 -10
- data/lib/request_handler/version.rb +1 -1
- data/request_handler.gemspec +3 -1
- metadata +19 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f080519adc7f75f22de23333a3a0056538422d09
|
4
|
+
data.tar.gz: 12e57d56a1b7e384e76e868805db60fb1ec65771
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d67dd542766a8792ab0c084c3d7f24a6867e7811497e3452378a31af61d6ecb502abc335bd135bbf368a0188e305c23f15b108f4916f658e5b8975554fd492f0
|
7
|
+
data.tar.gz: 18f1534788d306a99eeff74365bc5e779b09e2a40d827826fe171ac4e1e4487c1a623e1ae8de4a22b530ffc19db1cb2564a44c109f8504c6a49078fbd3ee0067
|
data/.travis.yml
CHANGED
@@ -4,7 +4,7 @@ rvm:
|
|
4
4
|
- 2.2.7
|
5
5
|
- 2.3.4
|
6
6
|
- 2.4.1
|
7
|
-
- jruby-9.1.
|
7
|
+
- jruby-9.1.12.0
|
8
8
|
jdk:
|
9
9
|
- oraclejdk8
|
10
10
|
env:
|
@@ -22,11 +22,11 @@ matrix:
|
|
22
22
|
jdk: oraclejdk8
|
23
23
|
env: "JRUBY_OPTS='-Xcompile.invokedynamic=true --debug'"
|
24
24
|
allow_failures:
|
25
|
-
- rvm: jruby-9.1.
|
25
|
+
- rvm: jruby-9.1.12.0
|
26
26
|
jdk: oraclejdk8
|
27
27
|
env: "JRUBY_OPTS='-Xcompile.invokedynamic=true --debug'"
|
28
28
|
before_install:
|
29
29
|
- gem update --system
|
30
|
-
- gem install bundler -v 1.
|
30
|
+
- gem install bundler -v 1.15.1
|
31
31
|
before_script:
|
32
32
|
- bundle exec danger
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -146,136 +146,11 @@ 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
|
-
|
275
149
|
### Multipart requests
|
276
150
|
It is also possible to process and validate multipart requests, consisting of an arbitrary number of parts.
|
151
|
+
You can require specific resources, all the other listed resources are optional
|
277
152
|
|
278
|
-
The following request handler
|
153
|
+
The following request handler requires a question (which will be uploaded as a json-file) and accepts an additional
|
279
154
|
file related to the question
|
280
155
|
|
281
156
|
```ruby
|
@@ -283,6 +158,7 @@ class CreateQuestionHandler < RequestHandler::Base
|
|
283
158
|
options do
|
284
159
|
multipart do
|
285
160
|
question do
|
161
|
+
required true
|
286
162
|
schema(
|
287
163
|
Dry::Validation.JSON do
|
288
164
|
required(:id).filled(:str?)
|
data/lib/request_handler/base.rb
CHANGED
@@ -104,8 +104,7 @@ module RequestHandler
|
|
104
104
|
BodyParser.new(
|
105
105
|
request: request,
|
106
106
|
schema: lookup!('body.schema'),
|
107
|
-
schema_options: execute_options(lookup('body.options'))
|
108
|
-
included_schemas: lookup('body.included')
|
107
|
+
schema_options: execute_options(lookup('body.options'))
|
109
108
|
).run
|
110
109
|
end
|
111
110
|
|
@@ -5,20 +5,18 @@ require 'request_handler/error'
|
|
5
5
|
require 'request_handler/json_api_document_parser'
|
6
6
|
module RequestHandler
|
7
7
|
class BodyParser
|
8
|
-
def initialize(request:, schema:, schema_options: {}
|
8
|
+
def initialize(request:, schema:, schema_options: {})
|
9
9
|
raise MissingArgumentError, "request.body": 'is missing' if request.body.nil?
|
10
10
|
@request = request
|
11
11
|
@schema = schema
|
12
12
|
@schema_options = schema_options
|
13
|
-
@included_schemas = included_schemas
|
14
13
|
end
|
15
14
|
|
16
15
|
def run
|
17
16
|
JsonApiDocumentParser.new(
|
18
17
|
document: request_body,
|
19
18
|
schema: schema,
|
20
|
-
schema_options: schema_options
|
21
|
-
included_schemas: included_schemas
|
19
|
+
schema_options: schema_options
|
22
20
|
).run
|
23
21
|
end
|
24
22
|
|
@@ -31,6 +29,6 @@ module RequestHandler
|
|
31
29
|
b.empty? ? {} : MultiJson.load(b)
|
32
30
|
end
|
33
31
|
|
34
|
-
attr_reader :request, :schema, :schema_options
|
32
|
+
attr_reader :request, :schema, :schema_options
|
35
33
|
end
|
36
34
|
end
|
@@ -4,21 +4,15 @@ require 'request_handler/schema_parser'
|
|
4
4
|
require 'request_handler/error'
|
5
5
|
module RequestHandler
|
6
6
|
class JsonApiDocumentParser < SchemaParser
|
7
|
-
def initialize(document:, schema:, schema_options: {}
|
7
|
+
def initialize(document:, schema:, schema_options: {})
|
8
8
|
raise MissingArgumentError, "data": 'is missing' if document.nil?
|
9
9
|
super(schema: schema, schema_options: schema_options)
|
10
10
|
@document = document
|
11
|
-
@included_schemas = included_schemas
|
12
11
|
end
|
13
12
|
|
14
13
|
def run
|
15
|
-
resource
|
16
|
-
|
17
|
-
raise SchemaValidationError, included: 'must be empty' unless included.empty?
|
18
|
-
return validate_schema(resource)
|
19
|
-
end
|
20
|
-
|
21
|
-
validate_schemas(resource, included)
|
14
|
+
resource = flattened_document
|
15
|
+
validate_schema(resource)
|
22
16
|
end
|
23
17
|
|
24
18
|
private
|
@@ -27,7 +21,7 @@ module RequestHandler
|
|
27
21
|
resource = document.fetch('data') do
|
28
22
|
raise BodyParamsError, resource: 'must contain data'
|
29
23
|
end
|
30
|
-
|
24
|
+
flatten_resource!(resource)
|
31
25
|
end
|
32
26
|
|
33
27
|
def flatten_resource!(resource)
|
@@ -44,27 +38,6 @@ module RequestHandler
|
|
44
38
|
end
|
45
39
|
end
|
46
40
|
|
47
|
-
|
48
|
-
included = document.fetch('included') { [] }
|
49
|
-
included.each do |hsh|
|
50
|
-
flatten_resource!(hsh)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def included_schemas?
|
55
|
-
!(included_schemas.nil? || included_schemas.empty?)
|
56
|
-
end
|
57
|
-
|
58
|
-
def validate_schemas(resource, included)
|
59
|
-
schemas = [validate_schema(resource)]
|
60
|
-
included_schemas.each do |type, schema|
|
61
|
-
included.select { |inc| inc['type'] == type.to_s }.each do |inc|
|
62
|
-
schemas << validate_schema(inc, with: schema)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
schemas
|
66
|
-
end
|
67
|
-
|
68
|
-
attr_reader :document, :included_schemas
|
41
|
+
attr_reader :document
|
69
42
|
end
|
70
43
|
end
|
@@ -16,20 +16,21 @@ module RequestHandler
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def run
|
19
|
-
multipart_config.
|
20
|
-
|
21
|
-
|
19
|
+
multipart_config.each_with_object({}) do |(name, config), memo|
|
20
|
+
raise MultipartParamsError, multipart: "#{name} missing" if config[:required] && !params.key?(name.to_s)
|
21
|
+
next if params[name.to_s].nil?
|
22
|
+
memo[name] = parse_part(name.to_s)
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
25
26
|
private
|
26
27
|
|
27
28
|
def parse_part(name)
|
28
|
-
params[name
|
29
|
+
params[name].fetch(:tempfile) { raise MultipartParamsError, multipart_file: 'missing' }
|
29
30
|
if lookup("#{name}.schema")
|
30
31
|
parse_data(name)
|
31
32
|
else
|
32
|
-
params[name
|
33
|
+
params[name]
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
@@ -37,19 +38,21 @@ module RequestHandler
|
|
37
38
|
JsonApiDocumentParser.new(
|
38
39
|
document: load_json(name),
|
39
40
|
schema: lookup("#{name}.schema"),
|
40
|
-
schema_options: execute_options(lookup("#{name}.options"))
|
41
|
-
included_schemas: lookup("#{name}.included")
|
41
|
+
schema_options: execute_options(lookup("#{name}.options"))
|
42
42
|
).run
|
43
43
|
end
|
44
44
|
|
45
45
|
def load_json(name)
|
46
|
-
|
46
|
+
file = multipart_file(name)
|
47
|
+
file.rewind
|
48
|
+
file = file.read
|
49
|
+
MultiJson.load(file)
|
47
50
|
rescue MultiJson::ParseError
|
48
|
-
raise MultipartParamsError, multipart_file: 'invalid'
|
51
|
+
raise MultipartParamsError, multipart_file: 'invalid JSON'
|
49
52
|
end
|
50
53
|
|
51
54
|
def multipart_file(name)
|
52
|
-
params[name
|
55
|
+
params[name][:tempfile]
|
53
56
|
end
|
54
57
|
|
55
58
|
def lookup(key)
|
data/request_handler.gemspec
CHANGED
@@ -23,7 +23,9 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
24
|
spec.require_paths = ['lib']
|
25
25
|
|
26
|
-
spec.add_dependency 'dry-validation', '~> 0.
|
26
|
+
spec.add_dependency 'dry-validation', '~> 0.11.0'
|
27
|
+
spec.add_dependency 'dry-types', '~> 0.11.0'
|
28
|
+
|
27
29
|
spec.add_dependency 'confstruct', '~> 1.0.2'
|
28
30
|
spec.add_dependency 'multi_json', '~> 1.12'
|
29
31
|
|
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.13.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-07-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: dry-validation
|
@@ -17,14 +17,28 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: 0.
|
20
|
+
version: 0.11.0
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: 0.
|
27
|
+
version: 0.11.0
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: dry-types
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.11.0
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 0.11.0
|
28
42
|
- !ruby/object:Gem::Dependency
|
29
43
|
name: confstruct
|
30
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -281,7 +295,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
281
295
|
version: '0'
|
282
296
|
requirements: []
|
283
297
|
rubyforge_project:
|
284
|
-
rubygems_version: 2.
|
298
|
+
rubygems_version: 2.6.11
|
285
299
|
signing_key:
|
286
300
|
specification_version: 4
|
287
301
|
summary: shared base for request_handler using dry-* gems
|