rspec-rails-api 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +112 -29
- data/Gemfile.lock +1 -1
- data/README.md +207 -4
- data/lib/rspec/rails/api/dsl/example.rb +35 -16
- data/lib/rspec/rails/api/dsl/example_group.rb +11 -1
- data/lib/rspec/rails/api/entity_config.rb +2 -2
- data/lib/rspec/rails/api/field_config.rb +4 -1
- data/lib/rspec/rails/api/metadata.rb +46 -18
- data/lib/rspec/rails/api/open_api_renderer.rb +141 -36
- data/lib/rspec/rails/api/version.rb +1 -1
- data/lib/rspec_rails_api.rb +2 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aa6db3329e09e4356d205cf49f100e11132a3e8a8fcb681ef8ede5e22ba470dd
|
4
|
+
data.tar.gz: e25b15faecc41214f92bb089f8fc9e28702f9c5ad2072daf4014251d30f23291
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c947cf296de131ece545c7a21ed093d79de1e1fd5f57af53c58985eab815c781ce4dcbba7f7269c679dbf8155d4eabf0ae5035caa70bbd88090650630ed8e11
|
7
|
+
data.tar.gz: b0c4d8bef1c715b0ea369b5f7a659e8fa69344735af2a7735a51a1cb755c3fc3b642597c47a133be9568c0361d8565725ed803d2b0dd530050d1e286795ee911
|
data/CHANGELOG.md
CHANGED
@@ -1,22 +1,74 @@
|
|
1
1
|
# Change Log
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
-
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
5
6
|
|
6
|
-
|
7
|
+
<!--
|
8
|
+
Quick remainder of the possible sections:
|
9
|
+
-----------------------------------------
|
10
|
+
Added, for new features.
|
11
|
+
Changed, for changes in existing functionality.
|
12
|
+
Deprecated, for soon-to-be removed features.
|
13
|
+
Removed, for now removed features.
|
14
|
+
Fixed, for any bug fixes.
|
15
|
+
Security, in case of vulnerabilities.
|
16
|
+
Maintenance, in case of rework, dependencies change
|
17
|
+
-->
|
18
|
+
|
19
|
+
## [Unreleased]
|
20
|
+
|
21
|
+
## [0.6.0] - 2023-07-17
|
22
|
+
|
23
|
+
### Added
|
24
|
+
|
25
|
+
- Declaring the same resource multiple times is now possible
|
26
|
+
- Add a simple support for security schemes
|
27
|
+
- Add support for global entities declarations. Keep things DRYer :)
|
28
|
+
- `test_response_of`: add `ignore_content_type` flag to ignore response's content type
|
29
|
+
- `test_response_of`: add `ignore_response` flag to ignore tests on response
|
30
|
+
- Renderer: Add simple support for redactable content. It will replace content in responses by something else. For now,
|
31
|
+
sub-entities are not redacted.
|
32
|
+
- Support for `file` type. When declaring a field with the `file` type, it will change the request content-type to
|
33
|
+
`multipart/form-data` automatically.
|
34
|
+
|
35
|
+
### Changed
|
36
|
+
|
37
|
+
- Generated JSON files will always be prettified to ease changes reviews when they are versioned
|
38
|
+
- When an unexpected 422 error happens, also display its content in error message
|
39
|
+
- [BREAKING] It is now impossible to declare the same entity twice. To remediate, rename your declared entities and/or
|
40
|
+
use global declarations. Check [README](./README.md) for example.
|
41
|
+
- Renderer: Use response data as-is when it's not valid JSON
|
42
|
+
|
43
|
+
### Fixed
|
44
|
+
|
45
|
+
- Strip generated descriptions and summaries
|
46
|
+
- Primitives are no more referenced in schemas for "expect_one" types
|
47
|
+
- Compare type `:float` against `Numeric` class
|
48
|
+
- Entities: Raise error when using `:array` type with `attributes` property and `:object` type with `of` property
|
49
|
+
- Rendering of sub-references now use the correct reference
|
50
|
+
- Metadata: Add missing type on array parameters
|
51
|
+
- Don't transform parameters into JSON when making `get` requests
|
52
|
+
- Operation IDs don't use summaries for uniqueness
|
53
|
+
- Downcase response content type before comparison
|
54
|
+
- Type format is now added on request parameters if applicable
|
55
|
+
|
56
|
+
## [0.5.0] - 2023-01-02
|
57
|
+
|
58
|
+
### Changed
|
7
59
|
|
8
60
|
- Improved error messages
|
9
61
|
- Improved usage of primitive types: use `:string` instead of `:type_string`, and more generally, remove the `type_`
|
10
62
|
prefix
|
63
|
+
|
64
|
+
### Fixed
|
65
|
+
-
|
11
66
|
- Fixed an error when an object is defined with an attribute named `type`.
|
12
67
|
|
13
|
-
## 0.4.0 - 2021-12-19
|
68
|
+
## [0.4.0] - 2021-12-19
|
69
|
+
|
70
|
+
### Added
|
14
71
|
|
15
|
-
- All parameters attributes are considered required unless specified
|
16
|
-
- Fix object `attributes` key in spec and documentation.
|
17
|
-
When defining object attributes, documentation and tests used `properties` key
|
18
|
-
while the code was waiting for an `attributes` key. The later makes more sense
|
19
|
-
so the spec and documentation were fixed.
|
20
72
|
- Check for arrays of primitives is now a thing:
|
21
73
|
```rb
|
22
74
|
# In entities, when attribute is an array of primitives
|
@@ -28,15 +80,19 @@ All notable changes to this project will be documented in this file.
|
|
28
80
|
#...
|
29
81
|
end
|
30
82
|
```
|
31
|
-
- RSpec metadata is now stored in `rra` instead of `rrad` (the gem's first name
|
32
|
-
was RSpec Rails API Doc at the time). Update RSpec configuration accordingly.
|
33
83
|
- OpenApi:
|
34
84
|
- Responses now includes the schema
|
35
85
|
- `on_xxx` methods second parameter is now used as summary instead of description.
|
36
86
|
Description can be defined on the third parameter.
|
87
|
+
|
88
|
+
### Changed
|
89
|
+
|
90
|
+
- All parameters attributes are considered required unless specified
|
91
|
+
- RSpec metadata is now stored in `rra` instead of `rrad` (the gem's first name
|
92
|
+
was RSpec Rails API Doc at the time). Update RSpec configuration accordingly.
|
37
93
|
- DSL changes:
|
38
94
|
- `visit` is renamed to `test_response_of`
|
39
|
-
- Support for `doc_only` is removed
|
95
|
+
- Support for `doc_only` is removed
|
40
96
|
- Response expectations _should_ now be declared with `for_code`, and should be
|
41
97
|
removed from example bodies:
|
42
98
|
```rb
|
@@ -52,44 +108,71 @@ All notable changes to this project will be documented in this file.
|
|
52
108
|
end
|
53
109
|
```
|
54
110
|
|
111
|
+
### Fixed
|
112
|
+
|
113
|
+
- Fix object `attributes` key in spec and documentation.
|
114
|
+
When defining object attributes, documentation and tests used `properties` key
|
115
|
+
while the code was waiting for an `attributes` key. The later makes more sense
|
116
|
+
so the spec and documentation were fixed.
|
117
|
+
|
118
|
+
## [0.3.4] - 2021-10-20
|
119
|
+
|
120
|
+
### Added
|
55
121
|
|
56
|
-
## 0.3.4 - 2021-10-20
|
57
122
|
- Add the "required" attribute in parameters
|
58
123
|
|
59
|
-
## 0.3.3 - 2021-06-02
|
124
|
+
## [0.3.3] - 2021-06-02
|
125
|
+
|
126
|
+
### Fixed
|
127
|
+
|
60
128
|
- Fix correct types on request parameters
|
61
129
|
|
62
|
-
## 0.3.2 - 2021-03-09
|
63
|
-
|
130
|
+
## [0.3.2] - 2021-03-09
|
131
|
+
|
132
|
+
### Changed
|
133
|
+
|
64
134
|
- Render examples results as YAML/JSON objects instead of text blocks.
|
65
135
|
|
66
|
-
|
136
|
+
### Fixed
|
137
|
+
|
138
|
+
- Fix YAML rendering (ruby objects were sometimes rendered in documentation)
|
139
|
+
|
140
|
+
## [0.3.1] - 2020-04-09
|
141
|
+
|
142
|
+
### Added
|
143
|
+
|
67
144
|
- Add support for "test only" examples, allowing to write examples without documentation.
|
68
145
|
|
69
|
-
## 0.3.0 - 2019-12-26
|
146
|
+
## [0.3.0] - 2019-12-26
|
70
147
|
|
71
148
|
### Changed
|
149
|
+
|
72
150
|
- Rails 6 support, deprecated methods from Rails 5 are not supported. Use version `0.2.3`
|
73
151
|
of this gem if your application is still on 5.
|
74
152
|
|
75
153
|
## 0.2.3 - 2019-12-04
|
76
154
|
|
77
|
-
###
|
155
|
+
### Added
|
78
156
|
|
79
157
|
- Generated Swagger file now use the payloads of POST/PATCH/PUT requests.
|
80
|
-
- Minimum Ruby version is now specified: Ruby 2.3.3.
|
81
158
|
|
82
|
-
|
159
|
+
### Changed
|
160
|
+
|
161
|
+
- Minimum Ruby version is now specified: Ruby 2.3.3.
|
83
162
|
|
84
|
-
|
85
|
-
same one, with a version bump_
|
163
|
+
## [0.2.2] - 2019-11-03
|
86
164
|
|
87
165
|
### Changed
|
88
166
|
|
89
167
|
- `for_code` method now have its `description` optional. If none is provided,
|
90
168
|
the description will be set from the status code.
|
91
169
|
|
92
|
-
## 0.2.
|
170
|
+
## [0.2.1] - 2019-11-03 [YANKED]
|
171
|
+
|
172
|
+
_Version 0.2.1 was released and yanked by mistake. Version 0.2.2 is the exact
|
173
|
+
same one, with a version bump_
|
174
|
+
|
175
|
+
## [0.2.0] - 2019-11-02
|
93
176
|
|
94
177
|
### Added
|
95
178
|
|
@@ -108,35 +191,35 @@ of the fixtures.
|
|
108
191
|
the existing calls accordingly. To use params defined with `parameters`, use the
|
109
192
|
`defined` option: `request_params defined: :common_form_params`
|
110
193
|
|
111
|
-
## 0.1.5 - 2019-10-31
|
194
|
+
## [0.1.5] - 2019-10-31
|
112
195
|
|
113
196
|
### Fixed
|
114
197
|
|
115
198
|
- Fixed issue with POST/PUT/DELETE requests with no `request_params`
|
116
199
|
- Improved documentation (integration with Devise, typos and mistakes)
|
117
200
|
|
118
|
-
## 0.1.4 - 2019-10-24
|
201
|
+
## [0.1.4] - 2019-10-24
|
119
202
|
|
120
203
|
### Added
|
121
204
|
|
122
205
|
- Added support for arrays of objects in request parameters
|
123
206
|
|
124
|
-
## 0.1.3 - 2019-10-23
|
207
|
+
## [0.1.3] - 2019-10-23
|
125
208
|
|
126
209
|
### Added
|
127
210
|
|
128
211
|
- Added ability to document API descriptions, servers, etc... from the RSpec helper files
|
129
212
|
|
130
|
-
## 0.1.2 - 2019-10-22
|
213
|
+
## [0.1.2] - 2019-10-22
|
131
214
|
|
132
215
|
### Added
|
133
216
|
|
134
217
|
- Added `item` property for arrays descriptions
|
135
218
|
|
136
|
-
## 0.1.1 - 2019-10-22
|
219
|
+
## [0.1.1] - 2019-10-22
|
137
220
|
|
138
221
|
- Added support for custom headers in request examples (useful for `visit` method)
|
139
222
|
|
140
|
-
## 0.1.0 - 2019-10-21
|
223
|
+
## [0.1.0] - 2019-10-21
|
141
224
|
|
142
225
|
Initial release
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -72,7 +72,101 @@ end
|
|
72
72
|
|
73
73
|
## Configuration
|
74
74
|
|
75
|
-
**TODO: This section is incomplete and the gem has no generator yet
|
75
|
+
**TODO: This section is incomplete and the gem has no generator yet.**
|
76
|
+
|
77
|
+
Here is an example configuration:
|
78
|
+
|
79
|
+
```rb
|
80
|
+
# spec/rails_helper.rb
|
81
|
+
require 'spec/support/rspec_rails_api'
|
82
|
+
```
|
83
|
+
|
84
|
+
```rb
|
85
|
+
# spec/support/acceptance_entities.rb
|
86
|
+
|
87
|
+
# This file contains common object definitions
|
88
|
+
{
|
89
|
+
error: {
|
90
|
+
error: { type: :string, description: "Error message" }
|
91
|
+
},
|
92
|
+
form_error: {
|
93
|
+
title: { type: :array, required: false, description: "Title errors", of: :string }
|
94
|
+
},
|
95
|
+
}.each do |name, attributes|
|
96
|
+
RSpec::Rails::Api::Metadata.add_entity name, attributes
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
100
|
+
```rb
|
101
|
+
# spec/support/rspec_rails_api.rb
|
102
|
+
require 'rspec_rails_api'
|
103
|
+
require 'support/acceptance_entities'
|
104
|
+
|
105
|
+
# Include DSL in RSpec
|
106
|
+
RSpec.configure do |config|
|
107
|
+
config.include RSpec::Rails::Api::DSL::Example
|
108
|
+
end
|
109
|
+
|
110
|
+
# Initialize and configure the renderer
|
111
|
+
renderer = RSpec::Rails::Api::OpenApiRenderer.new
|
112
|
+
# Server URL for quick reference
|
113
|
+
server_url = 'https://example.com'
|
114
|
+
|
115
|
+
# Options here should be present for a valid OpenAPI file
|
116
|
+
renderer.api_title = 'MyProject API'
|
117
|
+
renderer.api_version = '1'
|
118
|
+
|
119
|
+
# Options below are optional
|
120
|
+
#
|
121
|
+
# API description. Markdown supported
|
122
|
+
# renderer.api_description = 'Manage data on MyProject'
|
123
|
+
#
|
124
|
+
# List of servers, to live-test the documentation
|
125
|
+
# renderer.api_servers = [{ url: server_url }, { url: 'http://localhost:3000' }]
|
126
|
+
#
|
127
|
+
# Link to the API terms of service, if any
|
128
|
+
# renderer.api_tos = 'http://example.com/tos.html'
|
129
|
+
#
|
130
|
+
# Contact information
|
131
|
+
# renderer.api_contact = { name: 'Admin', email: 'admin@example.com', url: 'http://example.com/contact' }
|
132
|
+
#
|
133
|
+
# API license information
|
134
|
+
# renderer.api_license = { name: 'Apache', url: 'https://opensource.org/licenses/Apache-2.0' }
|
135
|
+
#
|
136
|
+
# Possible security schemes
|
137
|
+
# renderer.add_security_scheme :pkce_code_grant, 'PKCE code grant',
|
138
|
+
# type: 'oauth2',
|
139
|
+
# flows: {
|
140
|
+
# implicit: {
|
141
|
+
# authorizationUrl: "#{server_url}/oauth/authorize",
|
142
|
+
# scopes: { read: 'will read data on your behalf', write: 'will write data on your behalf' }
|
143
|
+
# }
|
144
|
+
# }
|
145
|
+
# renderer.add_security_scheme :bearer, 'Bearer token',
|
146
|
+
# type: 'http',
|
147
|
+
# scheme: 'bearer'
|
148
|
+
#
|
149
|
+
# Declare keys whose values should be filtered in responses.
|
150
|
+
# renderer.redact_responses entity_name: { key: 'REDACTED' },
|
151
|
+
# other_entity: { other_key: ['REDACTED'] }
|
152
|
+
|
153
|
+
|
154
|
+
# We need to merge each context metadata so we can reference to them to build the final file
|
155
|
+
RSpec.configuration.after(:context, type: :acceptance) do |context|
|
156
|
+
renderer.merge_context context.class.metadata[:rra].to_h
|
157
|
+
# During development of rspec_rails_api, you may want to dump raw metadata to a file
|
158
|
+
renderer.merge_context context.class.metadata[:rra].to_h, dump_metadata: true
|
159
|
+
end
|
160
|
+
|
161
|
+
# Skip this block if you don't need the OpenAPI documentation file and only have your responses tested
|
162
|
+
RSpec.configuration.after(:suite) do
|
163
|
+
renderer.write_files Rails.root.join('public/swagger') # Write both YAML and prettified JSON files
|
164
|
+
# or
|
165
|
+
renderer.write_files Rails.root.join('public/swagger'), only: [:json] # Prettified JSON only
|
166
|
+
# or
|
167
|
+
renderer.write_files Rails.root.join('public/swagger'), only: [:yaml] # YAML only
|
168
|
+
end
|
169
|
+
```
|
76
170
|
|
77
171
|
### Integration with Devise
|
78
172
|
|
@@ -124,7 +218,7 @@ end
|
|
124
218
|
# In examples
|
125
219
|
#...
|
126
220
|
for_code 200, 'Success' do |url|
|
127
|
-
|
221
|
+
sign_in #...
|
128
222
|
test_response_of url
|
129
223
|
|
130
224
|
#...
|
@@ -149,7 +243,7 @@ The idea is to have a simple DSL, and declare things like:
|
|
149
243
|
**spec/acceptance/users_spec.rb**
|
150
244
|
|
151
245
|
```ruby
|
152
|
-
require '
|
246
|
+
require 'rails_helper'
|
153
247
|
|
154
248
|
RSpec.describe 'Users', type: :acceptance do
|
155
249
|
resource 'Users', 'Manage users'
|
@@ -186,6 +280,78 @@ RSpec.describe 'Users', type: :acceptance do
|
|
186
280
|
end
|
187
281
|
```
|
188
282
|
|
283
|
+
### Entity declarations
|
284
|
+
|
285
|
+
You can declare entities locally (in every spec files), but sometimes you will need to use/reference the same entity
|
286
|
+
in multiple spec files (e.g.: an error message). In that case, you can create _global_ entities in separate files, and they
|
287
|
+
will be picked-up when needed.
|
288
|
+
|
289
|
+
Example of a local entity:
|
290
|
+
|
291
|
+
```rb
|
292
|
+
# spec/acceptance/api/users_acceptance_spec.rb
|
293
|
+
require 'rails_helper'
|
294
|
+
|
295
|
+
RSpec.describe 'Users', type: :acceptance do
|
296
|
+
resource 'Users', 'Manage users'
|
297
|
+
|
298
|
+
# This is a local entity
|
299
|
+
entity :user,
|
300
|
+
id: { type: :integer, description: 'The id' },
|
301
|
+
email: { type: :string, description: 'The name' },
|
302
|
+
role: { type: :string, description: 'The name' },
|
303
|
+
created_at: { type: :datetime, description: 'Creation date' },
|
304
|
+
updated_at: { type: :datetime, description: 'Modification date' },
|
305
|
+
url: { type: :string, description: 'URL to this category' }
|
306
|
+
|
307
|
+
on_get '/api/users/', 'Users list' do
|
308
|
+
for_code 200, 'Success response', expect_many: :user do |url|
|
309
|
+
test_response_of url
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
#...
|
314
|
+
end
|
315
|
+
```
|
316
|
+
|
317
|
+
Defining global entities:
|
318
|
+
|
319
|
+
```rb
|
320
|
+
# spec/support/entities/user.rb
|
321
|
+
# This file should be required at some point in the "rails_helper" or "acceptance_helper"
|
322
|
+
|
323
|
+
require 'rspec/rails/api/metadata'
|
324
|
+
|
325
|
+
RSpec::Rails::Api::Metadata.add_entity :user,
|
326
|
+
id: { type: :integer, description: 'The id' },
|
327
|
+
email: { type: :string, description: 'The name' },
|
328
|
+
role: { type: :string, description: 'The name' },
|
329
|
+
created_at: { type: :datetime, description: 'Creation date' },
|
330
|
+
updated_at: { type: :datetime, description: 'Modification date' },
|
331
|
+
url: { type: :string, description: 'URL to this category' }
|
332
|
+
|
333
|
+
```
|
334
|
+
|
335
|
+
Organization of the global entities declaration is up to you.
|
336
|
+
|
337
|
+
```rb
|
338
|
+
# spec/acceptance/api/users_acceptance_spec.rb
|
339
|
+
require 'rails_helper'
|
340
|
+
|
341
|
+
RSpec.describe 'Users', type: :acceptance do
|
342
|
+
resource 'Users', 'Manage users'
|
343
|
+
|
344
|
+
on_get '/api/users/', 'Users list' do
|
345
|
+
# "user" will use the global user entity
|
346
|
+
for_code 200, 'Success response', expect_many: :user do |url|
|
347
|
+
test_response_of url
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
#...
|
352
|
+
end
|
353
|
+
```
|
354
|
+
|
189
355
|
### DSL
|
190
356
|
|
191
357
|
#### Example groups
|
@@ -197,6 +363,23 @@ Starts a resource description.
|
|
197
363
|
- It must be called before any other documentation calls.
|
198
364
|
- It should be in the first `describe block`
|
199
365
|
|
366
|
+
A resource may be completed across multiple spec files:
|
367
|
+
|
368
|
+
```rb
|
369
|
+
# an_acceptance_spec.rb
|
370
|
+
RSpec.describe 'Something', type: :acceptance do
|
371
|
+
resource 'User', 'Manage users'
|
372
|
+
end
|
373
|
+
|
374
|
+
# another_acceptance_spec.rb
|
375
|
+
RSpec.describe 'Something else', type: :acceptance do
|
376
|
+
resource 'User', 'Another description'
|
377
|
+
end
|
378
|
+
|
379
|
+
```
|
380
|
+
|
381
|
+
The first evaluated `resource` statement will be used as description; all the tests in both files will complete it.
|
382
|
+
|
200
383
|
##### `entity(type, fields)`
|
201
384
|
|
202
385
|
Describes an entity for the documentation. The type is only a reference,
|
@@ -423,11 +606,30 @@ Once again, you have to pass an argument to the block if you use
|
|
423
606
|
# ...
|
424
607
|
```
|
425
608
|
|
609
|
+
##### `requires_security(scheme_references)`
|
610
|
+
|
611
|
+
Specifies the valid security schemes to use for this request. Security schemes are declared at the renderer level
|
612
|
+
(see [the configuration example](#Configuration)).
|
613
|
+
|
614
|
+
```rb
|
615
|
+
# Given a previously :basic scheme
|
616
|
+
|
617
|
+
# ...
|
618
|
+
on_get '/some/path' do
|
619
|
+
require_security :basic, :implicit
|
620
|
+
|
621
|
+
for_code 200 do |url|
|
622
|
+
#...
|
623
|
+
end
|
624
|
+
end
|
625
|
+
# ...
|
626
|
+
```
|
627
|
+
|
426
628
|
#### Examples
|
427
629
|
|
428
630
|
Example methods are available in `for_code` blocks
|
429
631
|
|
430
|
-
##### `test_response_of(example, path_params: {}, payload: {}, headers: {})`
|
632
|
+
##### `test_response_of(example, path_params: {}, payload: {}, headers: {}, ignore_content_type: false)`
|
431
633
|
|
432
634
|
Visits the described URL and:
|
433
635
|
|
@@ -441,6 +643,7 @@ Visits the described URL and:
|
|
441
643
|
- `payload`: a hash of values to send. Ignored for GET and DELETE
|
442
644
|
requests
|
443
645
|
- `headers`: a hash of custom headers.
|
646
|
+
- `ignore_content_type`: whether to ignore response's content-type. By default, checks for a JSON response
|
444
647
|
|
445
648
|
```ruby
|
446
649
|
for_code 200, 'Success' do |url|
|
@@ -9,14 +9,15 @@ module RSpec
|
|
9
9
|
##
|
10
10
|
# Visits the current example and tests the response
|
11
11
|
#
|
12
|
-
# @param example
|
13
|
-
# @param path_params
|
14
|
-
# @param payload
|
15
|
-
# @param headers
|
12
|
+
# @param example [Hash] Current example
|
13
|
+
# @param path_params [Hash] Path parameters definition
|
14
|
+
# @param payload [Hash] Request body
|
15
|
+
# @param headers [Hash] Custom headers
|
16
|
+
# @param ignore_content_type [Boolean] Whether to ignore the response's content-type for this response only
|
16
17
|
#
|
17
18
|
# @return [void]
|
18
|
-
def test_response_of(example, path_params: {}, payload: {}, headers: {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
19
|
-
raise 'Missing context. Call
|
19
|
+
def test_response_of(example, path_params: {}, payload: {}, headers: {}, ignore_content_type: false, ignore_response: false) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/ParameterLists, Layout/LineLength
|
20
|
+
raise 'Missing context. Call "test_response_of" within a "for_code" block.' unless example
|
20
21
|
|
21
22
|
status_code = prepare_status_code example.class.description
|
22
23
|
|
@@ -25,10 +26,10 @@ module RSpec
|
|
25
26
|
|
26
27
|
send(request_params[:action],
|
27
28
|
request_params[:url],
|
28
|
-
params: request_params[:params]
|
29
|
+
params: request_params[:params],
|
29
30
|
headers: request_params[:headers])
|
30
31
|
|
31
|
-
check_response(response, status_code)
|
32
|
+
check_response(response, status_code, ignore_content_type: ignore_content_type) unless ignore_response
|
32
33
|
|
33
34
|
return if example.class.description.match?(/-> test (\d+)(.*)/)
|
34
35
|
|
@@ -38,7 +39,10 @@ module RSpec
|
|
38
39
|
private
|
39
40
|
|
40
41
|
##
|
41
|
-
# Searches for a defined entity in metadata
|
42
|
+
# Searches for a defined entity in example metadata or global entities
|
43
|
+
#
|
44
|
+
# If an entity needs expansion (e.g.: with an attribute like "type: :array, of: :something"), it will use the
|
45
|
+
# scope where the entity was found: global entities or example metadata.
|
42
46
|
#
|
43
47
|
# @param entity [Symbol] Entity reference
|
44
48
|
#
|
@@ -49,10 +53,10 @@ module RSpec
|
|
49
53
|
current_resource = rra_metadata.current_resource
|
50
54
|
raise '@current_resource is unset' unless current_resource
|
51
55
|
|
52
|
-
|
53
|
-
raise "
|
56
|
+
definition = RSpec::Rails::Api::Metadata.entities[entity.to_sym]
|
57
|
+
raise "Entity '#{entity}' was never defined (globally or in '#{current_resource}')" unless definition
|
54
58
|
|
55
|
-
|
59
|
+
definition.expand_with(RSpec::Rails::Api::Metadata.entities)
|
56
60
|
end
|
57
61
|
|
58
62
|
##
|
@@ -60,9 +64,21 @@ module RSpec
|
|
60
64
|
#
|
61
65
|
# @param response [ActionDispatch::TestResponse] The response
|
62
66
|
# @param expected_code [Number] Code to test for
|
63
|
-
|
64
|
-
|
65
|
-
|
67
|
+
# @param ignore_content_type [Boolean] Whether to ignore the response's content-type for
|
68
|
+
# this response only
|
69
|
+
def check_response(response, expected_code, ignore_content_type: false) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
|
70
|
+
code_error_message = if response.status != expected_code && response.status == 422
|
71
|
+
<<~TXT
|
72
|
+
expected: #{expected_code}
|
73
|
+
got: #{response.status}
|
74
|
+
response: #{response.body}
|
75
|
+
TXT
|
76
|
+
end
|
77
|
+
|
78
|
+
expect(response.status).to eq(expected_code), code_error_message
|
79
|
+
if expected_code != 204 && !ignore_content_type
|
80
|
+
expect(response.headers['Content-Type'].downcase).to eq 'application/json; charset=utf-8'
|
81
|
+
end
|
66
82
|
expectations = rra_current_example[:expectations]
|
67
83
|
expect(response).to have_many defined(expectations[:many]) if expectations[:many]
|
68
84
|
expect(response).to have_one defined(expectations[:one]) if expectations[:one]
|
@@ -98,9 +114,12 @@ module RSpec
|
|
98
114
|
# @return [Hash] Options for the request
|
99
115
|
def prepare_request_params(description, request_params = {}, payload = {}, request_headers = {})
|
100
116
|
example_params = description.split
|
117
|
+
verb = example_params[0].downcase
|
118
|
+
|
119
|
+
payload = payload.to_json if verb != 'get'
|
101
120
|
|
102
121
|
{
|
103
|
-
action:
|
122
|
+
action: verb,
|
104
123
|
url: prepare_request_url(example_params[1], request_params),
|
105
124
|
example_url: example_params[1],
|
106
125
|
params: payload,
|
@@ -28,7 +28,7 @@ module RSpec
|
|
28
28
|
#
|
29
29
|
# @return [void]
|
30
30
|
def entity(type, fields)
|
31
|
-
|
31
|
+
RSpec::Rails::Api::Metadata.add_entity type, fields
|
32
32
|
end
|
33
33
|
|
34
34
|
##
|
@@ -78,6 +78,16 @@ module RSpec
|
|
78
78
|
metadata[:rra].add_request_params attributes
|
79
79
|
end
|
80
80
|
|
81
|
+
##
|
82
|
+
# Declares security schemes valid for this path. It won't be enforced during testing but will complete the
|
83
|
+
# documentation. When the reference does not exist, an exception will be thrown _during_ render, not before.
|
84
|
+
#
|
85
|
+
# @param scheme_references [Array<Symbol>] References to a security scheme defined with the renderer's
|
86
|
+
# `add_security_scheme`.
|
87
|
+
def requires_security(*scheme_references)
|
88
|
+
metadata[:rra].add_security_references(*scheme_references)
|
89
|
+
end
|
90
|
+
|
81
91
|
##
|
82
92
|
# Defines a GET action
|
83
93
|
#
|
@@ -11,10 +11,13 @@ module RSpec
|
|
11
11
|
class FieldConfig
|
12
12
|
attr_accessor :required, :type, :attributes, :description
|
13
13
|
|
14
|
-
def initialize(type:, description:, required: true, attributes: nil, of: nil)
|
14
|
+
def initialize(type:, description:, required: true, attributes: nil, of: nil) # rubocop:disable Metrics/CyclomaticComplexity
|
15
15
|
@required = required
|
16
16
|
@description = description
|
17
|
+
|
17
18
|
raise "Field type not allowed: '#{type}'" unless Validator.valid_type?(type)
|
19
|
+
raise "Don't use 'of' on non-arrays" if of && type != :array
|
20
|
+
raise "Don't use 'attributes' on non-objects" if attributes && type != :object
|
18
21
|
|
19
22
|
define_attributes attributes if type == :object
|
20
23
|
define_attributes of if type == :array
|
@@ -10,11 +10,10 @@ module RSpec
|
|
10
10
|
module Api
|
11
11
|
# Handles contexts and examples metadata.
|
12
12
|
class Metadata # rubocop:disable Metrics/ClassLength
|
13
|
-
attr_reader :
|
13
|
+
attr_reader :resources, :parameters, :current_resource, :current_url, :current_method, :current_code
|
14
14
|
|
15
15
|
def initialize
|
16
16
|
@resources = {}
|
17
|
-
@entities = {}
|
18
17
|
@parameters = {}
|
19
18
|
# Only used when building metadata during RSpec boot
|
20
19
|
@current_resource = nil
|
@@ -23,6 +22,33 @@ module RSpec
|
|
23
22
|
@current_code = nil
|
24
23
|
end
|
25
24
|
|
25
|
+
class << self
|
26
|
+
##
|
27
|
+
# Define an entity globally.
|
28
|
+
#
|
29
|
+
# Global entities will be available within the specs, but if they are re-declared locally, the local variant
|
30
|
+
# will be used.
|
31
|
+
#
|
32
|
+
# @param name [Symbol] Entity name
|
33
|
+
# @param fields [Hash] Fields definitions
|
34
|
+
#
|
35
|
+
# @return [void]
|
36
|
+
def add_entity(name, fields)
|
37
|
+
@entities ||= {}
|
38
|
+
raise "#{name} is already declared" if @entities.key? name
|
39
|
+
|
40
|
+
@entities[name] = EntityConfig.new fields
|
41
|
+
end
|
42
|
+
|
43
|
+
def entities
|
44
|
+
@entities || {}
|
45
|
+
end
|
46
|
+
|
47
|
+
def reset
|
48
|
+
@entities = {}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
26
52
|
##
|
27
53
|
# Adds a resource to metadata
|
28
54
|
#
|
@@ -31,24 +57,10 @@ module RSpec
|
|
31
57
|
#
|
32
58
|
# @return [void]
|
33
59
|
def add_resource(name, description)
|
34
|
-
@resources[name.to_sym]
|
60
|
+
@resources[name.to_sym] ||= { description: description, paths: {} }
|
35
61
|
@current_resource = name.to_sym
|
36
62
|
end
|
37
63
|
|
38
|
-
##
|
39
|
-
# Adds an entity definition
|
40
|
-
#
|
41
|
-
# @param name [Symbol] Entity name
|
42
|
-
# @param fields [Hash] Fields definitions
|
43
|
-
#
|
44
|
-
#
|
45
|
-
# @return [void]
|
46
|
-
def add_entity(name, fields)
|
47
|
-
Utils.deep_set(@resources,
|
48
|
-
[@current_resource, 'entities', name],
|
49
|
-
EntityConfig.new(fields))
|
50
|
-
end
|
51
|
-
|
52
64
|
##
|
53
65
|
# Adds a parameter definition
|
54
66
|
#
|
@@ -61,6 +73,7 @@ module RSpec
|
|
61
73
|
|
62
74
|
fields.each_value do |field|
|
63
75
|
field[:required] = true unless field[:required] == false
|
76
|
+
field[:schema] = { type: field[:of] } if field[:type] == :array && PRIMITIVES.include?(field[:of])
|
64
77
|
end
|
65
78
|
@parameters[name] = fields
|
66
79
|
end
|
@@ -114,6 +127,20 @@ module RSpec
|
|
114
127
|
params)
|
115
128
|
end
|
116
129
|
|
130
|
+
# Associate a defined security scheme to this request
|
131
|
+
#
|
132
|
+
# @param references [Array<Symbol>] Security scheme reference
|
133
|
+
def add_security_references(*references)
|
134
|
+
check_current_context :resource, :url, :method
|
135
|
+
|
136
|
+
refs = @resources.dig @current_resource, 'paths', @current_url, 'actions', @current_method, 'security'
|
137
|
+
refs ||= []
|
138
|
+
refs += references
|
139
|
+
Utils.deep_set(@resources,
|
140
|
+
[@current_resource, 'paths', @current_url, 'actions', @current_method, 'security'],
|
141
|
+
refs)
|
142
|
+
end
|
143
|
+
|
117
144
|
##
|
118
145
|
# Adds an action and sets `@current_url` and `@current_method`
|
119
146
|
#
|
@@ -294,7 +321,7 @@ module RSpec
|
|
294
321
|
# @param field [Hash] Parameter definition
|
295
322
|
#
|
296
323
|
# @return [Hash] Completed parameter
|
297
|
-
def fill_request_param(field)
|
324
|
+
def fill_request_param(field) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
298
325
|
if field[:type] == :object && field[:attributes]
|
299
326
|
organize_params field[:attributes]
|
300
327
|
else
|
@@ -302,6 +329,7 @@ module RSpec
|
|
302
329
|
type: PARAM_TYPES[field[:type]][:type],
|
303
330
|
description: field[:description] || nil,
|
304
331
|
}
|
332
|
+
properties[:format] = PARAM_TYPES[field[:type]][:format] if PARAM_TYPES[field[:type]][:format]
|
305
333
|
|
306
334
|
properties[:items] = organize_params field[:of] if field[:type] == :array && field[:of]
|
307
335
|
properties
|
@@ -15,16 +15,36 @@ module RSpec
|
|
15
15
|
# ```
|
16
16
|
class OpenApiRenderer # rubocop:disable Metrics/ClassLength
|
17
17
|
attr_writer :api_servers, :api_title, :api_version, :api_description, :api_tos
|
18
|
+
attr_reader :redactables
|
18
19
|
|
19
20
|
def initialize
|
20
|
-
@metadata
|
21
|
-
@api_infos
|
22
|
-
@api_servers
|
23
|
-
@api_paths
|
21
|
+
@metadata = { resources: {}, entities: {} }
|
22
|
+
@api_infos = {}
|
23
|
+
@api_servers = []
|
24
|
+
@api_paths = {}
|
24
25
|
@api_components = {}
|
25
26
|
@api_tags = []
|
26
27
|
@api_contact = {}
|
27
28
|
@api_license = {}
|
29
|
+
@api_security = {}
|
30
|
+
@redactables = {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def redact_responses(pairs)
|
34
|
+
@redactables = pairs
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Adds a security scheme definition to the API documentation
|
39
|
+
#
|
40
|
+
# @param reference [Symbol] Reference to use in the tests
|
41
|
+
# @param name [String] Human friendly name
|
42
|
+
# @param definition [Hash] Security scheme definition as per https://swagger.io/specification/#security-scheme-object
|
43
|
+
def add_security_scheme(reference, name, definition)
|
44
|
+
raise "Security scheme #{reference} is already defined" if @api_security.key? reference
|
45
|
+
|
46
|
+
definition[:name] = name
|
47
|
+
@api_security[reference] = definition
|
28
48
|
end
|
29
49
|
|
30
50
|
##
|
@@ -36,7 +56,6 @@ module RSpec
|
|
36
56
|
# @return [void
|
37
57
|
def merge_context(context, dump_metadata: false)
|
38
58
|
@metadata[:resources].deep_merge! context.respond_to?(:resources) ? context.resources : context[:resources]
|
39
|
-
@metadata[:entities].deep_merge! context.respond_to?(:entities) ? context.entities : context[:entities]
|
40
59
|
|
41
60
|
# Save context for debug and fixtures
|
42
61
|
File.write ::Rails.root.join('tmp', 'rra_metadata.yaml'), @metadata.to_yaml if dump_metadata
|
@@ -54,11 +73,16 @@ module RSpec
|
|
54
73
|
|
55
74
|
path ||= ::Rails.root.join('tmp', 'rspec_api_rails')
|
56
75
|
|
76
|
+
metadata = prepare_metadata
|
77
|
+
|
57
78
|
file_types = %i[yaml json]
|
58
79
|
only.each do |type|
|
59
80
|
next unless file_types.include? type
|
60
81
|
|
61
|
-
|
82
|
+
data = metadata.to_yaml if type == :yaml
|
83
|
+
data = JSON.pretty_generate(metadata) if type == :json
|
84
|
+
|
85
|
+
File.write "#{path}.#{type}", data
|
62
86
|
end
|
63
87
|
end
|
64
88
|
|
@@ -77,7 +101,7 @@ module RSpec
|
|
77
101
|
components: @api_components,
|
78
102
|
tags: @api_tags,
|
79
103
|
}
|
80
|
-
JSON.parse(
|
104
|
+
JSON.parse(hash.to_json)
|
81
105
|
end
|
82
106
|
|
83
107
|
##
|
@@ -125,24 +149,33 @@ module RSpec
|
|
125
149
|
#
|
126
150
|
# @return [void]
|
127
151
|
def extract_metadata
|
128
|
-
|
152
|
+
extract_security
|
129
153
|
api_infos
|
130
154
|
api_servers
|
155
|
+
global_entities
|
156
|
+
extract_from_resources
|
157
|
+
end
|
158
|
+
|
159
|
+
##
|
160
|
+
# Extracts metadata from security schemes for rendering
|
161
|
+
#
|
162
|
+
# @return [void]
|
163
|
+
def extract_security
|
164
|
+
return unless @api_security.keys.count.positive?
|
165
|
+
|
166
|
+
@api_components['securitySchemes'] = @api_security
|
131
167
|
end
|
132
168
|
|
133
169
|
##
|
134
170
|
# Extracts metadata from resources for rendering
|
135
171
|
#
|
136
172
|
# @return [void]
|
137
|
-
def extract_from_resources
|
173
|
+
def extract_from_resources
|
138
174
|
@api_components[:schemas] ||= {}
|
139
175
|
@metadata[:resources].each do |resource_key, resource|
|
140
|
-
resource[:entities].each do |name, entity|
|
141
|
-
@api_components[:schemas][name] = process_entity(entity)
|
142
|
-
end
|
143
176
|
@api_tags.push(
|
144
177
|
name: resource_key.to_s,
|
145
|
-
description: resource[:description]
|
178
|
+
description: resource[:description].presence&.strip || ''
|
146
179
|
)
|
147
180
|
process_resource resource: resource_key, resource_config: resource
|
148
181
|
end
|
@@ -190,22 +223,29 @@ module RSpec
|
|
190
223
|
parameters
|
191
224
|
end
|
192
225
|
|
193
|
-
def process_entity(entity) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
226
|
+
def process_entity(entity) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
194
227
|
schema = {
|
195
228
|
properties: {},
|
196
229
|
}
|
197
230
|
required = []
|
198
231
|
entity.fields.each do |name, field|
|
199
232
|
property = {
|
200
|
-
description: field.description,
|
233
|
+
description: field.description.presence&.strip || '',
|
201
234
|
type: PARAM_TYPES[field.type][:type],
|
202
235
|
}
|
203
|
-
property[:format]
|
204
|
-
|
205
|
-
|
206
|
-
|
236
|
+
property[:format] = PARAM_TYPES[field.type][:format] if PARAM_TYPES[field.type][:format]
|
237
|
+
|
238
|
+
if PRIMITIVES.include? field.attributes
|
239
|
+
property[:items] = { type: field.attributes }
|
240
|
+
elsif field.type == :object && field.attributes.is_a?(Symbol)
|
241
|
+
property = { '$ref' => "#/components/schemas/#{field.attributes}" }
|
242
|
+
elsif field.type == :array && field.attributes.is_a?(Symbol)
|
243
|
+
property = { type: :array, items: { '$ref' => "#/components/schemas/#{field.attributes}" } }
|
244
|
+
end
|
207
245
|
|
208
246
|
required.push name unless field.required == false
|
247
|
+
|
248
|
+
schema[:properties][name] = property
|
209
249
|
end
|
210
250
|
|
211
251
|
schema[:required] = required unless required.size.zero?
|
@@ -221,10 +261,10 @@ module RSpec
|
|
221
261
|
#
|
222
262
|
#
|
223
263
|
# @return [void]
|
224
|
-
def process_path_param(name, param) # rubocop:disable Metrics/MethodLength
|
264
|
+
def process_path_param(name, param) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
225
265
|
parameter = {
|
226
266
|
name: name.to_s,
|
227
|
-
description: param[:description],
|
267
|
+
description: param[:description].presence&.strip || '',
|
228
268
|
required: param[:required] || true,
|
229
269
|
in: param[:scope].to_s,
|
230
270
|
schema: {
|
@@ -250,7 +290,7 @@ module RSpec
|
|
250
290
|
#
|
251
291
|
# FIXME: Rename "action_config" to "action"
|
252
292
|
# FIXME: Rename "parameters" to "path_parameters"
|
253
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
293
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
254
294
|
def process_action(resource: nil, path: nil, path_config: nil, action_config: nil, parameters: nil)
|
255
295
|
responses = {}
|
256
296
|
request_body = nil
|
@@ -268,21 +308,31 @@ module RSpec
|
|
268
308
|
responses[status_key] = process_response status: status_key, status_config: status, content: content
|
269
309
|
end
|
270
310
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
operationId: "#{resource} #{summary}".downcase.gsub(/[^\w]/, '_'),
|
311
|
+
action = {
|
312
|
+
summary: path_config[:actions][action_config][:summary]&.strip || '',
|
313
|
+
description: path_config[:actions][action_config][:description].presence&.strip || '',
|
314
|
+
operationId: "#{resource} #{action_config} #{path}".downcase.gsub(/[^\w]/, '_'),
|
276
315
|
parameters: parameters,
|
277
316
|
responses: responses,
|
278
317
|
tags: [resource.to_s],
|
279
318
|
}
|
280
319
|
|
320
|
+
if path_config[:actions][action_config].key? :security
|
321
|
+
references = path_config[:actions][action_config][:security]
|
322
|
+
|
323
|
+
action[:security] = []
|
324
|
+
references.each do |reference|
|
325
|
+
raise "No security scheme defined with reference #{reference}" unless @api_security.key? reference
|
326
|
+
|
327
|
+
action[:security].push({ reference => [] })
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
281
331
|
action[:requestBody] = request_body if request_body
|
282
332
|
|
283
333
|
action
|
284
334
|
end
|
285
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
335
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
286
336
|
|
287
337
|
##
|
288
338
|
# Processes a request body from metadata
|
@@ -294,11 +344,12 @@ module RSpec
|
|
294
344
|
# @return [void]
|
295
345
|
def process_request_body(schema: nil, ref: nil, examples: {})
|
296
346
|
Utils.deep_set @api_components, ['schemas', ref], schema
|
347
|
+
|
297
348
|
{
|
298
349
|
# description: '',
|
299
350
|
required: true,
|
300
351
|
content: {
|
301
|
-
|
352
|
+
content_type_from_schema(schema) => {
|
302
353
|
schema: { '$ref' => "#/components/schemas/#{ref}" },
|
303
354
|
examples: examples,
|
304
355
|
},
|
@@ -306,6 +357,23 @@ module RSpec
|
|
306
357
|
}
|
307
358
|
end
|
308
359
|
|
360
|
+
def content_type_from_schema(schema)
|
361
|
+
schema_includes_file?(schema) ? 'multipart/form-data' : 'application/json'
|
362
|
+
end
|
363
|
+
|
364
|
+
def schema_includes_file?(schema)
|
365
|
+
return true if schema[:type] == 'string' && schema[:format] == 'binary'
|
366
|
+
return false unless schema[:properties].is_a?(Hash) && schema[:required].is_a?(Array)
|
367
|
+
|
368
|
+
schema[:properties].each_value do |definition|
|
369
|
+
next unless schema_includes_file?(definition)
|
370
|
+
|
371
|
+
return true
|
372
|
+
end
|
373
|
+
|
374
|
+
false
|
375
|
+
end
|
376
|
+
|
309
377
|
##
|
310
378
|
# Process a response from metadata
|
311
379
|
#
|
@@ -314,22 +382,45 @@ module RSpec
|
|
314
382
|
# @param content [String] Response content
|
315
383
|
#
|
316
384
|
# @return [void]
|
317
|
-
def process_response(status: nil, status_config: nil, content: nil)
|
318
|
-
response = { description: status_config[:description] }
|
385
|
+
def process_response(status: nil, status_config: nil, content: nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
386
|
+
response = { description: status_config[:description].presence&.strip || '' }
|
319
387
|
|
320
388
|
return response if status.to_s == '204' && content # No content
|
321
389
|
|
390
|
+
data = begin
|
391
|
+
JSON.parse(content)
|
392
|
+
rescue JSON::ParserError, TypeError
|
393
|
+
content
|
394
|
+
end
|
395
|
+
|
396
|
+
entity = status_config[:expectations][:one] || status_config[:expectations][:many]
|
397
|
+
|
398
|
+
# TODO: handle sub-entities
|
399
|
+
if @redactables.key?(entity) && data.is_a?(Hash)
|
400
|
+
if status_config[:expectations][:one]
|
401
|
+
@redactables[entity].each_pair do |attribute, replacement|
|
402
|
+
data[attribute.to_s] = replacement
|
403
|
+
end
|
404
|
+
else
|
405
|
+
data.each_index do |index|
|
406
|
+
@redactables[entity].each_pair do |attribute, replacement|
|
407
|
+
data[index][attribute.to_s] = replacement
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
322
413
|
response[:content] = {
|
323
414
|
'application/json': {
|
324
415
|
schema: response_schema(status_config[:expectations]),
|
325
|
-
examples: { default: { value:
|
416
|
+
examples: { default: { value: data } },
|
326
417
|
},
|
327
418
|
}
|
328
419
|
|
329
420
|
response
|
330
421
|
end
|
331
422
|
|
332
|
-
def response_schema(expectations)
|
423
|
+
def response_schema(expectations) # rubocop:disable Metrics/MethodLength
|
333
424
|
if expectations[:many]
|
334
425
|
items = if PRIMITIVES.include?(expectations[:many])
|
335
426
|
{ type: expectations[:many] }
|
@@ -338,7 +429,11 @@ module RSpec
|
|
338
429
|
end
|
339
430
|
{ type: 'array', items: items }
|
340
431
|
elsif expectations[:one]
|
341
|
-
|
432
|
+
if PRIMITIVES.include?(expectations[:one])
|
433
|
+
{ type: expectations[:one] }
|
434
|
+
else
|
435
|
+
{ '$ref' => "#/components/schemas/#{expectations[:one]}" }
|
436
|
+
end
|
342
437
|
end
|
343
438
|
end
|
344
439
|
|
@@ -361,6 +456,16 @@ module RSpec
|
|
361
456
|
request_examples
|
362
457
|
end
|
363
458
|
|
459
|
+
def global_entities
|
460
|
+
return if RSpec::Rails::Api::Metadata.entities.keys.count.zero?
|
461
|
+
|
462
|
+
@api_components[:schemas] = {}
|
463
|
+
|
464
|
+
RSpec::Rails::Api::Metadata.entities.each_pair do |name, entity|
|
465
|
+
@api_components[:schemas][name] = process_entity(entity)
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
364
469
|
##
|
365
470
|
# Converts path with params like ":id" to their OpenAPI representation
|
366
471
|
#
|
@@ -387,12 +492,12 @@ module RSpec
|
|
387
492
|
# Fills the API general information sections
|
388
493
|
#
|
389
494
|
# @return [void]
|
390
|
-
def api_infos
|
495
|
+
def api_infos # rubocop:disable Metrics/CyclomaticComplexity
|
391
496
|
@api_infos = {
|
392
497
|
title: @api_title || 'Some sample app',
|
393
498
|
version: @api_version || '1.0',
|
394
499
|
}
|
395
|
-
@api_infos[:description] = @api_description if @api_description
|
500
|
+
@api_infos[:description] = @api_description.strip || '' if @api_description.present?
|
396
501
|
@api_infos[:termsOfService] = @api_tos if @api_tos
|
397
502
|
@api_infos[:contact] = @api_contact if @api_contact[:name]
|
398
503
|
@api_infos[:license] = @api_license if @api_license[:name]
|
data/lib/rspec_rails_api.rb
CHANGED
@@ -30,9 +30,10 @@ module RSpec
|
|
30
30
|
boolean: { type: 'boolean', format: nil },
|
31
31
|
string: { type: 'string', format: nil, class: String },
|
32
32
|
integer: { type: 'integer', format: nil, class: Integer },
|
33
|
-
number: { type: 'number', format: nil, class:
|
33
|
+
number: { type: 'number', format: nil, class: Numeric },
|
34
34
|
array: { type: 'array', format: nil, class: Array },
|
35
35
|
object: { type: 'object', format: nil, class: Hash },
|
36
|
+
file: { type: 'string', format: 'binary' },
|
36
37
|
}.freeze
|
37
38
|
|
38
39
|
PRIMITIVES = PARAM_TYPES.keys
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-rails-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Manuel Tancoigne
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|