openapi_first 1.4.3 → 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 +43 -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 +25 -20
- 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
|
@@ -110,33 +110,38 @@ files:
|
|
110
110
|
- README.md
|
111
111
|
- lib/openapi_first.rb
|
112
112
|
- lib/openapi_first/body_parser.rb
|
113
|
+
- lib/openapi_first/builder.rb
|
113
114
|
- lib/openapi_first/configuration.rb
|
114
115
|
- lib/openapi_first/definition.rb
|
115
|
-
- lib/openapi_first/definition/operation.rb
|
116
|
-
- lib/openapi_first/definition/path_item.rb
|
117
|
-
- lib/openapi_first/definition/path_template.rb
|
118
|
-
- lib/openapi_first/definition/request_body.rb
|
119
|
-
- lib/openapi_first/definition/response.rb
|
120
|
-
- lib/openapi_first/definition/responses.rb
|
121
116
|
- lib/openapi_first/error_response.rb
|
117
|
+
- lib/openapi_first/error_responses/default.rb
|
118
|
+
- lib/openapi_first/error_responses/jsonapi.rb
|
122
119
|
- lib/openapi_first/errors.rb
|
123
120
|
- lib/openapi_first/failure.rb
|
124
121
|
- lib/openapi_first/json_refs.rb
|
125
122
|
- lib/openapi_first/middlewares/request_validation.rb
|
126
123
|
- lib/openapi_first/middlewares/response_validation.rb
|
127
|
-
- lib/openapi_first/
|
128
|
-
- lib/openapi_first/
|
129
|
-
- lib/openapi_first/
|
130
|
-
- lib/openapi_first/
|
131
|
-
- lib/openapi_first/
|
132
|
-
- lib/openapi_first/
|
133
|
-
- lib/openapi_first/
|
134
|
-
- lib/openapi_first/
|
135
|
-
- lib/openapi_first/
|
136
|
-
- 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
|
137
134
|
- lib/openapi_first/schema.rb
|
138
135
|
- lib/openapi_first/schema/validation_error.rb
|
139
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
|
140
145
|
- lib/openapi_first/version.rb
|
141
146
|
homepage: https://github.com/ahx/openapi_first
|
142
147
|
licenses:
|
@@ -155,14 +160,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
155
160
|
requirements:
|
156
161
|
- - ">="
|
157
162
|
- !ruby/object:Gem::Version
|
158
|
-
version: 3.
|
163
|
+
version: 3.2.0
|
159
164
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
160
165
|
requirements:
|
161
166
|
- - ">="
|
162
167
|
- !ruby/object:Gem::Version
|
163
168
|
version: '0'
|
164
169
|
requirements: []
|
165
|
-
rubygems_version: 3.5.
|
170
|
+
rubygems_version: 3.5.11
|
166
171
|
signing_key:
|
167
172
|
specification_version: 4
|
168
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
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OpenapiFirst
|
4
|
-
# Plugin System adapted from
|
5
|
-
# Polished Ruby Programming by Jeremy Evans
|
6
|
-
# https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewBook?id=0
|
7
|
-
# @!visibility private
|
8
|
-
module Plugins
|
9
|
-
PLUGINS = {} # rubocop:disable Style/MutableConstant
|
10
|
-
private_constant :PLUGINS
|
11
|
-
|
12
|
-
def register(name, klass)
|
13
|
-
PLUGINS[name.to_sym] = klass
|
14
|
-
end
|
15
|
-
|
16
|
-
def plugin(name)
|
17
|
-
require "openapi_first/plugins/#{name}"
|
18
|
-
PLUGINS.fetch(name.to_sym)
|
19
|
-
end
|
20
|
-
|
21
|
-
def find_plugin(name)
|
22
|
-
PLUGINS.fetch(name)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|