rspec-openapi 0.18.4 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +2 -0
- data/README.md +7 -2
- data/lib/rspec/openapi/extractors/hanami.rb +13 -1
- data/lib/rspec/openapi/extractors/rack.rb +13 -1
- data/lib/rspec/openapi/extractors/rails.rb +14 -1
- data/lib/rspec/openapi/record.rb +1 -0
- data/lib/rspec/openapi/record_builder.rb +2 -1
- data/lib/rspec/openapi/schema_builder.rb +20 -12
- data/lib/rspec/openapi/schema_cleaner.rb +2 -2
- data/lib/rspec/openapi/version.rb +1 -1
- data/lib/rspec/openapi.rb +2 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0fd8968099cf455a6ee1eabe6039c60955020493d97e584fb57b1ba79aca8d3
|
4
|
+
data.tar.gz: 15ae08318ba0843a65cbd4bf418b82f2ce9d7c173600f0ea2d776763f4790819
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c15dc4a7d63f610cd654ea2718ffccbd15c59ec66bcc45f219352bdccbe7a6e49baa4810a866fefd483b34ec94992a7f48ce799d8cace7ced59f2dd5fb9db81
|
7
|
+
data.tar.gz: 9306ae8e95e9d4dcb0aa40d20b716217c2712ad081448c7f61cb832dd210864883c6f93999257715347fd85b5eb9f1d5ccb0615db3102de112fdfddebeb78318
|
data/.github/workflows/test.yml
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
# rspec-openapi [](https://rubygems.org/gems/rspec-openapi) [](https://github.com/exoego/rspec-openapi/actions/workflows/test.yml) [](https://codecov.io/gh/exoego/rspec-openapi) [](https://www.ruby-toolbox.com/projects/rspec-openapi)
|
1
|
+
# rspec-openapi [](https://rubygems.org/gems/rspec-openapi) [](https://github.com/exoego/rspec-openapi/actions/workflows/test.yml) [](https://codecov.io/gh/exoego/rspec-openapi) [](https://www.ruby-toolbox.com/projects/rspec-openapi) [](https://deepwiki.com/exoego/rspec-openapi)
|
2
|
+
|
2
3
|
|
3
4
|
Generate OpenAPI schema from RSpec request specs.
|
4
5
|
|
@@ -52,7 +53,7 @@ end
|
|
52
53
|
If you run the spec with `OPENAPI=1`,
|
53
54
|
|
54
55
|
```
|
55
|
-
OPENAPI=1 rspec spec/requests/tables_spec.rb
|
56
|
+
OPENAPI=1 bundle exec rspec spec/requests/tables_spec.rb
|
56
57
|
```
|
57
58
|
|
58
59
|
It will generate [`doc/openapi.yaml` file](./spec/rails/doc/openapi.yaml) like:
|
@@ -187,6 +188,10 @@ RSpec::OpenAPI.summary_builder = ->(example) { example.metadata.dig(:example_gro
|
|
187
188
|
# This example uses the tags from the parent_example_group
|
188
189
|
RSpec::OpenAPI.tags_builder = -> (example) { example.metadata.dig(:example_group, :parent_example_group, :openapi, :tags) }
|
189
190
|
|
191
|
+
# Configure custom format for specific properties
|
192
|
+
# This example assigns 'date-time' format to properties with names ending in '_at'
|
193
|
+
RSpec::OpenAPI.formats_builder = ->(_example, key) { key.end_with?('_at') ? 'date-time' : nil }
|
194
|
+
|
190
195
|
# Change the example type(s) that will generate schema
|
191
196
|
RSpec::OpenAPI.example_types = %i[request]
|
192
197
|
|
@@ -59,6 +59,7 @@ class << RSpec::OpenAPI::Extractors::Hanami = Object.new
|
|
59
59
|
metadata = example.metadata[:openapi] || {}
|
60
60
|
summary = metadata[:summary] || RSpec::OpenAPI.summary_builder.call(example)
|
61
61
|
tags = metadata[:tags] || RSpec::OpenAPI.tags_builder.call(example)
|
62
|
+
formats = metadata[:formats] || RSpec::OpenAPI.formats_builder.curry.call(example)
|
62
63
|
operation_id = metadata[:operation_id]
|
63
64
|
required_request_params = metadata[:required_request_params] || []
|
64
65
|
security = metadata[:security]
|
@@ -76,7 +77,18 @@ class << RSpec::OpenAPI::Extractors::Hanami = Object.new
|
|
76
77
|
|
77
78
|
raw_path_params = raw_path_params.slice(*(raw_path_params.keys - RSpec::OpenAPI.ignored_path_params))
|
78
79
|
|
79
|
-
[
|
80
|
+
[
|
81
|
+
path,
|
82
|
+
summary,
|
83
|
+
tags,
|
84
|
+
operation_id,
|
85
|
+
required_request_params,
|
86
|
+
raw_path_params,
|
87
|
+
description,
|
88
|
+
security,
|
89
|
+
deprecated,
|
90
|
+
formats,
|
91
|
+
]
|
80
92
|
end
|
81
93
|
|
82
94
|
# @param [RSpec::ExampleGroups::*] context
|
@@ -9,6 +9,7 @@ class << RSpec::OpenAPI::Extractors::Rack = Object.new
|
|
9
9
|
metadata = example.metadata[:openapi] || {}
|
10
10
|
summary = metadata[:summary] || RSpec::OpenAPI.summary_builder.call(example)
|
11
11
|
tags = metadata[:tags] || RSpec::OpenAPI.tags_builder.call(example)
|
12
|
+
formats = metadata[:formats] || RSpec::OpenAPI.formats_builder.curry.call(example)
|
12
13
|
operation_id = metadata[:operation_id]
|
13
14
|
required_request_params = metadata[:required_request_params] || []
|
14
15
|
security = metadata[:security]
|
@@ -17,7 +18,18 @@ class << RSpec::OpenAPI::Extractors::Rack = Object.new
|
|
17
18
|
raw_path_params = request.path_parameters
|
18
19
|
path = request.path
|
19
20
|
summary ||= "#{request.method} #{path}"
|
20
|
-
[
|
21
|
+
[
|
22
|
+
path,
|
23
|
+
summary,
|
24
|
+
tags,
|
25
|
+
operation_id,
|
26
|
+
required_request_params,
|
27
|
+
raw_path_params,
|
28
|
+
description,
|
29
|
+
security,
|
30
|
+
deprecated,
|
31
|
+
formats,
|
32
|
+
]
|
21
33
|
end
|
22
34
|
|
23
35
|
# @param [RSpec::ExampleGroups::*] context
|
@@ -19,6 +19,8 @@ class << RSpec::OpenAPI::Extractors::Rails = Object.new
|
|
19
19
|
metadata = example.metadata[:openapi] || {}
|
20
20
|
summary = metadata[:summary] || RSpec::OpenAPI.summary_builder.call(example)
|
21
21
|
tags = metadata[:tags] || RSpec::OpenAPI.tags_builder.call(example)
|
22
|
+
formats = metadata[:formats] || RSpec::OpenAPI.formats_builder.curry.call(example)
|
23
|
+
|
22
24
|
operation_id = metadata[:operation_id]
|
23
25
|
required_request_params = metadata[:required_request_params] || []
|
24
26
|
security = metadata[:security]
|
@@ -34,7 +36,18 @@ class << RSpec::OpenAPI::Extractors::Rails = Object.new
|
|
34
36
|
|
35
37
|
summary ||= "#{request.method} #{path}"
|
36
38
|
|
37
|
-
[
|
39
|
+
[
|
40
|
+
path,
|
41
|
+
summary,
|
42
|
+
tags,
|
43
|
+
operation_id,
|
44
|
+
required_request_params,
|
45
|
+
raw_path_params,
|
46
|
+
description,
|
47
|
+
security,
|
48
|
+
deprecated,
|
49
|
+
formats,
|
50
|
+
]
|
38
51
|
end
|
39
52
|
|
40
53
|
# @param [RSpec::ExampleGroups::*] context
|
data/lib/rspec/openapi/record.rb
CHANGED
@@ -12,6 +12,7 @@ RSpec::OpenAPI::Record = Struct.new(
|
|
12
12
|
:request_headers, # @param [Array] - [["header_key1", "header_value1"], ["header_key2", "header_value2"]]
|
13
13
|
:summary, # @param [String] - "v1/statuses #show"
|
14
14
|
:tags, # @param [Array] - ["Status"]
|
15
|
+
:formats, # @param [Proc] - ->(key) { key.end_with?('_at') ? 'date-time' : nil }
|
15
16
|
:operation_id, # @param [String] - "request-1234"
|
16
17
|
:description, # @param [String] - "returns a status"
|
17
18
|
:security, # @param [Array] - [{securityScheme1: []}]
|
@@ -12,7 +12,7 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
|
|
12
12
|
return if request.nil?
|
13
13
|
|
14
14
|
title = RSpec::OpenAPI.title.then { |t| t.is_a?(Proc) ? t.call(example) : t }
|
15
|
-
path, summary, tags, operation_id, required_request_params, raw_path_params, description, security, deprecated =
|
15
|
+
path, summary, tags, operation_id, required_request_params, raw_path_params, description, security, deprecated, formats =
|
16
16
|
extractor.request_attributes(request, example)
|
17
17
|
|
18
18
|
return if RSpec::OpenAPI.ignored_paths.any? { |ignored_path| path.match?(ignored_path) }
|
@@ -31,6 +31,7 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
|
|
31
31
|
request_headers: request_headers,
|
32
32
|
summary: summary,
|
33
33
|
tags: tags,
|
34
|
+
formats: formats,
|
34
35
|
operation_id: operation_id,
|
35
36
|
description: description,
|
36
37
|
security: security,
|
@@ -18,7 +18,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
18
18
|
if has_content
|
19
19
|
response[:content] = {
|
20
20
|
normalize_content_type(record.response_content_type) => {
|
21
|
-
schema: build_property(record.response_body, disposition: disposition),
|
21
|
+
schema: build_property(record.response_body, disposition: disposition, record: record),
|
22
22
|
example: response_example(record, disposition: disposition),
|
23
23
|
}.compact,
|
24
24
|
}
|
@@ -73,7 +73,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
73
73
|
name: build_parameter_name(key, value),
|
74
74
|
in: 'path',
|
75
75
|
required: true,
|
76
|
-
schema: build_property(try_cast(value)),
|
76
|
+
schema: build_property(try_cast(value), key: key, record: record),
|
77
77
|
example: (try_cast(value) if example_enabled?),
|
78
78
|
}.compact
|
79
79
|
end
|
@@ -83,7 +83,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
83
83
|
name: build_parameter_name(key, value),
|
84
84
|
in: 'query',
|
85
85
|
required: record.required_request_params.include?(key),
|
86
|
-
schema: build_property(try_cast(value)),
|
86
|
+
schema: build_property(try_cast(value), key: key, record: record),
|
87
87
|
example: (try_cast(value) if example_enabled?),
|
88
88
|
}.compact
|
89
89
|
end
|
@@ -93,7 +93,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
93
93
|
name: build_parameter_name(key, value),
|
94
94
|
in: 'header',
|
95
95
|
required: true,
|
96
|
-
schema: build_property(try_cast(value)),
|
96
|
+
schema: build_property(try_cast(value), key: key, record: record),
|
97
97
|
example: (try_cast(value) if example_enabled?),
|
98
98
|
}.compact
|
99
99
|
end
|
@@ -110,7 +110,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
110
110
|
|
111
111
|
record.response_headers.each do |key, value|
|
112
112
|
headers[key] = {
|
113
|
-
schema: build_property(try_cast(value)),
|
113
|
+
schema: build_property(try_cast(value), key: key, record: record),
|
114
114
|
}.compact
|
115
115
|
end
|
116
116
|
|
@@ -134,27 +134,29 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
134
134
|
{
|
135
135
|
content: {
|
136
136
|
normalize_content_type(record.request_content_type) => {
|
137
|
-
schema: build_property(record.request_params),
|
137
|
+
schema: build_property(record.request_params, record: record),
|
138
138
|
example: (build_example(record.request_params) if example_enabled?),
|
139
139
|
}.compact,
|
140
140
|
},
|
141
141
|
}
|
142
142
|
end
|
143
143
|
|
144
|
-
def build_property(value, disposition: nil)
|
145
|
-
|
144
|
+
def build_property(value, disposition: nil, key: nil, record: nil)
|
145
|
+
format = disposition ? 'binary' : infer_format(key, record)
|
146
|
+
|
147
|
+
property = build_type(value, format: format)
|
146
148
|
|
147
149
|
case value
|
148
150
|
when Array
|
149
151
|
property[:items] = if value.empty?
|
150
152
|
{} # unknown
|
151
153
|
else
|
152
|
-
build_property(value.first)
|
154
|
+
build_property(value.first, record: record)
|
153
155
|
end
|
154
156
|
when Hash
|
155
157
|
property[:properties] = {}.tap do |properties|
|
156
158
|
value.each do |key, v|
|
157
|
-
properties[key] = build_property(v)
|
159
|
+
properties[key] = build_property(v, record: record, key: key)
|
158
160
|
end
|
159
161
|
end
|
160
162
|
property = enrich_with_required_keys(property)
|
@@ -162,8 +164,8 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
162
164
|
property
|
163
165
|
end
|
164
166
|
|
165
|
-
def build_type(value,
|
166
|
-
return { type: 'string', format:
|
167
|
+
def build_type(value, format: nil)
|
168
|
+
return { type: 'string', format: format } if format
|
167
169
|
|
168
170
|
case value
|
169
171
|
when String
|
@@ -187,6 +189,12 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
|
|
187
189
|
end
|
188
190
|
end
|
189
191
|
|
192
|
+
def infer_format(key, record)
|
193
|
+
return nil if !key || !record || !record.formats
|
194
|
+
|
195
|
+
record.formats[key]
|
196
|
+
end
|
197
|
+
|
190
198
|
# Convert an always-String param to an appropriate type
|
191
199
|
def try_cast(value)
|
192
200
|
Integer(value)
|
@@ -50,7 +50,7 @@ class << RSpec::OpenAPI::SchemaCleaner = Object.new
|
|
50
50
|
parent_path_definition = base.dig(*path.take(path.length - 1))
|
51
51
|
|
52
52
|
security_schemes.each do |security_scheme_name, security_scheme|
|
53
|
-
|
53
|
+
remove_parameters_conflicting_with_security_scheme!(
|
54
54
|
parent_path_definition, security_scheme, security_scheme_name,
|
55
55
|
)
|
56
56
|
end
|
@@ -71,7 +71,7 @@ class << RSpec::OpenAPI::SchemaCleaner = Object.new
|
|
71
71
|
|
72
72
|
private
|
73
73
|
|
74
|
-
def
|
74
|
+
def remove_parameters_conflicting_with_security_scheme!(path_definition, security_scheme, security_scheme_name)
|
75
75
|
return unless path_definition[:security]
|
76
76
|
return unless path_definition[:parameters]
|
77
77
|
return unless path_definition.dig(:security, 0).keys.include?(security_scheme_name)
|
data/lib/rspec/openapi.rb
CHANGED
@@ -33,6 +33,7 @@ module RSpec::OpenAPI
|
|
33
33
|
@description_builder = ->(example) { example.description }
|
34
34
|
@summary_builder = ->(example) { example.metadata[:summary] }
|
35
35
|
@tags_builder = ->(example) { example.metadata[:tags] }
|
36
|
+
@formats_builder = ->(example) { example.metadata[:formats] }
|
36
37
|
@info = {}
|
37
38
|
@application_version = '1.0.0'
|
38
39
|
@request_headers = []
|
@@ -56,6 +57,7 @@ module RSpec::OpenAPI
|
|
56
57
|
:description_builder,
|
57
58
|
:summary_builder,
|
58
59
|
:tags_builder,
|
60
|
+
:formats_builder,
|
59
61
|
:info,
|
60
62
|
:application_version,
|
61
63
|
:request_headers,
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-openapi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.19.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Takashi Kokubun
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2025-
|
12
|
+
date: 2025-05-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: actionpack
|
@@ -109,7 +109,7 @@ licenses:
|
|
109
109
|
metadata:
|
110
110
|
homepage_uri: https://github.com/exoego/rspec-openapi
|
111
111
|
source_code_uri: https://github.com/exoego/rspec-openapi
|
112
|
-
changelog_uri: https://github.com/exoego/rspec-openapi/releases/tag/v0.
|
112
|
+
changelog_uri: https://github.com/exoego/rspec-openapi/releases/tag/v0.19.0
|
113
113
|
rubygems_mfa_required: 'true'
|
114
114
|
post_install_message:
|
115
115
|
rdoc_options: []
|
@@ -126,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
126
126
|
- !ruby/object:Gem::Version
|
127
127
|
version: '0'
|
128
128
|
requirements: []
|
129
|
-
rubygems_version: 3.4.
|
129
|
+
rubygems_version: 3.4.19
|
130
130
|
signing_key:
|
131
131
|
specification_version: 4
|
132
132
|
summary: Generate OpenAPI schema from RSpec request specs
|