openapi_first 1.4.2 → 2.0.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/CHANGELOG.md +47 -1
- data/README.md +105 -28
- data/lib/openapi_first/body_parser.rb +8 -11
- data/lib/openapi_first/builder.rb +81 -0
- data/lib/openapi_first/configuration.rb +24 -3
- data/lib/openapi_first/definition.rb +44 -100
- data/lib/openapi_first/error_response.rb +2 -2
- data/lib/openapi_first/error_responses/default.rb +73 -0
- data/lib/openapi_first/error_responses/jsonapi.rb +59 -0
- data/lib/openapi_first/errors.rb +26 -4
- data/lib/openapi_first/failure.rb +29 -26
- data/lib/openapi_first/json_refs.rb +1 -3
- data/lib/openapi_first/middlewares/request_validation.rb +2 -2
- data/lib/openapi_first/middlewares/response_validation.rb +4 -3
- data/lib/openapi_first/request.rb +92 -0
- data/lib/openapi_first/request_parser.rb +35 -0
- data/lib/openapi_first/request_validator.rb +25 -0
- data/lib/openapi_first/response.rb +57 -0
- data/lib/openapi_first/response_parser.rb +49 -0
- data/lib/openapi_first/response_validator.rb +27 -0
- data/lib/openapi_first/router/find_content.rb +17 -0
- data/lib/openapi_first/router/find_response.rb +45 -0
- data/lib/openapi_first/{definition → router}/path_template.rb +9 -1
- data/lib/openapi_first/router.rb +100 -0
- data/lib/openapi_first/schema/validation_error.rb +16 -10
- data/lib/openapi_first/schema/validation_result.rb +8 -6
- data/lib/openapi_first/schema.rb +4 -8
- data/lib/openapi_first/test/methods.rb +21 -0
- data/lib/openapi_first/test.rb +19 -0
- data/lib/openapi_first/validated_request.rb +81 -0
- data/lib/openapi_first/validated_response.rb +33 -0
- data/lib/openapi_first/validators/request_body.rb +39 -0
- data/lib/openapi_first/validators/request_parameters.rb +61 -0
- data/lib/openapi_first/validators/response_body.rb +30 -0
- data/lib/openapi_first/validators/response_headers.rb +25 -0
- data/lib/openapi_first/version.rb +1 -1
- data/lib/openapi_first.rb +40 -21
- metadata +35 -24
- data/lib/openapi_first/definition/operation.rb +0 -197
- data/lib/openapi_first/definition/path_item.rb +0 -40
- data/lib/openapi_first/definition/request_body.rb +0 -46
- data/lib/openapi_first/definition/response.rb +0 -32
- data/lib/openapi_first/definition/responses.rb +0 -87
- data/lib/openapi_first/plugins/default/error_response.rb +0 -74
- data/lib/openapi_first/plugins/default.rb +0 -11
- data/lib/openapi_first/plugins/jsonapi/error_response.rb +0 -60
- data/lib/openapi_first/plugins/jsonapi.rb +0 -11
- data/lib/openapi_first/plugins.rb +0 -25
- data/lib/openapi_first/request_validation/request_body_validator.rb +0 -41
- data/lib/openapi_first/request_validation/validator.rb +0 -82
- data/lib/openapi_first/response_validation/validator.rb +0 -98
- data/lib/openapi_first/runtime_request.rb +0 -166
- data/lib/openapi_first/runtime_response.rb +0 -124
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openapi_first
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Haller
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hana
|
@@ -28,16 +28,22 @@ dependencies:
|
|
28
28
|
name: json_schemer
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.1'
|
34
|
+
- - "<"
|
32
35
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
36
|
+
version: '3.0'
|
34
37
|
type: :runtime
|
35
38
|
prerelease: false
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
37
40
|
requirements:
|
38
|
-
- - "
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '2.1'
|
44
|
+
- - "<"
|
39
45
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
46
|
+
version: '3.0'
|
41
47
|
- !ruby/object:Gem::Dependency
|
42
48
|
name: multi_json
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -104,33 +110,38 @@ files:
|
|
104
110
|
- README.md
|
105
111
|
- lib/openapi_first.rb
|
106
112
|
- lib/openapi_first/body_parser.rb
|
113
|
+
- lib/openapi_first/builder.rb
|
107
114
|
- lib/openapi_first/configuration.rb
|
108
115
|
- lib/openapi_first/definition.rb
|
109
|
-
- lib/openapi_first/definition/operation.rb
|
110
|
-
- lib/openapi_first/definition/path_item.rb
|
111
|
-
- lib/openapi_first/definition/path_template.rb
|
112
|
-
- lib/openapi_first/definition/request_body.rb
|
113
|
-
- lib/openapi_first/definition/response.rb
|
114
|
-
- lib/openapi_first/definition/responses.rb
|
115
116
|
- lib/openapi_first/error_response.rb
|
117
|
+
- lib/openapi_first/error_responses/default.rb
|
118
|
+
- lib/openapi_first/error_responses/jsonapi.rb
|
116
119
|
- lib/openapi_first/errors.rb
|
117
120
|
- lib/openapi_first/failure.rb
|
118
121
|
- lib/openapi_first/json_refs.rb
|
119
122
|
- lib/openapi_first/middlewares/request_validation.rb
|
120
123
|
- lib/openapi_first/middlewares/response_validation.rb
|
121
|
-
- lib/openapi_first/
|
122
|
-
- lib/openapi_first/
|
123
|
-
- lib/openapi_first/
|
124
|
-
- lib/openapi_first/
|
125
|
-
- lib/openapi_first/
|
126
|
-
- lib/openapi_first/
|
127
|
-
- lib/openapi_first/
|
128
|
-
- lib/openapi_first/
|
129
|
-
- lib/openapi_first/
|
130
|
-
- lib/openapi_first/
|
124
|
+
- lib/openapi_first/request.rb
|
125
|
+
- lib/openapi_first/request_parser.rb
|
126
|
+
- lib/openapi_first/request_validator.rb
|
127
|
+
- lib/openapi_first/response.rb
|
128
|
+
- lib/openapi_first/response_parser.rb
|
129
|
+
- lib/openapi_first/response_validator.rb
|
130
|
+
- lib/openapi_first/router.rb
|
131
|
+
- lib/openapi_first/router/find_content.rb
|
132
|
+
- lib/openapi_first/router/find_response.rb
|
133
|
+
- lib/openapi_first/router/path_template.rb
|
131
134
|
- lib/openapi_first/schema.rb
|
132
135
|
- lib/openapi_first/schema/validation_error.rb
|
133
136
|
- lib/openapi_first/schema/validation_result.rb
|
137
|
+
- lib/openapi_first/test.rb
|
138
|
+
- lib/openapi_first/test/methods.rb
|
139
|
+
- lib/openapi_first/validated_request.rb
|
140
|
+
- lib/openapi_first/validated_response.rb
|
141
|
+
- lib/openapi_first/validators/request_body.rb
|
142
|
+
- lib/openapi_first/validators/request_parameters.rb
|
143
|
+
- lib/openapi_first/validators/response_body.rb
|
144
|
+
- lib/openapi_first/validators/response_headers.rb
|
134
145
|
- lib/openapi_first/version.rb
|
135
146
|
homepage: https://github.com/ahx/openapi_first
|
136
147
|
licenses:
|
@@ -149,14 +160,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
149
160
|
requirements:
|
150
161
|
- - ">="
|
151
162
|
- !ruby/object:Gem::Version
|
152
|
-
version: 3.
|
163
|
+
version: 3.2.0
|
153
164
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
165
|
requirements:
|
155
166
|
- - ">="
|
156
167
|
- !ruby/object:Gem::Version
|
157
168
|
version: '0'
|
158
169
|
requirements: []
|
159
|
-
rubygems_version: 3.5.
|
170
|
+
rubygems_version: 3.5.11
|
160
171
|
signing_key:
|
161
172
|
specification_version: 4
|
162
173
|
summary: Implement HTTP APIs based on OpenApi 3.x
|
@@ -1,197 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'forwardable'
|
4
|
-
require 'set'
|
5
|
-
require 'openapi_parameters'
|
6
|
-
require_relative 'request_body'
|
7
|
-
require_relative 'responses'
|
8
|
-
|
9
|
-
module OpenapiFirst
|
10
|
-
class Definition
|
11
|
-
# Represents an operation object in the OpenAPI 3.X specification.
|
12
|
-
# Use this class to access information about the operation. Use `#[key]` to read the raw data.
|
13
|
-
# When using the middleware you can access the operation object via `env[OpenapiFirst::REQUEST].operation`.
|
14
|
-
class Operation
|
15
|
-
extend Forwardable
|
16
|
-
|
17
|
-
def_delegators :operation_object,
|
18
|
-
:[]
|
19
|
-
|
20
|
-
def initialize(path, request_method, path_item_object, openapi_version:)
|
21
|
-
@path = path
|
22
|
-
@method = request_method
|
23
|
-
@path_item_object = path_item_object
|
24
|
-
@openapi_version = openapi_version
|
25
|
-
@operation_object = @path_item_object[request_method]
|
26
|
-
end
|
27
|
-
|
28
|
-
# Returns the path of the operation as in the API description.
|
29
|
-
# @return [String] The path of the operation.
|
30
|
-
attr_reader :path
|
31
|
-
|
32
|
-
# Returns the (downcased) request method of the operation.
|
33
|
-
# Example: "get"
|
34
|
-
# @return [String] The request method of the operation.
|
35
|
-
attr_reader :method
|
36
|
-
alias request_method method
|
37
|
-
|
38
|
-
attr_reader :openapi_version # :nodoc:
|
39
|
-
|
40
|
-
# Returns the operation ID as defined in the API description.
|
41
|
-
# @return [String, nil]
|
42
|
-
def operation_id
|
43
|
-
operation_object['operationId']
|
44
|
-
end
|
45
|
-
|
46
|
-
# Checks if the operation is a read operation.
|
47
|
-
# This is the case for all request methods except POST, PUT, PATCH and DELETE.
|
48
|
-
# @return [Boolean] `true` if the operation is a read operation, `false` otherwise.
|
49
|
-
def read?
|
50
|
-
!write?
|
51
|
-
end
|
52
|
-
|
53
|
-
# Checks if the operation is a write operation.
|
54
|
-
# This is the case for POST, PUT, PATCH and DELETE request methods.
|
55
|
-
# @return [Boolean] `true` if the operation is a write operation, `false` otherwise.
|
56
|
-
# @deprecated Use {#write?} instead.
|
57
|
-
def write?
|
58
|
-
WRITE_METHODS.include?(method)
|
59
|
-
end
|
60
|
-
|
61
|
-
# Returns the request body definition if defined in the API description.
|
62
|
-
# @return [RequestBody, nil] The request body of the operation, or `nil` if not present.
|
63
|
-
def request_body
|
64
|
-
@request_body ||= RequestBody.new(operation_object['requestBody'], self) if operation_object['requestBody']
|
65
|
-
end
|
66
|
-
|
67
|
-
# Checks if a response status is defined for this operation.
|
68
|
-
# @param status [Integer, String] The response status to check.
|
69
|
-
# @return [Boolean] `true` if the response status is defined, `false` otherwise.
|
70
|
-
def response_status_defined?(status)
|
71
|
-
responses.status_defined?(status)
|
72
|
-
end
|
73
|
-
|
74
|
-
# Returns the response object for a given status.
|
75
|
-
# @param status [Integer, String] The response status.
|
76
|
-
# @param content_type [String] Content-Type of the current response.
|
77
|
-
# @return [Response, nil] The response object for the given status, or `nil` if not found.
|
78
|
-
def response_for(status, content_type)
|
79
|
-
responses.response_for(status, content_type)
|
80
|
-
end
|
81
|
-
|
82
|
-
# Returns the schema for a given content type.
|
83
|
-
# @param content_type [String] The content type.
|
84
|
-
# @return [Schema, nil] The schema for the given content type, or `nil` if not found.
|
85
|
-
def schema_for(content_type)
|
86
|
-
content = @request_body_object['content']
|
87
|
-
return unless content&.any?
|
88
|
-
|
89
|
-
content_schemas&.fetch(content_type) do
|
90
|
-
type = content_type.split(';')[0]
|
91
|
-
content_schemas[type] || content_schemas["#{type.split('/')[0]}/*"] || content_schemas['*/*']
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
# Returns a unique name for this operation. Used for generating error messages.
|
96
|
-
# @visibility private
|
97
|
-
def name
|
98
|
-
@name ||= "#{method.upcase} #{path}"
|
99
|
-
end
|
100
|
-
|
101
|
-
# Returns the path parameters of the operation.
|
102
|
-
# @return [Array<Hash>] The path parameters of the operation.
|
103
|
-
def path_parameters
|
104
|
-
all_parameters['path']
|
105
|
-
end
|
106
|
-
|
107
|
-
# Returns the query parameters of the operation.
|
108
|
-
# Returns parameters defined on the path and in the operation.
|
109
|
-
# @return [Array<Hash>] The query parameters of the operation.
|
110
|
-
def query_parameters
|
111
|
-
all_parameters['query']
|
112
|
-
end
|
113
|
-
|
114
|
-
# Returns the header parameters of the operation.
|
115
|
-
# Returns parameters defined on the path and in the operation.
|
116
|
-
# @return [Array<Hash>] The header parameters of the operation.
|
117
|
-
def header_parameters
|
118
|
-
all_parameters['header']
|
119
|
-
end
|
120
|
-
|
121
|
-
# Returns the cookie parameters of the operation.
|
122
|
-
# Returns parameters defined on the path and in the operation.
|
123
|
-
# @return [Array<Hash>] The cookie parameters of the operation.
|
124
|
-
def cookie_parameters
|
125
|
-
all_parameters['cookie']
|
126
|
-
end
|
127
|
-
|
128
|
-
# Returns the schema for the path parameters.
|
129
|
-
# @visibility private
|
130
|
-
# @return [Schema, nil] The schema for the path parameters, or `nil` if not found.
|
131
|
-
def path_parameters_schema
|
132
|
-
@path_parameters_schema ||= build_schema(path_parameters)
|
133
|
-
end
|
134
|
-
|
135
|
-
# Returns the schema for the query parameters.
|
136
|
-
# @visibility private
|
137
|
-
# @return [Schema, nil] The schema for the query parameters, or `nil` if not found.
|
138
|
-
def query_parameters_schema
|
139
|
-
@query_parameters_schema ||= build_schema(query_parameters)
|
140
|
-
end
|
141
|
-
|
142
|
-
# Returns the schema for the header parameters.
|
143
|
-
# @visibility private
|
144
|
-
# @return [Schema, nil] The schema for the header parameters, or `nil` if not found.
|
145
|
-
def header_parameters_schema
|
146
|
-
@header_parameters_schema ||= build_schema(header_parameters)
|
147
|
-
end
|
148
|
-
|
149
|
-
# Returns the schema for the cookie parameters.
|
150
|
-
# @visibility private
|
151
|
-
# @return [Schema, nil] The schema for the cookie parameters, or `nil` if not found.
|
152
|
-
def cookie_parameters_schema
|
153
|
-
@cookie_parameters_schema ||= build_schema(cookie_parameters)
|
154
|
-
end
|
155
|
-
|
156
|
-
private
|
157
|
-
|
158
|
-
WRITE_METHODS = Set.new(%w[post put patch delete]).freeze
|
159
|
-
private_constant :WRITE_METHODS
|
160
|
-
|
161
|
-
IGNORED_HEADERS = Set['Content-Type', 'Accept', 'Authorization'].freeze
|
162
|
-
private_constant :IGNORED_HEADERS
|
163
|
-
|
164
|
-
def all_parameters
|
165
|
-
@all_parameters ||= (@path_item_object.fetch('parameters', []) + operation_object.fetch('parameters', []))
|
166
|
-
.reject { |p| p['in'] == 'header' && IGNORED_HEADERS.include?(p['name']) }
|
167
|
-
.group_by { _1['in'] }
|
168
|
-
end
|
169
|
-
|
170
|
-
def build_schema(parameters)
|
171
|
-
return unless parameters&.any?
|
172
|
-
|
173
|
-
init_schema = {
|
174
|
-
'type' => 'object',
|
175
|
-
'properties' => {},
|
176
|
-
'required' => []
|
177
|
-
}
|
178
|
-
schema = parameters.each_with_object(init_schema) do |parameter_def, result|
|
179
|
-
parameter = OpenapiParameters::Parameter.new(parameter_def)
|
180
|
-
result['properties'][parameter.name] = parameter.schema if parameter.schema
|
181
|
-
result['required'] << parameter.name if parameter.required?
|
182
|
-
end
|
183
|
-
Schema.new(schema, openapi_version: @openapi_version)
|
184
|
-
end
|
185
|
-
|
186
|
-
def responses
|
187
|
-
@responses ||= Responses.new(self, operation_object['responses'])
|
188
|
-
end
|
189
|
-
|
190
|
-
attr_reader :operation_object
|
191
|
-
|
192
|
-
def build_parameters(parameters, klass)
|
193
|
-
klass.new(parameters, openapi_version:) if parameters.any?
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'forwardable'
|
4
|
-
require_relative 'operation'
|
5
|
-
require_relative 'path_template'
|
6
|
-
|
7
|
-
module OpenapiFirst
|
8
|
-
class Definition
|
9
|
-
# A pathItem as defined in the OpenAPI document.
|
10
|
-
class PathItem
|
11
|
-
extend Forwardable
|
12
|
-
|
13
|
-
def initialize(path, path_item_object, openapi_version:)
|
14
|
-
@path = path
|
15
|
-
@path_item_object = path_item_object
|
16
|
-
@openapi_version = openapi_version
|
17
|
-
@path_template = PathTemplate.new(path)
|
18
|
-
end
|
19
|
-
|
20
|
-
attr_reader :path
|
21
|
-
|
22
|
-
def_delegator :@path_template, :match
|
23
|
-
|
24
|
-
def operation(request_method)
|
25
|
-
return unless @path_item_object[request_method]
|
26
|
-
|
27
|
-
Operation.new(
|
28
|
-
@path, request_method, @path_item_object, openapi_version: @openapi_version
|
29
|
-
)
|
30
|
-
end
|
31
|
-
|
32
|
-
METHODS = %w[get head post put patch delete trace options].freeze
|
33
|
-
private_constant :METHODS
|
34
|
-
|
35
|
-
def operations
|
36
|
-
@operations ||= @path_item_object.slice(*METHODS).keys.map { |method| operation(method) }
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../schema'
|
4
|
-
|
5
|
-
module OpenapiFirst
|
6
|
-
class Definition
|
7
|
-
# Represents a request body definition in the OpenAPI document that belongs to an operation.
|
8
|
-
class RequestBody
|
9
|
-
def initialize(request_body_object, operation)
|
10
|
-
@request_body_object = request_body_object
|
11
|
-
@operation = operation
|
12
|
-
end
|
13
|
-
|
14
|
-
def description
|
15
|
-
@request_body_object['description']
|
16
|
-
end
|
17
|
-
|
18
|
-
def required?
|
19
|
-
!!@request_body_object['required']
|
20
|
-
end
|
21
|
-
|
22
|
-
def schema_for(content_type)
|
23
|
-
content = @request_body_object['content']
|
24
|
-
return unless content&.any?
|
25
|
-
|
26
|
-
content_schemas&.fetch(content_type) do
|
27
|
-
type = content_type.split(';')[0]
|
28
|
-
content_schemas[type] || content_schemas["#{type.split('/')[0]}/*"] || content_schemas['*/*']
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def content_schemas
|
35
|
-
@content_schemas ||= @request_body_object['content']&.each_with_object({}) do |kv, result|
|
36
|
-
type, media_type = kv
|
37
|
-
schema_object = media_type['schema']
|
38
|
-
next unless schema_object
|
39
|
-
|
40
|
-
result[type] = Schema.new(schema_object, write: true,
|
41
|
-
openapi_version: @operation.openapi_version)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OpenapiFirst
|
4
|
-
class Definition
|
5
|
-
# Represents a response definition in the OpenAPI document.
|
6
|
-
# This is not a direct reflecton of the OpenAPI 3.X response definition, but a combination of
|
7
|
-
# status, content type and content schema.
|
8
|
-
class Response
|
9
|
-
def initialize(operation:, status:, response_object:, content_type:, content_schema:)
|
10
|
-
@operation = operation
|
11
|
-
@response_object = response_object
|
12
|
-
@status = status
|
13
|
-
@content_type = content_type
|
14
|
-
@content_schema = content_schema
|
15
|
-
end
|
16
|
-
|
17
|
-
# @attr_reader [Operation] operation The operation this response belongs to.
|
18
|
-
# @attr_reader [Integer] status The HTTP status code of the response definition.
|
19
|
-
# @attr_reader [String, nil] content_type Content type of this response.
|
20
|
-
# @attr_reader [Schema, nil] content_schema the Schema of the response body.
|
21
|
-
attr_reader :operation, :status, :content_type, :content_schema
|
22
|
-
|
23
|
-
def headers
|
24
|
-
@response_object['headers']
|
25
|
-
end
|
26
|
-
|
27
|
-
def description
|
28
|
-
@response_object['description']
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,87 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'response'
|
4
|
-
|
5
|
-
module OpenapiFirst
|
6
|
-
class Definition
|
7
|
-
# @visibility private
|
8
|
-
class Responses
|
9
|
-
def initialize(operation, responses_object)
|
10
|
-
@operation = operation
|
11
|
-
@responses_object = responses_object
|
12
|
-
end
|
13
|
-
|
14
|
-
def status_defined?(status)
|
15
|
-
!!find_response_object(status)
|
16
|
-
end
|
17
|
-
|
18
|
-
def response_for(status, response_content_type)
|
19
|
-
response_object = find_response_object(status)
|
20
|
-
return unless response_object
|
21
|
-
return response_without_content(status, response_object) unless content_defined?(response_object)
|
22
|
-
|
23
|
-
defined_content_type = find_defined_content_type(response_object, response_content_type)
|
24
|
-
return unless defined_content_type
|
25
|
-
|
26
|
-
content_schema = find_content_schema(response_object, response_content_type)
|
27
|
-
Response.new(operation:, status:, response_object:, content_type: defined_content_type, content_schema:)
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
attr_reader :openapi_version, :operation
|
33
|
-
|
34
|
-
def response_without_content(status, response_object)
|
35
|
-
Response.new(operation:, status:, response_object:, content_type: nil, content_schema: nil)
|
36
|
-
end
|
37
|
-
|
38
|
-
def find_defined_content_type(response_object, content_type)
|
39
|
-
return if content_type.nil?
|
40
|
-
|
41
|
-
content = response_object['content']
|
42
|
-
return content_type if content.key?(content_type)
|
43
|
-
|
44
|
-
type = content_type.split(';')[0]
|
45
|
-
return type if content.key?(type)
|
46
|
-
|
47
|
-
key = "#{type.split('/')[0]}/*"
|
48
|
-
return key if content.key?(key)
|
49
|
-
|
50
|
-
key = '*/*'
|
51
|
-
key if content.key?(key)
|
52
|
-
end
|
53
|
-
|
54
|
-
def content_defined?(response_object)
|
55
|
-
response_object['content']&.any?
|
56
|
-
end
|
57
|
-
|
58
|
-
def find_content_schema(response_object, response_content_type)
|
59
|
-
return unless response_content_type
|
60
|
-
|
61
|
-
content_object = find_response_body(response_object['content'], response_content_type)
|
62
|
-
content_schema_object = content_object&.fetch('schema', nil)
|
63
|
-
return unless content_schema_object
|
64
|
-
|
65
|
-
Schema.new(content_schema_object, write: false, openapi_version: operation.openapi_version)
|
66
|
-
end
|
67
|
-
|
68
|
-
def find_response_object(status)
|
69
|
-
# According to OAS status has to be a string,
|
70
|
-
# but there are a few API descriptions out there that use integers because of YAML.
|
71
|
-
return @responses_object[status] if @responses_object.key?(status)
|
72
|
-
|
73
|
-
@responses_object[status.to_s] ||
|
74
|
-
@responses_object["#{status / 100}XX"] ||
|
75
|
-
@responses_object["#{status / 100}xx"] ||
|
76
|
-
@responses_object['default']
|
77
|
-
end
|
78
|
-
|
79
|
-
def find_response_body(content, content_type)
|
80
|
-
content&.fetch(content_type) do |_|
|
81
|
-
type = content_type.split(';')[0]
|
82
|
-
content[type] || content["#{type.split('/')[0]}/*"] || content['*/*']
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
@@ -1,74 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OpenapiFirst
|
4
|
-
module Plugins
|
5
|
-
module Default
|
6
|
-
# An error reponse that returns application/problem+json with a list of "errors"
|
7
|
-
# See also https://www.rfc-editor.org/rfc/rfc9457.html
|
8
|
-
class ErrorResponse
|
9
|
-
include OpenapiFirst::ErrorResponse
|
10
|
-
|
11
|
-
TITLES = {
|
12
|
-
not_found: 'Not Found',
|
13
|
-
method_not_allowed: 'Request Method Not Allowed',
|
14
|
-
unsupported_media_type: 'Unsupported Media Type',
|
15
|
-
invalid_body: 'Bad Request Body',
|
16
|
-
invalid_query: 'Bad Query Parameter',
|
17
|
-
invalid_header: 'Bad Request Header',
|
18
|
-
invalid_path: 'Bad Request Path',
|
19
|
-
invalid_cookie: 'Bad Request Cookie'
|
20
|
-
}.freeze
|
21
|
-
private_constant :TITLES
|
22
|
-
|
23
|
-
def body
|
24
|
-
result = {
|
25
|
-
title:,
|
26
|
-
status:
|
27
|
-
}
|
28
|
-
result[:errors] = errors if failure.errors
|
29
|
-
MultiJson.dump(result)
|
30
|
-
end
|
31
|
-
|
32
|
-
def type = failure.type
|
33
|
-
|
34
|
-
def title
|
35
|
-
TITLES.fetch(type)
|
36
|
-
end
|
37
|
-
|
38
|
-
def content_type
|
39
|
-
'application/problem+json'
|
40
|
-
end
|
41
|
-
|
42
|
-
def errors
|
43
|
-
key = pointer_key
|
44
|
-
failure.errors.map do |error|
|
45
|
-
{
|
46
|
-
message: error.error,
|
47
|
-
key => pointer(error.instance_location),
|
48
|
-
code: error.type
|
49
|
-
}
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def pointer_key
|
54
|
-
case type
|
55
|
-
when :invalid_body
|
56
|
-
:pointer
|
57
|
-
when :invalid_query, :invalid_path
|
58
|
-
:parameter
|
59
|
-
when :invalid_header
|
60
|
-
:header
|
61
|
-
when :invalid_cookie
|
62
|
-
:cookie
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def pointer(data_pointer)
|
67
|
-
return data_pointer if type == :invalid_body
|
68
|
-
|
69
|
-
data_pointer.delete_prefix('/')
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
@@ -1,60 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OpenapiFirst
|
4
|
-
module Plugins
|
5
|
-
module Jsonapi
|
6
|
-
# A JSON:API conform error response. See https://jsonapi.org/.
|
7
|
-
class ErrorResponse
|
8
|
-
include OpenapiFirst::ErrorResponse
|
9
|
-
|
10
|
-
def body
|
11
|
-
MultiJson.dump({ errors: serialized_errors })
|
12
|
-
end
|
13
|
-
|
14
|
-
def content_type
|
15
|
-
'application/vnd.api+json'
|
16
|
-
end
|
17
|
-
|
18
|
-
def serialized_errors
|
19
|
-
return default_errors unless failure.errors
|
20
|
-
|
21
|
-
key = pointer_key
|
22
|
-
failure.errors.map do |error|
|
23
|
-
{
|
24
|
-
status: status.to_s,
|
25
|
-
source: { key => pointer(error.instance_location) },
|
26
|
-
title: error.error,
|
27
|
-
code: error.type
|
28
|
-
}
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def default_errors
|
33
|
-
[{
|
34
|
-
status: status.to_s,
|
35
|
-
title: Rack::Utils::HTTP_STATUS_CODES[status]
|
36
|
-
}]
|
37
|
-
end
|
38
|
-
|
39
|
-
def pointer_key
|
40
|
-
case failure.type
|
41
|
-
when :invalid_body
|
42
|
-
:pointer
|
43
|
-
when :invalid_query, :invalid_path
|
44
|
-
:parameter
|
45
|
-
when :invalid_header
|
46
|
-
:header
|
47
|
-
when :invalid_cookie
|
48
|
-
:cookie
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def pointer(data_pointer)
|
53
|
-
return data_pointer if failure.type == :invalid_body
|
54
|
-
|
55
|
-
data_pointer.delete_prefix('/')
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|