sober_swag 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.github/config/rubocop_linter_action.yml +5 -0
  3. data/.github/workflows/lint.yml +15 -0
  4. data/.github/workflows/ruby.yml +23 -1
  5. data/.gitignore +3 -0
  6. data/.rubocop.yml +73 -1
  7. data/.ruby-version +1 -1
  8. data/Gemfile.lock +29 -5
  9. data/README.md +109 -0
  10. data/bin/console +15 -14
  11. data/docs/serializers.md +203 -0
  12. data/example/.rspec +1 -0
  13. data/example/.ruby-version +1 -1
  14. data/example/Gemfile +10 -6
  15. data/example/Gemfile.lock +96 -76
  16. data/example/app/controllers/people_controller.rb +37 -21
  17. data/example/app/controllers/posts_controller.rb +102 -0
  18. data/example/app/models/application_record.rb +3 -0
  19. data/example/app/models/person.rb +6 -0
  20. data/example/app/models/post.rb +9 -0
  21. data/example/app/output_objects/person_errors_output_object.rb +5 -0
  22. data/example/app/output_objects/person_output_object.rb +15 -0
  23. data/example/app/output_objects/post_output_object.rb +10 -0
  24. data/example/bin/bundle +24 -20
  25. data/example/bin/rails +1 -1
  26. data/example/bin/rake +1 -1
  27. data/example/config/application.rb +11 -7
  28. data/example/config/environments/development.rb +0 -1
  29. data/example/config/environments/production.rb +3 -3
  30. data/example/config/puma.rb +5 -5
  31. data/example/config/routes.rb +3 -0
  32. data/example/config/spring.rb +4 -4
  33. data/example/db/migrate/20200311152021_create_people.rb +0 -1
  34. data/example/db/migrate/20200603172347_create_posts.rb +11 -0
  35. data/example/db/schema.rb +16 -7
  36. data/example/spec/rails_helper.rb +64 -0
  37. data/example/spec/requests/people/create_spec.rb +52 -0
  38. data/example/spec/requests/people/get_spec.rb +35 -0
  39. data/example/spec/requests/people/index_spec.rb +69 -0
  40. data/example/spec/spec_helper.rb +94 -0
  41. data/lib/sober_swag.rb +6 -3
  42. data/lib/sober_swag/compiler/error.rb +2 -0
  43. data/lib/sober_swag/compiler/path.rb +2 -5
  44. data/lib/sober_swag/compiler/paths.rb +0 -1
  45. data/lib/sober_swag/compiler/type.rb +28 -15
  46. data/lib/sober_swag/controller.rb +16 -11
  47. data/lib/sober_swag/controller/route.rb +18 -21
  48. data/lib/sober_swag/controller/undefined_body_error.rb +3 -0
  49. data/lib/sober_swag/controller/undefined_path_error.rb +3 -0
  50. data/lib/sober_swag/controller/undefined_query_error.rb +3 -0
  51. data/lib/sober_swag/input_object.rb +28 -0
  52. data/lib/sober_swag/nodes/array.rb +1 -1
  53. data/lib/sober_swag/nodes/base.rb +2 -4
  54. data/lib/sober_swag/nodes/binary.rb +2 -1
  55. data/lib/sober_swag/nodes/enum.rb +4 -2
  56. data/lib/sober_swag/nodes/list.rb +0 -1
  57. data/lib/sober_swag/nodes/primitive.rb +6 -5
  58. data/lib/sober_swag/output_object.rb +102 -0
  59. data/lib/sober_swag/output_object/definition.rb +30 -0
  60. data/lib/sober_swag/{blueprint → output_object}/field.rb +14 -4
  61. data/lib/sober_swag/{blueprint → output_object}/field_syntax.rb +1 -1
  62. data/lib/sober_swag/{blueprint → output_object}/view.rb +15 -6
  63. data/lib/sober_swag/parser.rb +5 -3
  64. data/lib/sober_swag/serializer.rb +5 -2
  65. data/lib/sober_swag/serializer/array.rb +12 -0
  66. data/lib/sober_swag/serializer/base.rb +50 -1
  67. data/lib/sober_swag/serializer/conditional.rb +15 -2
  68. data/lib/sober_swag/serializer/field_list.rb +29 -6
  69. data/lib/sober_swag/serializer/mapped.rb +12 -2
  70. data/lib/sober_swag/serializer/meta.rb +35 -0
  71. data/lib/sober_swag/serializer/optional.rb +17 -2
  72. data/lib/sober_swag/serializer/primitive.rb +4 -1
  73. data/lib/sober_swag/server.rb +83 -0
  74. data/lib/sober_swag/types.rb +3 -0
  75. data/lib/sober_swag/version.rb +1 -1
  76. data/sober_swag.gemspec +6 -4
  77. metadata +77 -44
  78. data/example/person.json +0 -4
  79. data/example/test/controllers/.keep +0 -0
  80. data/example/test/fixtures/.keep +0 -0
  81. data/example/test/fixtures/files/.keep +0 -0
  82. data/example/test/fixtures/people.yml +0 -11
  83. data/example/test/integration/.keep +0 -0
  84. data/example/test/models/.keep +0 -0
  85. data/example/test/models/person_test.rb +0 -7
  86. data/example/test/test_helper.rb +0 -13
  87. data/lib/sober_swag/blueprint.rb +0 -113
  88. data/lib/sober_swag/path.rb +0 -8
  89. data/lib/sober_swag/path/integer.rb +0 -21
  90. data/lib/sober_swag/path/lit.rb +0 -41
  91. data/lib/sober_swag/path/literal.rb +0 -29
  92. data/lib/sober_swag/path/param.rb +0 -33
