rspec-rails-api 0.2.2 → 0.3.3
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/.gitlab-ci.yml +1 -1
- data/.rubocop.yml +8 -6
- data/CHANGELOG.md +20 -0
- data/README.md +13 -1
- data/lib/rspec/rails/api/dsl/example.rb +9 -5
- data/lib/rspec/rails/api/dsl/example_group.rb +4 -4
- data/lib/rspec/rails/api/field_config.rb +4 -3
- data/lib/rspec/rails/api/metadata.rb +11 -21
- data/lib/rspec/rails/api/open_api_renderer.rb +32 -15
- data/lib/rspec/rails/api/utils.rb +2 -2
- data/lib/rspec/rails/api/version.rb +1 -1
- data/rspec-rails-api.gemspec +3 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c254dadb291d2de68f549efc3c26407a9bf67b23058d495af7543534ee6b5dd
|
4
|
+
data.tar.gz: f67dedb6a9b0336fade718fd4249781a7db541f22017ae793f6ccc449d7fdd27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 988cdf3140fa6de3fe5f290ba43114a52e9037e158b8d6acec65c850d86cf4563b7c5870b2137424ffaeaaf887d4c368f1bfacc448363ee31984e17de6594b95
|
7
|
+
data.tar.gz: 81945d8559b1b1ff3bcd0de08542eead1ad1b30c3fd8d9472cd89ac128e22afa1b385abed2e54daf636c19bb220224842c0e1dc560aa9959f6256f11545505b7
|
data/.gitlab-ci.yml
CHANGED
data/.rubocop.yml
CHANGED
@@ -3,25 +3,27 @@ require:
|
|
3
3
|
- rubocop-performance
|
4
4
|
|
5
5
|
AllCops:
|
6
|
+
TargetRubyVersion: 2.5
|
6
7
|
Exclude:
|
7
8
|
- dummy/**/*
|
8
9
|
- vendor/bundle/**/*
|
10
|
+
NewCops: enable
|
9
11
|
|
10
|
-
|
12
|
+
Layout/LineLength:
|
13
|
+
Max: 120
|
11
14
|
Exclude:
|
12
|
-
- rspec-rails-api.gemspec
|
13
15
|
- spec/**/*_spec.rb
|
14
16
|
|
15
|
-
Metrics/
|
16
|
-
Max: 120
|
17
|
+
Metrics/BlockLength:
|
17
18
|
Exclude:
|
19
|
+
- rspec-rails-api.gemspec
|
18
20
|
- spec/**/*_spec.rb
|
19
21
|
|
20
|
-
Naming/
|
22
|
+
Naming/MethodParameterName:
|
21
23
|
AllowedNames:
|
22
24
|
- of
|
23
25
|
|
24
|
-
Layout/
|
26
|
+
Layout/HashAlignment:
|
25
27
|
EnforcedColonStyle: table
|
26
28
|
EnforcedHashRocketStyle: table
|
27
29
|
|
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file.
|
|
3
3
|
|
4
4
|
## Not released
|
5
5
|
|
6
|
+
## 0.3.2 - 2021-03-09
|
7
|
+
- Fix YAML rendering (ruby objects were sometimes rendered in documentation)
|
8
|
+
- Render examples results as YAML/JSON objects instead of text blocks.
|
9
|
+
|
10
|
+
## 0.3.1 - 2020-04-09
|
11
|
+
- Add support for "test only" examples, allowing to write examples without documentation.
|
12
|
+
|
13
|
+
## 0.3.0 - 2019-12-26
|
14
|
+
|
15
|
+
### Changed
|
16
|
+
- Rails 6 support, deprecated methods from Rails 5 are not supported. Use version `0.2.3`
|
17
|
+
of this gem if your application is still on 5.
|
18
|
+
|
19
|
+
## 0.2.3 - 2019-12-04
|
20
|
+
|
21
|
+
### Improved
|
22
|
+
|
23
|
+
- Generated Swagger file now use the payloads of POST/PATCH/PUT requests.
|
24
|
+
- Minimum Ruby version is now specified: Ruby 2.3.3.
|
25
|
+
|
6
26
|
## 0.2.1/0.2.2 - 2019-11-03
|
7
27
|
|
8
28
|
_Version 0.2.1 was released and yanked by mistake. Version 0.2.2 is the exact
|
data/README.md
CHANGED
@@ -6,6 +6,8 @@
|
|
6
6
|
**This is a work in progress** but you're welcome to help, test, submit
|
7
7
|
issues, ...
|
8
8
|
|
9
|
+
**Note** For Rails 5, use version 0.2.3
|
10
|
+
|
9
11
|
## Installation
|
10
12
|
|
11
13
|
Add this line to your application's Gemfile:
|
@@ -378,7 +380,7 @@ on_post '/api/items' do
|
|
378
380
|
end
|
379
381
|
```
|
380
382
|
|
381
|
-
##### `for_code(http_status, description = nil, doc_only: false &block)`
|
383
|
+
##### `for_code(http_status, description = nil, doc_only: false, test_only: false &block)`
|
382
384
|
|
383
385
|
Describes the desired output for a precedently defined URL.
|
384
386
|
|
@@ -386,6 +388,9 @@ Block takes one required argument, that should be passed to `visit`.
|
|
386
388
|
This argument will contain the block context and allow `visit` to access
|
387
389
|
the metadatas.
|
388
390
|
|
391
|
+
You can have only one documented code per action/url, unless you use
|
392
|
+
`test_only`.
|
393
|
+
|
389
394
|
- `http_status` is an integer representing an
|
390
395
|
[HTTP status](https://httpstat.us/)
|
391
396
|
- `description` should be some valid
|
@@ -393,6 +398,8 @@ the metadatas.
|
|
393
398
|
translation of the `http_status` will be used.
|
394
399
|
- `doc_only` can be set to true to temporarily disable block execution
|
395
400
|
and only create the documentation (without examples).
|
401
|
+
- `test_only` will omit the test from the documentation. Useful when you
|
402
|
+
need to test things _around_ the call (response content, db,...)
|
396
403
|
- `block` where additional tests can be performed. If `visit()` is
|
397
404
|
called within the block, its output will be used in documentation
|
398
405
|
examples, and the response type and code will actually be tested.
|
@@ -410,6 +417,11 @@ Once again, you have to pass an argument to the block if you use
|
|
410
417
|
visit url
|
411
418
|
# ...
|
412
419
|
end
|
420
|
+
|
421
|
+
for_code 200, 'Side test', test_only: true do |url|
|
422
|
+
visit url
|
423
|
+
# ...
|
424
|
+
end
|
413
425
|
# ...
|
414
426
|
```
|
415
427
|
|
@@ -6,11 +6,13 @@ module RSpec
|
|
6
6
|
module DSL
|
7
7
|
# These methods will be available in examples (i.e.: 'for_code')
|
8
8
|
module Example
|
9
|
-
def visit(example, path_params: {}, payload: {}, headers: {}) # rubocop:disable Metrics/AbcSize
|
9
|
+
def visit(example, path_params: {}, payload: {}, headers: {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
10
10
|
raise 'Missing context. Call visit with for_code context.' unless example
|
11
11
|
|
12
|
-
status_code
|
13
|
-
|
12
|
+
status_code = prepare_status_code example.class.description
|
13
|
+
|
14
|
+
request_params = prepare_request_params example.class.module_parent.description,
|
15
|
+
path_params, payload, headers
|
14
16
|
|
15
17
|
send(request_params[:action],
|
16
18
|
request_params[:url],
|
@@ -19,6 +21,8 @@ module RSpec
|
|
19
21
|
|
20
22
|
check_response(response, status_code)
|
21
23
|
|
24
|
+
return if example.class.description.match?(/-> test (\d+)(.*)/)
|
25
|
+
|
22
26
|
set_request_example example.class.metadata[:rrad], request_params, status_code, response.body
|
23
27
|
end
|
24
28
|
|
@@ -51,7 +55,7 @@ module RSpec
|
|
51
55
|
end
|
52
56
|
|
53
57
|
def prepare_request_params(description, request_params = {}, payload = {}, request_headers = {})
|
54
|
-
example_params = description.split
|
58
|
+
example_params = description.split
|
55
59
|
|
56
60
|
{
|
57
61
|
action: example_params[0].downcase,
|
@@ -84,7 +88,7 @@ module RSpec
|
|
84
88
|
end
|
85
89
|
|
86
90
|
def prepare_status_code(description)
|
87
|
-
code_match = /-> (\d+) - .*/.match description
|
91
|
+
code_match = /->(?: test)? (\d+) - .*/.match description
|
88
92
|
|
89
93
|
raise 'Please provide a numerical code for the "for_code" block' unless code_match
|
90
94
|
|
@@ -74,12 +74,12 @@ module RSpec
|
|
74
74
|
describe("#{action.upcase} #{url}", &block)
|
75
75
|
end
|
76
76
|
|
77
|
-
def for_code(status_code, description = nil, doc_only: false, &block)
|
77
|
+
def for_code(status_code, description = nil, doc_only: false, test_only: false, &block)
|
78
78
|
description ||= Rack::Utils::HTTP_STATUS_CODES[status_code]
|
79
79
|
|
80
|
-
metadata[:rrad].add_status_code(status_code, description)
|
80
|
+
metadata[:rrad].add_status_code(status_code, description) unless test_only
|
81
81
|
|
82
|
-
describe "
|
82
|
+
describe "->#{test_only ? ' test' : ''} #{status_code} - #{description}" do
|
83
83
|
execute_for_code_block(status_code, doc_only, block)
|
84
84
|
end
|
85
85
|
end
|
@@ -89,7 +89,7 @@ module RSpec
|
|
89
89
|
def document_only(status_code)
|
90
90
|
example 'Create documentation' do |example|
|
91
91
|
parent_example = example.example_group
|
92
|
-
request_params = prepare_request_params parent_example.
|
92
|
+
request_params = prepare_request_params parent_example.module_parent.description
|
93
93
|
|
94
94
|
set_request_example parent_example.metadata[:rrad], request_params, status_code
|
95
95
|
end
|
@@ -11,7 +11,7 @@ module RSpec
|
|
11
11
|
class FieldConfig
|
12
12
|
attr_accessor :required, :type, :attributes, :description
|
13
13
|
|
14
|
-
def initialize(type:, required: true,
|
14
|
+
def initialize(type:, description:, required: true, attributes: nil, of: nil)
|
15
15
|
@required = required
|
16
16
|
@description = description
|
17
17
|
raise "Field type not allowed: '#{type}'" unless Utils.check_attribute_type(type)
|
@@ -40,9 +40,10 @@ module RSpec
|
|
40
40
|
private
|
41
41
|
|
42
42
|
def define_attributes(attributes)
|
43
|
-
@attributes =
|
43
|
+
@attributes = case attributes
|
44
|
+
when Hash
|
44
45
|
@attributes = EntityConfig.new attributes
|
45
|
-
|
46
|
+
when Symbol
|
46
47
|
attributes
|
47
48
|
end
|
48
49
|
end
|
@@ -87,7 +87,7 @@ module RSpec
|
|
87
87
|
@current_method = method
|
88
88
|
end
|
89
89
|
|
90
|
-
# rubocop:disable
|
90
|
+
# rubocop:disable Layout/LineLength
|
91
91
|
def add_status_code(status_code, description)
|
92
92
|
check_current_context :resource, :url, :method
|
93
93
|
|
@@ -97,7 +97,7 @@ module RSpec
|
|
97
97
|
example: { response: nil })
|
98
98
|
@current_code = status_code
|
99
99
|
end
|
100
|
-
# rubocop:enable
|
100
|
+
# rubocop:enable Layout/LineLength
|
101
101
|
|
102
102
|
# rubocop:disable Metrics/ParameterLists
|
103
103
|
def add_request_example(url: nil, action: nil, status_code: nil, response: nil, path_params: nil, params: nil)
|
@@ -125,25 +125,14 @@ module RSpec
|
|
125
125
|
|
126
126
|
private
|
127
127
|
|
128
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
129
|
-
def check_current_context(*scope)
|
128
|
+
def check_current_context(*scope) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
130
129
|
scope ||= []
|
131
|
-
if scope.include?(:resource)
|
132
|
-
|
133
|
-
|
134
|
-
if scope.include?(:
|
135
|
-
raise 'No action declared' unless @current_method
|
136
|
-
end
|
137
|
-
if scope.include?(:url)
|
138
|
-
raise 'No url declared' unless @current_url
|
139
|
-
end
|
140
|
-
if scope.include?(:code)
|
141
|
-
raise 'No status code declared' unless @current_code
|
142
|
-
end
|
130
|
+
raise 'No resource declared' if scope.include?(:resource) && !@current_resource
|
131
|
+
raise 'No action declared' if scope.include?(:method) && !@current_method
|
132
|
+
raise 'No url declared' if scope.include?(:url) && !@current_url
|
133
|
+
raise 'No status code declared' if scope.include?(:code) && !@current_code
|
143
134
|
end
|
144
135
|
|
145
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength, Style/GuardClause
|
146
|
-
|
147
136
|
def path_param_scope(url_chunks, name)
|
148
137
|
if /:#{name}/.match?(url_chunks[0])
|
149
138
|
:path
|
@@ -154,11 +143,12 @@ module RSpec
|
|
154
143
|
end
|
155
144
|
end
|
156
145
|
|
157
|
-
def organize_params(fields) # rubocop:disable Metrics/AbcSize
|
146
|
+
def organize_params(fields) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
158
147
|
out = { properties: {} }
|
159
148
|
required = []
|
149
|
+
allowed_types = %i[array object]
|
160
150
|
fields.each do |name, field|
|
161
|
-
allowed_type =
|
151
|
+
allowed_type = allowed_types.include?(field[:type]) || PARAM_TYPES.key?(field[:type])
|
162
152
|
raise "Field type not allowed: #{field[:type]}" unless allowed_type
|
163
153
|
|
164
154
|
required.push name.to_s if field[:required]
|
@@ -174,7 +164,7 @@ module RSpec
|
|
174
164
|
organize_params field[:properties]
|
175
165
|
else
|
176
166
|
properties = {
|
177
|
-
type: field[:type]
|
167
|
+
type: PARAM_TYPES[field[:type]][:type],
|
178
168
|
description: field[:description] || nil,
|
179
169
|
}
|
180
170
|
|
@@ -39,10 +39,12 @@ module RSpec
|
|
39
39
|
content = prepare_metadata
|
40
40
|
path ||= ::Rails.root.join('tmp', 'rspec_api_rails')
|
41
41
|
|
42
|
+
file_types = %i[yaml json]
|
43
|
+
|
42
44
|
only.each do |type|
|
43
|
-
next unless
|
45
|
+
next unless file_types.include? type
|
44
46
|
|
45
|
-
File.write "#{path}.#{type}", content.send("to_#{type}")
|
47
|
+
File.write "#{path}.#{type}", JSON.parse(JSON.pretty_generate(content)).send("to_#{type}")
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
@@ -89,13 +91,14 @@ module RSpec
|
|
89
91
|
end
|
90
92
|
|
91
93
|
def process_resource(resource: nil, resource_config: nil) # rubocop:disable Metrics/MethodLength
|
94
|
+
http_verbs = %i[get post put patch delete]
|
92
95
|
resource_config[:paths].each do |path_key, path|
|
93
96
|
url = path_with_params path_key.to_s
|
94
97
|
actions = {}
|
95
98
|
parameters = path.key?(:path_params) ? process_path_params(path[:path_params]) : []
|
96
99
|
|
97
100
|
path[:actions].each_key do |action|
|
98
|
-
next unless
|
101
|
+
next unless http_verbs.include? action
|
99
102
|
|
100
103
|
actions[action] = process_action resource: resource,
|
101
104
|
path: path_key,
|
@@ -117,7 +120,7 @@ module RSpec
|
|
117
120
|
parameters
|
118
121
|
end
|
119
122
|
|
120
|
-
def process_path_param(name, param) # rubocop:disable Metrics/MethodLength
|
123
|
+
def process_path_param(name, param) # rubocop:disable Metrics/MethodLength
|
121
124
|
parameter = {
|
122
125
|
name: name.to_s,
|
123
126
|
description: param[:description],
|
@@ -138,12 +141,12 @@ module RSpec
|
|
138
141
|
responses = {}
|
139
142
|
request_body = nil
|
140
143
|
|
141
|
-
if %i[post put
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
144
|
+
if %i[post put
|
145
|
+
patch].include?(action_config) && path_config[:actions][action_config][:params].keys.count.positive?
|
146
|
+
schema = path_config[:actions][action_config][:params]
|
147
|
+
schema_ref = escape_operation_id("#{action_config}_#{path}")
|
148
|
+
examples = process_examples(path_config[:actions][action_config][:statuses])
|
149
|
+
request_body = process_request_body schema: schema, ref: schema_ref, examples: examples
|
147
150
|
end
|
148
151
|
|
149
152
|
path_config[:actions][action_config][:statuses].each do |status_key, status|
|
@@ -166,14 +169,15 @@ module RSpec
|
|
166
169
|
end
|
167
170
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
168
171
|
|
169
|
-
def process_request_body(schema: nil, ref: nil)
|
172
|
+
def process_request_body(schema: nil, ref: nil, examples: {})
|
170
173
|
Utils.deep_set @api_components, "schemas.#{ref}", schema
|
171
174
|
{
|
172
175
|
# description: '',
|
173
176
|
required: true,
|
174
177
|
content: {
|
175
178
|
'application/json' => {
|
176
|
-
schema:
|
179
|
+
schema: { '$ref' => "#/components/schemas/#{ref}" },
|
180
|
+
examples: examples,
|
177
181
|
},
|
178
182
|
},
|
179
183
|
}
|
@@ -184,17 +188,30 @@ module RSpec
|
|
184
188
|
description: status_config[:description],
|
185
189
|
}
|
186
190
|
|
187
|
-
return response
|
191
|
+
return response if status.to_s == '204' && content # No content
|
188
192
|
|
189
193
|
response[:content] = {
|
190
194
|
'application/json': {
|
191
|
-
examples: { default: { value: JSON.
|
195
|
+
examples: { default: { value: JSON.parse(content) } },
|
192
196
|
},
|
193
197
|
}
|
194
198
|
|
195
199
|
response
|
196
200
|
end
|
197
201
|
|
202
|
+
def process_examples(statuses)
|
203
|
+
request_examples = {}
|
204
|
+
|
205
|
+
statuses.each do |code, request|
|
206
|
+
request_examples[code] = {
|
207
|
+
summary: "Example for a #{code} code",
|
208
|
+
value: request[:example][:params],
|
209
|
+
}
|
210
|
+
end
|
211
|
+
|
212
|
+
request_examples
|
213
|
+
end
|
214
|
+
|
198
215
|
def path_with_params(string)
|
199
216
|
string.gsub(/(?::(\w*))/) do |e|
|
200
217
|
"{#{e.sub(':', '')}}"
|
@@ -205,7 +222,7 @@ module RSpec
|
|
205
222
|
string.downcase.gsub(/[^\w]+/, '_')
|
206
223
|
end
|
207
224
|
|
208
|
-
def api_infos
|
225
|
+
def api_infos
|
209
226
|
@api_infos = {
|
210
227
|
title: @api_title || 'Some sample app',
|
211
228
|
version: @api_version || '1.0',
|
@@ -11,7 +11,7 @@ module RSpec
|
|
11
11
|
path.split('.').inject(hash) do |sub_hash, key|
|
12
12
|
return nil unless sub_hash.is_a?(Hash) && sub_hash.key?(key.to_sym)
|
13
13
|
|
14
|
-
sub_hash[key.to_sym]
|
14
|
+
sub_hash[key.to_sym] # rubocop:disable Lint/UnmodifiedReduceAccumulator
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
@@ -27,7 +27,7 @@ module RSpec
|
|
27
27
|
hash
|
28
28
|
end
|
29
29
|
|
30
|
-
def self.check_value_type(type, value)
|
30
|
+
def self.check_value_type(type, value)
|
31
31
|
return true if type == :boolean && (value.is_a?(TrueClass) || value.is_a?(FalseClass))
|
32
32
|
return true if type == :array && value.is_a?(Array)
|
33
33
|
|
data/rspec-rails-api.gemspec
CHANGED
@@ -32,7 +32,9 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
33
33
|
spec.require_paths = ['lib']
|
34
34
|
|
35
|
-
spec.
|
35
|
+
spec.required_ruby_version = '>= 2.5.0'
|
36
|
+
|
37
|
+
spec.add_development_dependency 'activesupport', '~> 6.0'
|
36
38
|
spec.add_development_dependency 'bundler', '~> 1.17'
|
37
39
|
spec.add_development_dependency 'byebug'
|
38
40
|
spec.add_development_dependency 'rake', '~> 10.0'
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
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.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Manuel Tancoigne
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '6.0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '6.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -170,7 +170,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
170
170
|
requirements:
|
171
171
|
- - ">="
|
172
172
|
- !ruby/object:Gem::Version
|
173
|
-
version:
|
173
|
+
version: 2.5.0
|
174
174
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
175
175
|
requirements:
|
176
176
|
- - ">="
|