openapi_first 0.20.0 → 1.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +20 -2
- data/Gemfile.lock +43 -34
- data/README.md +26 -177
- data/benchmarks/Gemfile.lock +54 -54
- data/benchmarks/apps/openapi_first.ru +1 -1
- data/benchmarks/benchmarks.rb +2 -1
- data/examples/app.rb +12 -16
- data/lib/openapi_first/body_parser_middleware.rb +53 -0
- data/lib/openapi_first/errors.rb +2 -0
- data/lib/openapi_first/operation.rb +25 -53
- data/lib/openapi_first/request_validation.rb +53 -96
- data/lib/openapi_first/router.rb +47 -17
- data/lib/openapi_first/schema_validation.rb +9 -0
- data/lib/openapi_first/use_router.rb +1 -3
- data/lib/openapi_first/utils.rb +11 -5
- data/lib/openapi_first/version.rb +1 -1
- data/lib/openapi_first.rb +3 -35
- data/openapi_first.gemspec +6 -4
- metadata +56 -23
- data/lib/openapi_first/app.rb +0 -29
- data/lib/openapi_first/coverage.rb +0 -28
- data/lib/openapi_first/default_operation_resolver.rb +0 -63
- data/lib/openapi_first/inbox.rb +0 -13
- data/lib/openapi_first/rack_responder.rb +0 -12
- data/lib/openapi_first/responder.rb +0 -44
- data/lib/openapi_first/response_object.rb +0 -20
- data/lib/openapi_first/validation.rb +0 -15
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: 0.
|
4
|
+
version: 1.0.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Haller
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-04-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: deep_merge
|
@@ -28,30 +28,30 @@ dependencies:
|
|
28
28
|
name: hanami-router
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 2.0.
|
33
|
+
version: 2.0.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 2.0.
|
40
|
+
version: 2.0.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: hanami-utils
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 2.0.
|
47
|
+
version: 2.0.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 2.0.
|
54
|
+
version: 2.0.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: json_refs
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -101,19 +101,39 @@ dependencies:
|
|
101
101
|
- !ruby/object:Gem::Version
|
102
102
|
version: '1.14'
|
103
103
|
- !ruby/object:Gem::Dependency
|
104
|
-
name:
|
104
|
+
name: mustermann-contrib
|
105
105
|
requirement: !ruby/object:Gem::Requirement
|
106
106
|
requirements:
|
107
107
|
- - "~>"
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
109
|
+
version: 3.0.0
|
110
110
|
type: :runtime
|
111
111
|
prerelease: false
|
112
112
|
version_requirements: !ruby/object:Gem::Requirement
|
113
113
|
requirements:
|
114
114
|
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 3.0.0
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: rack
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
115
122
|
- !ruby/object:Gem::Version
|
116
123
|
version: '2.2'
|
124
|
+
- - "<"
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '4.0'
|
127
|
+
type: :runtime
|
128
|
+
prerelease: false
|
129
|
+
version_requirements: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '2.2'
|
134
|
+
- - "<"
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '4.0'
|
117
137
|
- !ruby/object:Gem::Dependency
|
118
138
|
name: bundler
|
119
139
|
requirement: !ruby/object:Gem::Requirement
|
@@ -128,6 +148,26 @@ dependencies:
|
|
128
148
|
- - "~>"
|
129
149
|
- !ruby/object:Gem::Version
|
130
150
|
version: '2'
|
151
|
+
- !ruby/object:Gem::Dependency
|
152
|
+
name: openapi_parameters
|
153
|
+
requirement: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - "~>"
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0.2'
|
158
|
+
- - "<="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: 2.0.0
|
161
|
+
type: :development
|
162
|
+
prerelease: false
|
163
|
+
version_requirements: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - "~>"
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: '0.2'
|
168
|
+
- - "<="
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: 2.0.0
|
131
171
|
- !ruby/object:Gem::Dependency
|
132
172
|
name: rack-test
|
133
173
|
requirement: !ruby/object:Gem::Requirement
|
@@ -214,24 +254,17 @@ files:
|
|
214
254
|
- examples/config.ru
|
215
255
|
- examples/openapi.yaml
|
216
256
|
- lib/openapi_first.rb
|
217
|
-
- lib/openapi_first/
|
218
|
-
- lib/openapi_first/coverage.rb
|
219
|
-
- lib/openapi_first/default_operation_resolver.rb
|
257
|
+
- lib/openapi_first/body_parser_middleware.rb
|
220
258
|
- lib/openapi_first/definition.rb
|
221
259
|
- lib/openapi_first/errors.rb
|
222
|
-
- lib/openapi_first/inbox.rb
|
223
260
|
- lib/openapi_first/operation.rb
|
224
|
-
- lib/openapi_first/rack_responder.rb
|
225
261
|
- lib/openapi_first/request_validation.rb
|
226
|
-
- lib/openapi_first/responder.rb
|
227
|
-
- lib/openapi_first/response_object.rb
|
228
262
|
- lib/openapi_first/response_validation.rb
|
229
263
|
- lib/openapi_first/response_validator.rb
|
230
264
|
- lib/openapi_first/router.rb
|
231
265
|
- lib/openapi_first/schema_validation.rb
|
232
266
|
- lib/openapi_first/use_router.rb
|
233
267
|
- lib/openapi_first/utils.rb
|
234
|
-
- lib/openapi_first/validation.rb
|
235
268
|
- lib/openapi_first/validation_format.rb
|
236
269
|
- lib/openapi_first/version.rb
|
237
270
|
- openapi_first.gemspec
|
@@ -248,12 +281,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
248
281
|
requirements:
|
249
282
|
- - ">="
|
250
283
|
- !ruby/object:Gem::Version
|
251
|
-
version:
|
284
|
+
version: 3.0.5
|
252
285
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
253
286
|
requirements:
|
254
|
-
- - "
|
287
|
+
- - ">"
|
255
288
|
- !ruby/object:Gem::Version
|
256
|
-
version:
|
289
|
+
version: 1.3.1
|
257
290
|
requirements: []
|
258
291
|
rubygems_version: 3.3.7
|
259
292
|
signing_key:
|
data/lib/openapi_first/app.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'rack'
|
4
|
-
|
5
|
-
module OpenapiFirst
|
6
|
-
class App
|
7
|
-
def initialize( # rubocop:disable Metrics/ParameterLists
|
8
|
-
parent_app,
|
9
|
-
spec,
|
10
|
-
namespace:,
|
11
|
-
router_raise_error: false,
|
12
|
-
request_validation_raise_error: false,
|
13
|
-
response_validation: false,
|
14
|
-
resolver: nil
|
15
|
-
)
|
16
|
-
@stack = Rack::Builder.app do
|
17
|
-
freeze_app
|
18
|
-
use OpenapiFirst::Router, spec: spec, raise_error: router_raise_error, parent_app: parent_app
|
19
|
-
use OpenapiFirst::RequestValidation, raise_error: request_validation_raise_error
|
20
|
-
use OpenapiFirst::ResponseValidation if response_validation
|
21
|
-
run OpenapiFirst::Responder.new(namespace: namespace, resolver: resolver)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def call(env)
|
26
|
-
@stack.call(env)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OpenapiFirst
|
4
|
-
class Coverage
|
5
|
-
attr_reader :to_be_called
|
6
|
-
|
7
|
-
def initialize(app, spec)
|
8
|
-
@app = app
|
9
|
-
@spec = spec
|
10
|
-
@to_be_called = spec.operations.map do |operation|
|
11
|
-
endpoint_id(operation)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def call(env)
|
16
|
-
response = @app.call(env)
|
17
|
-
operation = env[OPERATION]
|
18
|
-
@to_be_called.delete(endpoint_id(operation)) if operation
|
19
|
-
response
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def endpoint_id(operation)
|
25
|
-
"#{operation.path}##{operation.method}"
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'utils'
|
4
|
-
|
5
|
-
module OpenapiFirst
|
6
|
-
class DefaultOperationResolver
|
7
|
-
def initialize(namespace)
|
8
|
-
@namespace = namespace
|
9
|
-
@handlers = {}
|
10
|
-
end
|
11
|
-
|
12
|
-
def call(operation)
|
13
|
-
@handlers[operation.name] ||= begin
|
14
|
-
id = handler_id(operation)
|
15
|
-
find_handler(id) if id
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def find_handler(id)
|
20
|
-
name = id.match(/:*(.*)/)&.to_a&.at(1)
|
21
|
-
return if name.nil?
|
22
|
-
|
23
|
-
catch :halt do
|
24
|
-
return find_class_method_handler(name) if name.include?('.')
|
25
|
-
return find_instance_method_handler(name) if name.include?('#')
|
26
|
-
end
|
27
|
-
method_name = Utils.underscore(name)
|
28
|
-
return unless @namespace.respond_to?(method_name)
|
29
|
-
|
30
|
-
@namespace.method(method_name)
|
31
|
-
end
|
32
|
-
|
33
|
-
def handler_id(operation)
|
34
|
-
id = operation['x-handler'] || operation['operationId']
|
35
|
-
if id.nil?
|
36
|
-
raise HandlerNotFoundError,
|
37
|
-
"operationId or x-handler is missing in '#{operation.method} #{operation.path}' so I cannot find a handler for this operation." # rubocop:disable Layout/LineLength
|
38
|
-
end
|
39
|
-
|
40
|
-
id
|
41
|
-
end
|
42
|
-
|
43
|
-
def find_class_method_handler(name)
|
44
|
-
module_name, method_name = name.split('.')
|
45
|
-
klass = find_const(@namespace, module_name)
|
46
|
-
klass.method(Utils.underscore(method_name))
|
47
|
-
end
|
48
|
-
|
49
|
-
def find_instance_method_handler(name)
|
50
|
-
module_name, klass_name = name.split('#')
|
51
|
-
const = find_const(@namespace, module_name)
|
52
|
-
klass = find_const(const, klass_name)
|
53
|
-
klass.new
|
54
|
-
end
|
55
|
-
|
56
|
-
def find_const(parent, name)
|
57
|
-
name = Utils.classify(name)
|
58
|
-
throw :halt unless parent.const_defined?(name, false)
|
59
|
-
|
60
|
-
parent.const_get(name, false)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
data/lib/openapi_first/inbox.rb
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'rack'
|
4
|
-
require 'multi_json'
|
5
|
-
require_relative 'inbox'
|
6
|
-
require_relative 'default_operation_resolver'
|
7
|
-
|
8
|
-
module OpenapiFirst
|
9
|
-
class Responder
|
10
|
-
def initialize(namespace: nil, resolver: nil)
|
11
|
-
@resolver = resolver || DefaultOperationResolver.new(namespace)
|
12
|
-
@namespace = namespace
|
13
|
-
end
|
14
|
-
|
15
|
-
def call(env)
|
16
|
-
operation = env[OpenapiFirst::OPERATION]
|
17
|
-
res = Rack::Response.new
|
18
|
-
handler = find_handler(operation)
|
19
|
-
result = handler.call(inbox(env), res)
|
20
|
-
res.write serialize(result) if result && res.body.empty?
|
21
|
-
res[Rack::CONTENT_TYPE] ||= operation.content_types_for(res.status)&.first
|
22
|
-
res.finish
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
def inbox(env)
|
28
|
-
Inbox.new(env).tap { |i| i.merge!(env[INBOX]) if env[INBOX] }
|
29
|
-
end
|
30
|
-
|
31
|
-
def find_handler(operation)
|
32
|
-
handler = @resolver.call(operation)
|
33
|
-
raise NotImplementedError, "Could not find handler for #{operation.name}" unless handler
|
34
|
-
|
35
|
-
handler
|
36
|
-
end
|
37
|
-
|
38
|
-
def serialize(result)
|
39
|
-
return result if result.is_a?(String)
|
40
|
-
|
41
|
-
MultiJson.dump(result)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'forwardable'
|
4
|
-
|
5
|
-
module OpenapiFirst
|
6
|
-
# Represents an OpenAPI Response Object
|
7
|
-
class ResponseObject
|
8
|
-
extend Forwardable
|
9
|
-
def_delegators :@parsed,
|
10
|
-
:content
|
11
|
-
|
12
|
-
def_delegators :@raw,
|
13
|
-
:[]
|
14
|
-
|
15
|
-
def initialize(parsed)
|
16
|
-
@parsed = parsed
|
17
|
-
@raw = parsed.raw
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|