@@ -0,0 +1,35 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe 'Getting a person' do
4
+ context 'with a good id' do
5
+ let!(:person) { Person.create(first_name: 'Anthony', last_name: 'Guy') }
6
+ let(:request) { get "/people/#{person.id}" }
7
+
8
+ describe 'the response' do
9
+ subject { request && response }
10
+
11
+ it { should be_successful }
12
+ it { should_not be_server_error }
13
+ end
14
+
15
+ describe 'the response body' do
16
+ subject { request && response && JSON.parse(response.body) }
17
+
18
+ it { should include('first_name' => 'Anthony') }
19
+ it { should include('last_name' => 'Guy') }
20
+ end
21
+ end
22
+
23
+ context 'with a bad id' do
24
+ let(:request) { get '/people/my-awesome-guy' }
25
+
26
+ describe 'the response' do
27
+ subject { request && response }
28
+
29
+ it { should_not be_successful }
30
+ it { should_not be_server_error }
31
+ it { should have_http_status(:bad_request) }
32
+ it { should be_bad_request }
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,69 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe 'Index action for people' do
4
+ let(:parsed_body) { request && response && JSON.parse(response.body) }
5
+
6
+ context 'with no people' do
7
+ let(:request) { get '/people' }
8
+
9
+ it 'is successful' do
10
+ request
11
+ expect(response).to be_successful
12
+ end
13
+
14
+ it 'is has a blank body' do
15
+ expect(parsed_body).to be_blank
16
+ end
17
+ end
18
+
19
+ context 'with a person' do
20
+ let!(:person) { Person.create(first_name: 'Anthony', last_name: 'Guy') }
21
+
22
+ shared_examples 'a request with the person' do
23
+ it 'is successful' do
24
+ request
25
+ expect(response).to be_successful
26
+ end
27
+
28
+ it 'has people' do
29
+ expect(parsed_body).not_to be_blank
30
+ end
31
+
32
+ it 'has the right person' do
33
+ expect(parsed_body).to include(include('id' => person.id))
34
+ end
35
+ end
36
+
37
+ context 'with a good first-name search' do
38
+ let(:request) { get '/people', params: { first_name: 'A' } }
39
+
40
+ it_behaves_like 'a request with the person'
41
+ end
42
+
43
+ context 'with a good last-name search' do
44
+ let(:request) { get '/people', params: { last_name: 'G' } }
45
+
46
+ it_behaves_like 'a request with the person'
47
+ end
48
+
49
+ context 'with a valid view' do
50
+ let(:request) { get '/people', params: { view: 'detail' } }
51
+
52
+ it_behaves_like 'a request with the person'
53
+ end
54
+
55
+ context 'with an invalid view' do
56
+ let(:request) { get '/people', params: { view: 'not-a-thing-lol' } }
57
+
58
+ it 'is not successful' do
59
+ request
60
+ expect(response).not_to be_successful
61
+ end
62
+
63
+ it 'is a bad request' do
64
+ request
65
+ expect(response).to be_bad_request
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,94 @@
1
+ # This file was generated by the `rails generate rspec:install` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
16
+ RSpec.configure do |config|
17
+ # rspec-expectations config goes here. You can use an alternate
18
+ # assertion/expectation library such as wrong or the stdlib/minitest
19
+ # assertions if you prefer.
20
+ config.expect_with :rspec do |expectations|
21
+ # This option will default to `true` in RSpec 4. It makes the `description`
22
+ # and `failure_message` of custom matchers include text for helper methods
23
+ # defined using `chain`, e.g.:
24
+ # be_bigger_than(2).and_smaller_than(4).description
25
+ # # => "be bigger than 2 and smaller than 4"
26
+ # ...rather than:
27
+ # # => "be bigger than 2"
28
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
29
+ end
30
+
31
+ # rspec-mocks config goes here. You can use an alternate test double
32
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
33
+ config.mock_with :rspec do |mocks|
34
+ # Prevents you from mocking or stubbing a method that does not exist on
35
+ # a real object. This is generally recommended, and will default to
36
+ # `true` in RSpec 4.
37
+ mocks.verify_partial_doubles = true
38
+ end
39
+
40
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
41
+ # have no way to turn it off -- the option exists only for backwards
42
+ # compatibility in RSpec 3). It causes shared context metadata to be
43
+ # inherited by the metadata hash of host groups and examples, rather than
44
+ # triggering implicit auto-inclusion in groups with matching metadata.
45
+ config.shared_context_metadata_behavior = :apply_to_host_groups
46
+
47
+ # The settings below are suggested to provide a good initial experience
48
+ # with RSpec, but feel free to customize to your heart's content.
49
+ # # This allows you to limit a spec run to individual examples or groups
50
+ # # you care about by tagging them with `:focus` metadata. When nothing
51
+ # # is tagged with `:focus`, all examples get run. RSpec also provides
52
+ # # aliases for `it`, `describe`, and `context` that include `:focus`
53
+ # # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
54
+ # config.filter_run_when_matching :focus
55
+ #
56
+ # # Allows RSpec to persist some state between runs in order to support
57
+ # # the `--only-failures` and `--next-failure` CLI options. We recommend
58
+ # # you configure your source control system to ignore this file.
59
+ # config.example_status_persistence_file_path = "spec/examples.txt"
60
+ #
61
+ # # Limits the available syntax to the non-monkey patched syntax that is
62
+ # # recommended. For more details, see:
63
+ # # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
64
+ # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
65
+ # # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
66
+ # config.disable_monkey_patching!
67
+ #
68
+ # # Many RSpec users commonly either run the entire suite or an individual
69
+ # # file, and it's useful to allow more verbose output when running an
70
+ # # individual spec file.
71
+ # if config.files_to_run.one?
72
+ # # Use the documentation formatter for detailed output,
73
+ # # unless a formatter has already been configured
74
+ # # (e.g. via a command-line flag).
75
+ # config.default_formatter = "doc"
76
+ # end
77
+ #
78
+ # # Print the 10 slowest examples and example groups at the
79
+ # # end of the spec run, to help surface which specs are running
80
+ # # particularly slow.
81
+ # config.profile_examples = 10
82
+ #
83
+ # # Run specs in random order to surface order dependencies. If you find an
84
+ # # order dependency and want to debug it, you can fix the order by providing
85
+ # # the seed, which is printed after each run.
86
+ # # --seed 1234
87
+ # config.order = :random
88
+ #
89
+ # # Seed global randomization in this process using the `--seed` CLI option.
90
+ # # Setting this allows you to use `--seed` to deterministically reproduce
91
+ # # test failures related to randomization by passing the same `--seed` value
92
+ # # as the one that triggered the failure.
93
+ # Kernel.srand config.seed
94
+ end
@@ -15,15 +15,18 @@ module SoberSwag
15
15
 
