openapi_first 1.0.0.beta3 → 1.0.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +8 -20
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +13 -0
- data/Gemfile +4 -1
- data/Gemfile.lock +39 -37
- data/README.md +17 -11
- data/benchmarks/Gemfile.lock +21 -20
- data/benchmarks/apps/openapi_first_with_plain_rack.ru +2 -2
- data/lib/openapi_first/body_parser_middleware.rb +1 -1
- data/lib/openapi_first/config.rb +19 -0
- data/lib/openapi_first/default_error_response.rb +47 -0
- data/lib/openapi_first/definition.rb +8 -1
- data/lib/openapi_first/error_response.rb +31 -0
- data/lib/openapi_first/errors.rb +3 -40
- data/lib/openapi_first/operation.rb +33 -14
- data/lib/openapi_first/operation_schemas.rb +52 -0
- data/lib/openapi_first/plugins.rb +17 -0
- data/lib/openapi_first/request_body_validator.rb +41 -0
- data/lib/openapi_first/request_validation.rb +66 -84
- data/lib/openapi_first/response_validation.rb +38 -7
- data/lib/openapi_first/response_validator.rb +1 -1
- data/lib/openapi_first/router.rb +15 -14
- data/lib/openapi_first/schema_validation.rb +22 -21
- data/lib/openapi_first/string_keyed_hash.rb +20 -0
- data/lib/openapi_first/validation_result.rb +15 -0
- data/lib/openapi_first/version.rb +1 -1
- data/lib/openapi_first.rb +30 -3
- data/openapi_first.gemspec +4 -9
- metadata +16 -66
- data/lib/openapi_first/utils.rb +0 -19
- data/lib/openapi_first/validation_format.rb +0 -55
data/lib/openapi_first.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'yaml'
|
4
4
|
require 'json_refs'
|
5
|
+
require_relative 'openapi_first/config'
|
6
|
+
require_relative 'openapi_first/plugins'
|
5
7
|
require_relative 'openapi_first/definition'
|
6
8
|
require_relative 'openapi_first/version'
|
7
9
|
require_relative 'openapi_first/errors'
|
@@ -9,15 +11,40 @@ require_relative 'openapi_first/router'
|
|
9
11
|
require_relative 'openapi_first/request_validation'
|
10
12
|
require_relative 'openapi_first/response_validator'
|
11
13
|
require_relative 'openapi_first/response_validation'
|
14
|
+
require_relative 'openapi_first/default_error_response'
|
12
15
|
|
13
16
|
module OpenapiFirst
|
17
|
+
# The OpenAPI operation for the current request
|
14
18
|
OPERATION = 'openapi.operation'
|
19
|
+
|
20
|
+
# Merged parsed path and query parameters
|
15
21
|
PARAMS = 'openapi.params'
|
22
|
+
|
23
|
+
# Parsed query parameters
|
24
|
+
QUERY_PARAMS = 'openapi.query'
|
25
|
+
|
26
|
+
# Parsed path parameters
|
27
|
+
PATH_PARAMS = 'openapi.path_params'
|
28
|
+
|
29
|
+
# Parsed header parameters, except for Content-Type, Accept and Authorization
|
30
|
+
HEADER_PARAMS = 'openapi.headers'
|
31
|
+
|
32
|
+
# Parsed cookie parameter values
|
33
|
+
COOKIE_PARAMS = 'openapi.cookies'
|
34
|
+
|
35
|
+
# The parsed request body
|
16
36
|
REQUEST_BODY = 'openapi.parsed_request_body'
|
17
|
-
HANDLER = 'openapi_first.handler'
|
18
37
|
|
19
|
-
|
20
|
-
|
38
|
+
class << self
|
39
|
+
# Throws an error in the middle of the request validation to stop validation and send a response.
|
40
|
+
def error!(status, location = nil, title: nil, validation_result: nil)
|
41
|
+
throw :error, {
|
42
|
+
status:,
|
43
|
+
location:,
|
44
|
+
title: title || validation_result&.output&.fetch('error') || Rack::Utils::HTTP_STATUS_CODES[status],
|
45
|
+
validation_result:
|
46
|
+
}
|
47
|
+
end
|
21
48
|
end
|
22
49
|
|
23
50
|
def self.load(spec_path, only: nil)
|
data/openapi_first.gemspec
CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.email = ['andreas.haller@posteo.de']
|
12
12
|
spec.licenses = ['MIT']
|
13
13
|
|
14
|
-
spec.summary = 'Implement REST APIs based on OpenApi.'
|
14
|
+
spec.summary = 'Implement REST APIs based on OpenApi 3.x'
|
15
15
|
spec.homepage = 'https://github.com/ahx/openapi_first'
|
16
16
|
|
17
17
|
if spec.respond_to?(:metadata)
|
@@ -32,19 +32,14 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.bindir = 'exe'
|
33
33
|
spec.require_paths = ['lib']
|
34
34
|
|
35
|
-
spec.required_ruby_version = '>= 3.
|
35
|
+
spec.required_ruby_version = '>= 3.1.1'
|
36
36
|
|
37
37
|
spec.add_runtime_dependency 'hanami-router', '~> 2.0.0'
|
38
38
|
spec.add_runtime_dependency 'json_refs', '~> 0.1', '>= 0.1.7'
|
39
|
-
spec.add_runtime_dependency 'json_schemer', '~> 0.
|
39
|
+
spec.add_runtime_dependency 'json_schemer', '~> 2.0.0'
|
40
40
|
spec.add_runtime_dependency 'multi_json', '~> 1.14'
|
41
|
-
spec.add_runtime_dependency 'openapi_parameters', '~> 0.2'
|
41
|
+
spec.add_runtime_dependency 'openapi_parameters', '~> 0.2.2'
|
42
42
|
spec.add_runtime_dependency 'rack', '>= 2.2', '< 4.0'
|
43
|
-
|
44
|
-
spec.add_development_dependency 'bundler', '~> 2'
|
45
|
-
spec.add_development_dependency 'rack-test', '~> 1'
|
46
|
-
spec.add_development_dependency 'rake', '~> 13'
|
47
|
-
spec.add_development_dependency 'rspec', '~> 3'
|
48
43
|
spec.metadata = {
|
49
44
|
'rubygems_mfa_required' => 'true'
|
50
45
|
}
|
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: 1.0.0.
|
4
|
+
version: 1.0.0.beta4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Haller
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-10-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hanami-router
|
@@ -50,14 +50,14 @@ dependencies:
|
|
50
50
|
requirements:
|
51
51
|
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: 0.
|
53
|
+
version: 2.0.0
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
58
|
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version: 0.
|
60
|
+
version: 2.0.0
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
62
|
name: multi_json
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -78,14 +78,14 @@ dependencies:
|
|
78
78
|
requirements:
|
79
79
|
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
|
-
version:
|
81
|
+
version: 0.2.2
|
82
82
|
type: :runtime
|
83
83
|
prerelease: false
|
84
84
|
version_requirements: !ruby/object:Gem::Requirement
|
85
85
|
requirements:
|
86
86
|
- - "~>"
|
87
87
|
- !ruby/object:Gem::Version
|
88
|
-
version:
|
88
|
+
version: 0.2.2
|
89
89
|
- !ruby/object:Gem::Dependency
|
90
90
|
name: rack
|
91
91
|
requirement: !ruby/object:Gem::Requirement
|
@@ -106,62 +106,6 @@ dependencies:
|
|
106
106
|
- - "<"
|
107
107
|
- !ruby/object:Gem::Version
|
108
108
|
version: '4.0'
|
109
|
-
- !ruby/object:Gem::Dependency
|
110
|
-
name: bundler
|
111
|
-
requirement: !ruby/object:Gem::Requirement
|
112
|
-
requirements:
|
113
|
-
- - "~>"
|
114
|
-
- !ruby/object:Gem::Version
|
115
|
-
version: '2'
|
116
|
-
type: :development
|
117
|
-
prerelease: false
|
118
|
-
version_requirements: !ruby/object:Gem::Requirement
|
119
|
-
requirements:
|
120
|
-
- - "~>"
|
121
|
-
- !ruby/object:Gem::Version
|
122
|
-
version: '2'
|
123
|
-
- !ruby/object:Gem::Dependency
|
124
|
-
name: rack-test
|
125
|
-
requirement: !ruby/object:Gem::Requirement
|
126
|
-
requirements:
|
127
|
-
- - "~>"
|
128
|
-
- !ruby/object:Gem::Version
|
129
|
-
version: '1'
|
130
|
-
type: :development
|
131
|
-
prerelease: false
|
132
|
-
version_requirements: !ruby/object:Gem::Requirement
|
133
|
-
requirements:
|
134
|
-
- - "~>"
|
135
|
-
- !ruby/object:Gem::Version
|
136
|
-
version: '1'
|
137
|
-
- !ruby/object:Gem::Dependency
|
138
|
-
name: rake
|
139
|
-
requirement: !ruby/object:Gem::Requirement
|
140
|
-
requirements:
|
141
|
-
- - "~>"
|
142
|
-
- !ruby/object:Gem::Version
|
143
|
-
version: '13'
|
144
|
-
type: :development
|
145
|
-
prerelease: false
|
146
|
-
version_requirements: !ruby/object:Gem::Requirement
|
147
|
-
requirements:
|
148
|
-
- - "~>"
|
149
|
-
- !ruby/object:Gem::Version
|
150
|
-
version: '13'
|
151
|
-
- !ruby/object:Gem::Dependency
|
152
|
-
name: rspec
|
153
|
-
requirement: !ruby/object:Gem::Requirement
|
154
|
-
requirements:
|
155
|
-
- - "~>"
|
156
|
-
- !ruby/object:Gem::Version
|
157
|
-
version: '3'
|
158
|
-
type: :development
|
159
|
-
prerelease: false
|
160
|
-
version_requirements: !ruby/object:Gem::Requirement
|
161
|
-
requirements:
|
162
|
-
- - "~>"
|
163
|
-
- !ruby/object:Gem::Version
|
164
|
-
version: '3'
|
165
109
|
description:
|
166
110
|
email:
|
167
111
|
- andreas.haller@posteo.de
|
@@ -208,17 +152,23 @@ files:
|
|
208
152
|
- examples/openapi.yaml
|
209
153
|
- lib/openapi_first.rb
|
210
154
|
- lib/openapi_first/body_parser_middleware.rb
|
155
|
+
- lib/openapi_first/config.rb
|
156
|
+
- lib/openapi_first/default_error_response.rb
|
211
157
|
- lib/openapi_first/definition.rb
|
158
|
+
- lib/openapi_first/error_response.rb
|
212
159
|
- lib/openapi_first/errors.rb
|
213
160
|
- lib/openapi_first/operation.rb
|
161
|
+
- lib/openapi_first/operation_schemas.rb
|
162
|
+
- lib/openapi_first/plugins.rb
|
163
|
+
- lib/openapi_first/request_body_validator.rb
|
214
164
|
- lib/openapi_first/request_validation.rb
|
215
165
|
- lib/openapi_first/response_validation.rb
|
216
166
|
- lib/openapi_first/response_validator.rb
|
217
167
|
- lib/openapi_first/router.rb
|
218
168
|
- lib/openapi_first/schema_validation.rb
|
169
|
+
- lib/openapi_first/string_keyed_hash.rb
|
219
170
|
- lib/openapi_first/use_router.rb
|
220
|
-
- lib/openapi_first/
|
221
|
-
- lib/openapi_first/validation_format.rb
|
171
|
+
- lib/openapi_first/validation_result.rb
|
222
172
|
- lib/openapi_first/version.rb
|
223
173
|
- openapi_first.gemspec
|
224
174
|
homepage: https://github.com/ahx/openapi_first
|
@@ -234,7 +184,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
234
184
|
requirements:
|
235
185
|
- - ">="
|
236
186
|
- !ruby/object:Gem::Version
|
237
|
-
version: 3.
|
187
|
+
version: 3.1.1
|
238
188
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
239
189
|
requirements:
|
240
190
|
- - ">"
|
@@ -244,5 +194,5 @@ requirements: []
|
|
244
194
|
rubygems_version: 3.3.7
|
245
195
|
signing_key:
|
246
196
|
specification_version: 4
|
247
|
-
summary: Implement REST APIs based on OpenApi.
|
197
|
+
summary: Implement REST APIs based on OpenApi 3.x
|
248
198
|
test_files: []
|
data/lib/openapi_first/utils.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OpenapiFirst
|
4
|
-
module Utils
|
5
|
-
class StringKeyedHash
|
6
|
-
def initialize(original)
|
7
|
-
@orig = original
|
8
|
-
end
|
9
|
-
|
10
|
-
def key?(key)
|
11
|
-
@orig.key?(key.to_sym)
|
12
|
-
end
|
13
|
-
|
14
|
-
def [](key)
|
15
|
-
@orig[key.to_sym]
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OpenapiFirst
|
4
|
-
module ValidationFormat
|
5
|
-
SIMPLE_TYPES = %w[string integer].freeze
|
6
|
-
|
7
|
-
# rubocop:disable Metrics/MethodLength
|
8
|
-
# rubocop:disable Metrics/AbcSize
|
9
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
10
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
11
|
-
def self.error_details(error)
|
12
|
-
if error['type'] == 'pattern'
|
13
|
-
{
|
14
|
-
title: 'is not valid',
|
15
|
-
detail: "does not match pattern '#{error['schema']['pattern']}'"
|
16
|
-
}
|
17
|
-
elsif error['type'] == 'format'
|
18
|
-
{
|
19
|
-
title: "has not a valid #{error.dig('schema', 'format')} format",
|
20
|
-
detail: "#{error['data'].inspect} is not a valid #{error.dig('schema', 'format')} format"
|
21
|
-
}
|
22
|
-
elsif error['type'] == 'enum'
|
23
|
-
{
|
24
|
-
title: "value #{error['data'].inspect} is not defined in enum",
|
25
|
-
detail: "value can be one of #{error.dig('schema', 'enum')&.join(', ')}"
|
26
|
-
}
|
27
|
-
elsif error['type'] == 'required'
|
28
|
-
missing_keys = error['details']['missing_keys']
|
29
|
-
{
|
30
|
-
title: "is missing required properties: #{missing_keys.join(', ')}"
|
31
|
-
}
|
32
|
-
elsif error['type'] == 'readOnly'
|
33
|
-
{
|
34
|
-
title: 'appears in request, but is read-only'
|
35
|
-
}
|
36
|
-
elsif error['type'] == 'writeOnly'
|
37
|
-
{
|
38
|
-
title: 'write-only field appears in response:'
|
39
|
-
}
|
40
|
-
elsif SIMPLE_TYPES.include?(error['type'])
|
41
|
-
{
|
42
|
-
title: "should be a #{error['type']}"
|
43
|
-
}
|
44
|
-
elsif error['schema'] == false
|
45
|
-
{ title: 'unknown fields are not allowed' }
|
46
|
-
else
|
47
|
-
{ title: "is not valid: #{error['data'].inspect}" }
|
48
|
-
end
|
49
|
-
end
|
50
|
-
# rubocop:enable Metrics/MethodLength
|
51
|
-
# rubocop:enable Metrics/AbcSize
|
52
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
53
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
54
|
-
end
|
55
|
-
end
|