openapi_first 1.0.0.beta5 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +2 -1
  3. data/CHANGELOG.md +23 -2
  4. data/Gemfile +2 -0
  5. data/Gemfile.lock +16 -18
  6. data/Gemfile.rack2 +15 -0
  7. data/Gemfile.rack2.lock +99 -0
  8. data/README.md +99 -130
  9. data/lib/openapi_first/body_parser.rb +29 -0
  10. data/lib/openapi_first/configuration.rb +20 -0
  11. data/lib/openapi_first/definition/cookie_parameters.rb +12 -0
  12. data/lib/openapi_first/definition/header_parameters.rb +12 -0
  13. data/lib/openapi_first/definition/operation.rb +116 -0
  14. data/lib/openapi_first/definition/parameters.rb +47 -0
  15. data/lib/openapi_first/definition/path_item.rb +32 -0
  16. data/lib/openapi_first/definition/path_parameters.rb +12 -0
  17. data/lib/openapi_first/definition/query_parameters.rb +12 -0
  18. data/lib/openapi_first/definition/request_body.rb +43 -0
  19. data/lib/openapi_first/definition/response.rb +25 -0
  20. data/lib/openapi_first/definition/responses.rb +83 -0
  21. data/lib/openapi_first/definition.rb +61 -8
  22. data/lib/openapi_first/error_response.rb +22 -27
  23. data/lib/openapi_first/errors.rb +2 -14
  24. data/lib/openapi_first/failure.rb +55 -0
  25. data/lib/openapi_first/middlewares/request_validation.rb +52 -0
  26. data/lib/openapi_first/middlewares/response_validation.rb +35 -0
  27. data/lib/openapi_first/plugins/default/error_response.rb +74 -0
  28. data/lib/openapi_first/plugins/default.rb +11 -0
  29. data/lib/openapi_first/plugins/jsonapi/error_response.rb +58 -0
  30. data/lib/openapi_first/plugins/jsonapi.rb +11 -0
  31. data/lib/openapi_first/plugins.rb +9 -7
  32. data/lib/openapi_first/request_validation/request_body_validator.rb +41 -0
  33. data/lib/openapi_first/request_validation/validator.rb +81 -0
  34. data/lib/openapi_first/response_validation/validator.rb +101 -0
  35. data/lib/openapi_first/runtime_request.rb +84 -0
  36. data/lib/openapi_first/runtime_response.rb +31 -0
  37. data/lib/openapi_first/schema/validation_error.rb +18 -0
  38. data/lib/openapi_first/schema/validation_result.rb +32 -0
  39. data/lib/openapi_first/{json_schema.rb → schema.rb} +9 -5
  40. data/lib/openapi_first/version.rb +1 -1
  41. data/lib/openapi_first.rb +32 -28
  42. data/openapi_first.gemspec +10 -9
  43. metadata +55 -67
  44. data/.rspec +0 -3
  45. data/.rubocop.yml +0 -14
  46. data/Rakefile +0 -15
  47. data/benchmarks/Gemfile +0 -16
  48. data/benchmarks/Gemfile.lock +0 -142
  49. data/benchmarks/README.md +0 -29
  50. data/benchmarks/apps/committee_with_hanami_api.ru +0 -26
  51. data/benchmarks/apps/committee_with_response_validation.ru +0 -29
  52. data/benchmarks/apps/committee_with_sinatra.ru +0 -31
  53. data/benchmarks/apps/grape.ru +0 -21
  54. data/benchmarks/apps/hanami_api.ru +0 -21
  55. data/benchmarks/apps/hanami_router.ru +0 -14
  56. data/benchmarks/apps/openapi.yaml +0 -268
  57. data/benchmarks/apps/openapi_first_with_hanami_api.ru +0 -24
  58. data/benchmarks/apps/openapi_first_with_plain_rack.ru +0 -32
  59. data/benchmarks/apps/openapi_first_with_response_validation.ru +0 -25
  60. data/benchmarks/apps/openapi_first_with_sinatra.ru +0 -29
  61. data/benchmarks/apps/roda.ru +0 -27
  62. data/benchmarks/apps/sinatra.ru +0 -26
  63. data/benchmarks/apps/syro.ru +0 -25
  64. data/benchmarks/benchmark-wrk.sh +0 -3
  65. data/benchmarks/benchmarks.rb +0 -48
  66. data/benchmarks/post.lua +0 -3
  67. data/bin/console +0 -15
  68. data/bin/setup +0 -8
  69. data/examples/README.md +0 -13
  70. data/examples/app.rb +0 -18
  71. data/examples/config.ru +0 -7
  72. data/examples/openapi.yaml +0 -29
  73. data/lib/openapi_first/body_parser_middleware.rb +0 -40
  74. data/lib/openapi_first/config.rb +0 -20
  75. data/lib/openapi_first/error_responses/default.rb +0 -58
  76. data/lib/openapi_first/error_responses/json_api.rb +0 -58
  77. data/lib/openapi_first/json_schema/result.rb +0 -17
  78. data/lib/openapi_first/operation.rb +0 -170
  79. data/lib/openapi_first/request_body_validator.rb +0 -41
  80. data/lib/openapi_first/request_validation.rb +0 -118
  81. data/lib/openapi_first/request_validation_error.rb +0 -31
  82. data/lib/openapi_first/response_validation.rb +0 -93
  83. data/lib/openapi_first/response_validator.rb +0 -21
  84. data/lib/openapi_first/router.rb +0 -102
  85. data/lib/openapi_first/string_keyed_hash.rb +0 -20
  86. data/lib/openapi_first/use_router.rb +0 -18
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'hanami/router'
4
- require 'multi_json'
5
-
6
- app = Hanami::Router.new do
7
- get '/hello', to: ->(_env) { [200, {}, [MultiJson.dump([{ hello: 'world' }])]] }
8
- get '/hello/:id', to: lambda { |env|
9
- [200, {}, [MultiJson.dump(hello: 'world', id: env['router.params'][:id])]]
10
- }
11
- post '/hello', to: ->(_env) { [201, {}, [MultiJson.dump(hello: 'world')]] }
12
- end
13
-
14
- run app
@@ -1,268 +0,0 @@
1
- openapi: 3.0.0
2
- info:
3
- title: "API"
4
- version: "1.0.0"
5
- contact:
6
- name: Contact Name
7
- email: contact@example.com
8
- url: https://example.com/
9
- tags:
10
- - name: Metadata
11
- description: Metadata related requests
12
- paths:
13
- /hello/{id}:
14
- parameters:
15
- - name: id
16
- description: ID of the thing to get
17
- in: path
18
- required: true
19
- schema:
20
- type: string
21
- get:
22
- operationId: find_thing
23
- description: Get one thing
24
- tags: ["Metadata"]
25
- responses:
26
- "200":
27
- description: OK
28
- content:
29
- application/json:
30
- schema:
31
- type: object
32
- required: [hello, id]
33
- properties:
34
- hello:
35
- type: string
36
- id:
37
- type: string
38
- /hello:
39
- get:
40
- operationId: find_things
41
- description: Get multiple things
42
- tags: ["Metadata"]
43
- parameters:
44
- - name: filter
45
- description: filter things
46
- in: query
47
- required: false
48
- schema:
49
- type: object
50
- required: [id]
51
- properties:
52
- id:
53
- type: string
54
- description: Comma separated list of thing-IDs
55
-
56
- responses:
57
- "200":
58
- description: OK
59
- content:
60
- application/json:
61
- schema:
62
- type: array
63
- items:
64
- type: object
65
- required: [hello]
66
- properties:
67
- hello:
68
- type: string
69
- default:
70
- description: Error response
71
-
72
- post:
73
- operationId: create_thing
74
- description: Create a thing
75
- tags: ["Metadata"]
76
- requestBody:
77
- content:
78
- application/json:
79
- schema:
80
- type: object
81
- required:
82
- - say
83
- properties:
84
- say:
85
- type: string
86
- responses:
87
- "201":
88
- description: OK
89
- content:
90
- application/json:
91
- schema:
92
- type: object
93
- required: [hello]
94
- properties:
95
- hello:
96
- type: string
97
- /pets:
98
- get:
99
- description: |
100
- Returns all pets from the system that the user has access to
101
- Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia.
102
-
103
- Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien.
104
- operationId: find_pets
105
- parameters:
106
- - name: tags
107
- in: query
108
- description: tags to filter by
109
- required: false
110
- style: form
111
- schema:
112
- type: array
113
- items:
114
- type: string
115
- - name: limit
116
- in: query
117
- description: maximum number of results to return
118
- required: false
119
- schema:
120
- type: integer
121
- format: int32
122
- responses:
123
- '200':
124
- description: pet response
125
- content:
126
- application/json:
127
- schema:
128
- type: array
129
- items:
130
- $ref: '#/components/schemas/Pet'
131
- default:
132
- description: unexpected error
133
- content:
134
- application/json:
135
- schema:
136
- $ref: '#/components/schemas/Error'
137
- post:
138
- description: Creates a new pet in the store. Duplicates are allowed
139
- operationId: create_pet
140
- requestBody:
141
- description: Pet to add to the store
142
- required: true
143
- content:
144
- application/json:
145
- schema:
146
- $ref: '#/components/schemas/NewPet'
147
- responses:
148
- '200':
149
- description: pet response
150
- content:
151
- application/json:
152
- schema:
153
- $ref: '#/components/schemas/Pet'
154
- default:
155
- description: unexpected error
156
- content:
157
- application/json:
158
- schema:
159
- $ref: '#/components/schemas/Error'
160
- /pets/{id}:
161
- parameters:
162
- - name: id
163
- in: path
164
- description: ID of pet to fetch
165
- required: true
166
- schema:
167
- type: integer
168
- format: int64
169
- get:
170
- description: Returns a user based on a single ID, if the user does not have access to the pet
171
- operationId: find_pet
172
- responses:
173
- '200':
174
- description: pet response
175
- content:
176
- application/json:
177
- schema:
178
- $ref: '#/components/schemas/Pet'
179
- default:
180
- description: unexpected error
181
- content:
182
- application/json:
183
- schema:
184
- $ref: '#/components/schemas/Error'
185
- delete:
186
- description: deletes a single pet based on the ID supplied
187
- operationId: delete_pet
188
- parameters:
189
- - name: id
190
- in: path
191
- description: ID of pet to delete
192
- required: true
193
- schema:
194
- type: integer
195
- format: int64
196
- responses:
197
- '204':
198
- description: pet deleted
199
- default:
200
- description: unexpected error
201
- content:
202
- application/json:
203
- schema:
204
- $ref: '#/components/schemas/Error'
205
- patch:
206
- description: Updates a pet
207
- operationId: update_pet
208
- requestBody:
209
- description: Changes
210
- required: false
211
- content:
212
- application/json:
213
- schema:
214
- $ref: '#/components/schemas/NewPet'
215
- responses:
216
- '200':
217
- description: pet response
218
- content:
219
- application/json:
220
- schema:
221
- $ref: '#/components/schemas/Pet'
222
- default:
223
- description: unexpected error
224
- content:
225
- application/json:
226
- schema:
227
- $ref: '#/components/schemas/Error'
228
-
229
- components:
230
- schemas:
231
- Pet:
232
- allOf:
233
- - $ref: '#/components/schemas/NewPet'
234
- - required:
235
- - id
236
- properties:
237
- id:
238
- type: integer
239
- format: int64
240
-
241
- NewPet:
242
- required:
243
- - type
244
- - attributes
245
- properties:
246
- type:
247
- type: string
248
- enum:
249
- - pet
250
- - plant
251
- attributes:
252
- additionalProperties: false
253
- type: object
254
- required: [name]
255
- properties:
256
- name:
257
- type: string
258
-
259
- Error:
260
- required:
261
- - code
262
- - message
263
- properties:
264
- code:
265
- type: integer
266
- format: int32
267
- message:
268
- type: string
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'multi_json'
4
- require 'openapi_first'
5
- require 'hanami/api'
6
-
7
- app = Class.new(Hanami::API) do
8
- get '/hello/:id' do
9
- json(hello: 'world', id: params.fetch(:id))
10
- end
11
-
12
- get '/hello' do
13
- json([{ hello: 'world' }])
14
- end
15
-
16
- post '/hello' do
17
- status 201
18
- json(hello: 'world')
19
- end
20
- end.new
21
-
22
- use OpenapiFirst::RequestValidation, spec: File.absolute_path('./openapi.yaml', __dir__)
23
-
24
- run app
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'multi_json'
4
- require 'openapi_first'
5
-
6
- app = Rack::Builder.new do
7
- use OpenapiFirst::RequestValidation, spec: File.expand_path('./openapi.yaml', __dir__)
8
-
9
- handlers = {
10
- 'find_thing' => lambda do |env|
11
- params = env[OpenapiFirst::PARAMS]
12
- body = MultiJson.dump(hello: 'world', id: params.fetch('id'))
13
- [200, { 'Content-Type' => 'application/json' }, [body]]
14
- end,
15
- 'find_things' => lambda do |_env|
16
- body = MultiJson.dump(hello: 'world')
17
- [200, { 'Content-Type' => 'application/json' }, [body]]
18
- end,
19
- 'create_thing' => lambda do |_env|
20
- body = MultiJson.dump(hello: 'world')
21
- [201, { 'Content-Type' => 'application/json' }, [body]]
22
- end
23
- }
24
-
25
- not_found = ->(_env) { [404, {}, []] }
26
-
27
- run(lambda do |env|
28
- handlers.fetch(env[OpenapiFirst::OPERATION]&.operation_id, not_found).call(env)
29
- end)
30
- end
31
-
32
- run app
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'multi_json'
4
- require 'openapi_first'
5
- require 'hanami/api'
6
-
7
- app = Class.new(Hanami::API) do
8
- get '/hello/:id' do
9
- json(hello: 'world', id: params.fetch(:id))
10
- end
11
-
12
- get '/hello' do
13
- json([{ hello: 'world' }])
14
- end
15
-
16
- post '/hello' do
17
- status 201
18
- json(hello: 'world')
19
- end
20
- end.new
21
-
22
- use OpenapiFirst::RequestValidation, spec: File.absolute_path('./openapi.yaml', __dir__)
23
- use OpenapiFirst::ResponseValidation
24
-
25
- run app
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'multi_json'
4
- require 'openapi_first'
5
- require 'sinatra'
6
-
7
- class SinatraWithOpenapiFirstExample < Sinatra::Base
8
- set :environment, :production
9
-
10
- get '/hello/:id' do
11
- content_type :json
12
- MultiJson.dump(hello: 'world', id: params.fetch('id'))
13
- end
14
-
15
- get '/hello' do
16
- content_type :json
17
- MultiJson.dump([{ hello: 'world' }])
18
- end
19
-
20
- post '/hello' do
21
- content_type :json
22
- status 201
23
- MultiJson.dump(hello: 'world')
24
- end
25
- end
26
-
27
- use OpenapiFirst::RequestValidation, spec: File.absolute_path('./openapi.yaml', __dir__)
28
-
29
- run SinatraWithOpenapiFirstExample
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'roda'
4
- require 'multi_json'
5
-
6
- class App < Roda
7
- route do |r|
8
- r.on 'hello' do
9
- r.on :id do
10
- r.get do
11
- MultiJson.dump({ hello: 'world', id: r.params[:id] })
12
- end
13
- end
14
-
15
- r.get do
16
- MultiJson.dump([{ hello: 'world' }])
17
- end
18
-
19
- r.post do
20
- response.status = 201
21
- MultiJson.dump({ hello: 'world' })
22
- end
23
- end
24
- end
25
- end
26
-
27
- run App.freeze.app
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'multi_json'
4
- require 'sinatra/base'
5
-
6
- class SinatraExample < Sinatra::Base
7
- set :environment, :production
8
-
9
- get '/hello/:id' do
10
- content_type :json
11
- MultiJson.dump(hello: 'world', id: params.fetch('id'))
12
- end
13
-
14
- get '/hello' do
15
- content_type :json
16
- MultiJson.dump([{ hello: 'world' }])
17
- end
18
-
19
- post '/hello' do
20
- content_type :json
21
- status 201
22
- MultiJson.dump(hello: 'world')
23
- end
24
- end
25
-
26
- run SinatraExample
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'syro'
4
- require 'multi_json'
5
-
6
- app = Syro.new do
7
- on 'hello' do
8
- on :id do
9
- get do
10
- res.json({ hello: 'world', id: inbox[:id] })
11
- end
12
- end
13
-
14
- get do
15
- res.json([{ hello: 'world' }])
16
- end
17
-
18
- post do
19
- res.status = 201
20
- res.json({ hello: 'world' })
21
- end
22
- end
23
- end
24
-
25
- run app
@@ -1,3 +0,0 @@
1
- #!/bin/sh
2
-
3
- wrk -t12 -c400 -d10s --latency -s post.lua http://localhost:9292/hello
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'benchmark/ips'
4
- require 'benchmark/memory'
5
- require 'rack'
6
- require 'json'
7
- ENV['RACK_ENV'] = 'production'
8
-
9
- examples = [
10
- [Rack::MockRequest.env_for('/hello'), 200],
11
- [Rack::MockRequest.env_for('/unknown'), 404],
12
- [
13
- Rack::MockRequest.env_for('/hello', method: 'POST', input: JSON.dump({ say: 'hi!' }),
14
- 'CONTENT_TYPE' => 'application/json'), 201
15
- ],
16
- [Rack::MockRequest.env_for('/hello/1'), 200],
17
- [Rack::MockRequest.env_for('/hello/123'), 200],
18
- [Rack::MockRequest.env_for('/hello?filter[id]=1,2'), 200]
19
- ]
20
-
21
- glob = ARGV[0] || './apps/*.ru'
22
- apps = Dir[glob].each_with_object({}) do |config, hash|
23
- hash[config] = Rack::Builder.parse_file(config).first
24
- end
25
- apps.freeze
26
-
27
- bench = lambda do |app|
28
- examples.each do |example|
29
- env, expected_status = example
30
- 100.times { app.call(env) }
31
- response = app.call(env)
32
- raise "expected status #{expected_status}, but was #{response[0]}" unless response[0] == expected_status
33
- end
34
- end
35
-
36
- Benchmark.ips do |x|
37
- apps.each do |config, app|
38
- x.report(config) { bench.call(app) }
39
- end
40
- x.compare!
41
- end
42
-
43
- Benchmark.memory do |x|
44
- apps.each do |config, app|
45
- x.report(config) { bench.call(app) }
46
- end
47
- x.compare!
48
- end
data/benchmarks/post.lua DELETED
@@ -1,3 +0,0 @@
1
- wrk.method = "POST"
2
- wrk.body = "{\"say\":\"hi!\"}"
3
- wrk.headers["Content-Type"] = "application/json"
data/bin/console DELETED
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'bundler/setup'
5
- require 'openapi_first'
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
13
-
14
- require 'irb'
15
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
data/examples/README.md DELETED
@@ -1,13 +0,0 @@
1
- # Example
2
-
3
- How to run the example:
4
-
5
- ```bash
6
- cd examples
7
- bundle install
8
- bundle exec rackup
9
- ```
10
-
11
- open http://localhost:9292/
12
-
13
- 🎉
data/examples/app.rb DELETED
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'openapi_first'
4
- require 'rack'
5
-
6
- # This example is a bit contrived, but it shows what you could do with the middlewares
7
-
8
- App = Rack::Builder.new do
9
- use OpenapiFirst::RequestValidation, raise_error: true, spec: File.expand_path('./openapi.yaml', __dir__)
10
- use OpenapiFirst::ResponseValidation
11
-
12
- handlers = {
13
- 'things#index' => ->(_env) { [200, { 'Content-Type' => 'application/json' }, ['{"hello": "world"}']] }
14
- }
15
- not_found = ->(_env) { [404, {}, []] }
16
-
17
- run ->(env) { handlers.fetch(env[OpenapiFirst::OPERATION].operation_id, not_found).call(env) }
18
- end
data/examples/config.ru DELETED
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- lib = File.expand_path('../lib', __dir__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
-
6
- require_relative 'app'
7
- run App
@@ -1,29 +0,0 @@
1
- openapi: 3.0.0
2
- info:
3
- title: "API"
4
- version: "1.0.0"
5
- contact:
6
- name: Contact Name
7
- email: contact@example.com
8
- url: https://example.com/
9
- tags:
10
- - name: Metadata
11
- description: Metadata related requests
12
- paths:
13
- /:
14
- get:
15
- operationId: things#index
16
- summary: Get metadata from the root of the API
17
- tags: ["Metadata"]
18
- responses:
19
- "200":
20
- description: OK
21
- content:
22
- application/json:
23
- schema:
24
- type: object
25
- required: [hello]
26
- properties:
27
- hello:
28
- type: string
29
-
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'multi_json'
4
-
5
- module OpenapiFirst
6
- class BodyParserMiddleware
7
- def initialize(app)
8
- @app = app
9
- end
10
-
11
- ROUTER_PARSED_BODY = 'router.parsed_body'
12
- private_constant :ROUTER_PARSED_BODY
13
-
14
- def call(env)
15
- env[ROUTER_PARSED_BODY] = parse_body(env)
16
- @app.call(env)
17
- end
18
-
19
- private
20
-
21
- def parse_body(env)
22
- request = Rack::Request.new(env)
23
- body = read_body(request)
24
- return if body.empty?
25
-
26
- return MultiJson.load(body) if request.media_type =~ (/json/i) && (request.media_type =~ /json/i)
27
- return request.POST if request.form_data?
28
-
29
- body
30
- rescue MultiJson::ParseError
31
- raise BodyParsingError, 'Failed to parse body as application/json'
32
- end
33
-
34
- def read_body(request)
35
- body = request.body.read
36
- request.body.rewind
37
- body
38
- end
39
- end
40
- end