16
16
  autoload :Parser, 'sober_swag/parser'
17
17
  autoload :Serializer, 'sober_swag/serializer'
18
- autoload :Blueprint, 'sober_swag/blueprint'
18
+ autoload :OutputObject, 'sober_swag/output_object'
19
19
  autoload :Nodes, 'sober_swag/nodes'
20
20
  autoload :Compiler, 'sober_swag/compiler'
21
21
  autoload :Controller, 'sober_swag/controller'
22
+ autoload :InputObject, 'sober_swag/input_object'
23
+ autoload :Server, 'sober_swag/server'
22
24
 
23
25
  ##
24
26
  # Define a struct of something.
25
27
  # Useful to prevent weirdness from autoloading.
26
- def self.struct(parent = Dry::Struct, &block)
27
- Class.new(parent, &block)
28
+ # @param parent [Class] the base class for the struct (default of {SoberSwag::Struct})
29
+ def self.input_object(parent = nil, &block)
30
+ Class.new(parent || SoberSwag::InputObject, &block)
28
31
  end
29
32
  end
@@ -1,5 +1,7 @@
1
1
  module SoberSwag
2
2
  class Compiler
3
+ ##
4
+ # Base class of compilation errors.
3
5
  class Error < ::SoberSwag::Error; end
4
6
  end
5
7
  end
@@ -24,7 +24,7 @@ module SoberSwag
24
24
  base
25
25
  end
26
26
 
27
- def responses
27
+ def responses # rubocop:disable Metrics/MethodLength
28
28
  route.response_serializers.map { |status, serializer|
29
29
  [
30
30
  status.to_s,
@@ -32,9 +32,7 @@ module SoberSwag
32
32
  description: route.response_descriptions[status],
33
33
  content: {
34
34
  'application/json': {
35
- schema: compiler.response_for(
36
- serializer.respond_to?(:new) ? serializer.new.type : serializer.type
37
- )
35
+ schema: compiler.response_for(serializer.type)
38
36
  }
39
37
  }
40
38
  }
@@ -74,7 +72,6 @@ module SoberSwag
74
72
  }
75
73
  }
76
74
  end
77
-
78
75
  end
79
76
  end
80
77
  end
@@ -48,7 +48,6 @@ module SoberSwag
48
48
  def compile_route(route, compiler)
49
49
  SoberSwag::Compiler::Path.new(route, compiler).schema
50
50
  end
51
-
52
51
  end
53
52
  end
54
53
  end
@@ -3,14 +3,18 @@ module SoberSwag
3
3
  ##
4
4
  # A compiler for DRY-Struct data types, essentially.
5
5
  # It only consumes one type at a time.
6
- class Type
6
+ class Type # rubocop:disable Metrics/ClassLength
7
7
  class << self
8
8
  def get_ref(klass)
9
9
  "#/components/schemas/#{safe_name(klass)}"
10
10
  end
11
11
 
12
12
  def safe_name(klass)
13
- klass.to_s.gsub('::', '.')
13
+ if klass.respond_to?(:identifier)
14
+ klass.identifier
15
+ else
16
+ klass.to_s.gsub('::', '.')
17
+ end
14
18
  end
15
19
 
16
20
  def primitive?(value)
@@ -42,6 +46,8 @@ module SoberSwag
42
46
  class TooComplicatedForPathError < TooComplicatedError; end
43
47
  class TooComplicatedForQueryError < TooComplicatedError; end
44
48
 
49
+ METADATA_KEYS = %i[description deprecated].freeze
50
+
45
51
  def initialize(type)
46
52
  @type = type
47
53
  end
@@ -91,7 +97,7 @@ module SoberSwag
91
97
  def parsed_type
92
98
  @parsed_type ||=
93
99
  begin
94
- (parsed, _) = parsed_result
100
+ (parsed,) = parsed_result
95
101
  parsed
96
102
  end
97
103
  end
@@ -110,7 +116,7 @@ module SoberSwag
110
116
 
111
117
  private
112
118
 
113
- def generate_schema_stub
119
+ def generate_schema_stub # rubocop:disable Metrics/MethodLength
114
120
  return self.class.primitive_def(type) if self.class.primitive?(type)
115
121
 
116
122
  case type
@@ -120,6 +126,8 @@ module SoberSwag
120
126
  self.class.new(type.type).schema_stub
121
127
  when Dry::Types::Array::Member
122
128
  { type: :array, items: self.class.new(type.member).schema_stub }
129
+ when Dry::Types::Sum
130
+ { oneOf: normalize(parsed_type).elements.map { |t| self.class.new(t.value).schema_stub } }
123
131
  else
124
132
  raise ArgumentError, "Cannot generate a schema stub for #{type} (#{type.class})"
125
133
  end
@@ -138,7 +146,7 @@ module SoberSwag
138
146
  object.cata { |e| rewrite_sums(e) }.cata { |e| flatten_one_ofs(e) }
139
147
  end
140
148
 
141
- def rewrite_sums(object)
149
+ def rewrite_sums(object) # rubocop:disable Metrics/MethodLength
142
150
  case object
143
151
  in Nodes::Sum[Nodes::OneOf[*lhs], Nodes::OneOf[*rhs]]
144
152
  Nodes::OneOf.new(lhs + rhs)
@@ -157,12 +165,12 @@ module SoberSwag
157
165
  case object
158
166
  in Nodes::OneOf[*args]
159
167
  Nodes::OneOf.new(args.uniq)
160
- else
168
+ else
161
169
  object
162
170
  end
163
171
  end
164
172
 
165
- def to_object_schema(object)
173
+ def to_object_schema(object) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
166
174
  case object
167
175
  in Nodes::List[element]
168
176
  {
@@ -186,11 +194,11 @@ module SoberSwag
186
194
  # openAPI requires that you give a list of required attributes
187
195
  # (which IMO is the *totally* wrong thing to do but whatever)
188
196
  # so we must do this garbage
189
- required = attrs.filter {|(_, b)| b[:required] }.map(&:first)
197
+ required = attrs.filter { |(_, b)| b[:required] }.map(&:first)
190
198
  {
191
199
  type: :object,
192
- properties: attrs.map { |(a,b)|
193
- [a, b.select { |k, _| k != :required }]
200
+ properties: attrs.map { |(a, b)|
201
+ [a, b.reject { |k, _| k == :required }]
194
202
  }.to_h,
195
203
  required: required
196
204
  }
@@ -201,8 +209,11 @@ module SoberSwag
201
209
  # can't match on value directly as ruby uses `===` to match,
202
210
  # and classes use `===` to mean `is an instance of`, as
203
211
  # opposed to direct equality lmao
204
- in Nodes::Primitive[value:] if self.class.primitive?(value)
205
- self.class.primitive_def(value)
212
+ in Nodes::Primitive[value:, metadata:] if self.class.primitive?(value)
213
+ md = self.class.primitive_def(value)
214
+ METADATA_KEYS.select(&metadata.method(:key?)).reduce(md) do |definition, key|
215
+ definition.merge(key => metadata[key])
216
+ end
206
217
  in Nodes::Primitive[value:]
207
218
  { '$ref': self.class.get_ref(value) }
208
219
  else
@@ -216,20 +227,22 @@ module SoberSwag
216
227
  ensure_uncomplicated(k, v)
217
228
  {
218
229
  name: k,
219
- schema: v.reject { |k, _| %i[required nullable].include?(k) },
230
+ schema: v.reject { |key, _| %i[required nullable].include?(key) },
231
+ # rubocop:disable Style/DoubleNegation
220
232
  allowEmptyValue: !object_schema[:required].include?(k) || !!v[:nullable], # if it's required, no empties, but if *nullabe*, empties are okay
221
- required: object_schema[:required].include?(k) || false,
233
+ # rubocop:enable Style/DoubleNegation
234
+ required: object_schema[:required].include?(k) || false
222
235
  }
223
236
  end
224
237
  end
225
238
 
226
239
  def ensure_uncomplicated(key, value)
227
240
  return if value[:type]
241
+
228
242
  raise TooComplicatedError, <<~ERROR
229
243
  Property #{key} has object-schema #{value}, but this type of param should be simple (IE a primitive of some kind)
230
244
  ERROR
231
245
  end
232
-
233
246
  end
234
247
  end
235
248
  end
@@ -10,10 +10,19 @@ module SoberSwag
10
10
  autoload :UndefinedPathError, 'sober_swag/controller/undefined_path_error'
11
11
  autoload :UndefinedQueryError, 'sober_swag/controller/undefined_query_error'
12
12
 
13
+ ##
14
+ # Types module, so you can more easily access Types::Whatever
15
+ # without having to type SoberSwag::Types::Whatever.
13
16
  module Types
14
17
  include ::Dry::Types()
15
18
  end
16
19
 
20
+ included do
21
+ rescue_from Dry::Struct::Error do
22
+ head :bad_request
23
+ end
24
+ end
25
+
17
26
  class_methods do
18
27
  ##
19
28
  # Define a new action with the given HTTP method, action name, and path.
@@ -65,10 +74,7 @@ module SoberSwag
65
74
  res = defined_routes.reduce(SoberSwag::Compiler.new) { |c, r| c.add_route(r) }
66
75
  {
67
76
  openapi: '3.0.0',
68
- info: {
69
- version: '1',
70
- title: self.name
71
- }
77
+ info: { version: '1', title: name }
72
78
  }.merge(res.to_swagger)
73
79
  end
74
80
  end
@@ -89,6 +95,7 @@ module SoberSwag
89
95
  begin
90
96
  r = current_action_def
91
97
  raise UndefinedPathError unless r&.path_params_class
98
+
92
99
  r.path_params_class.new(request.path_parameters)
93
100
  end
94
101
  end
@@ -102,6 +109,7 @@ module SoberSwag
102
109
  begin
103
110
  r = current_action_def
104
111
  raise UndefinedBodyError unless r&.request_body_class
112
+
105
113
  r.request_body_class.new(body_params)
106
114
  end
107
115
  end
@@ -115,6 +123,7 @@ module SoberSwag
115
123
  begin
116
124
  r = current_action_def
117
125
  raise UndefinedQueryError unless r&.query_params_class
126
+
118
127
  r.query_params_class.new(request.query_parameters)
119
128
  end
120
129
  end
@@ -124,11 +133,11 @@ module SoberSwag
124
133
  # @todo figure out how to specify views and other options for the serializer here
125
134
  # @param status [Symbol] the HTTP status symbol to use for the status code
126
135
  # @param entity the thing to serialize
127
- def respond!(status, entity)
136
+ def respond!(status, entity, serializer_opts: {}, rails_opts: {})
128
137
  r = current_action_def
129
138
  serializer = r.response_serializers[Rack::Utils.status_code(status)]
130
139
  serializer ||= serializer.new if serializer.respond_to?(:new)
131
- render json: serializer.serialize(entity)
140
+ render json: serializer.serialize(entity, serializer_opts), status: status, **rails_opts
132
141
  end
133
142
 
134
143
  ##
@@ -138,10 +147,7 @@ module SoberSwag
138
147
  # but it keeps the docs honest: parameters sent in the body *must* be
139
148
  # in the body.
140
149
  def body_params
141
- bparams = params.reject do |k, _|
142
- request.query_parameters.key?(k) || request.path_parameters.key?(k)
143
- end
144
- bparams.permit(bparams.keys)
150
+ request.request_parameters
145
151
  end
146
152
 
147
153
  ##
@@ -150,7 +156,6 @@ module SoberSwag
150
156
  def current_action_def
151
157
  self.class.find_route(params[:action])
152
158
  end
153
-
154
159
  end
155
160
  end
156
161