scorpio 0.4.5 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/LICENSE.md +613 -0
- data/README.md +31 -18
- data/documents/github.com/OAI/OpenAPI-Specification/blob/oas3-schema/schemas/v3.0/schema.yaml +30 -22
- data/lib/scorpio/google_api_document.rb +27 -15
- data/lib/scorpio/openapi/document.rb +8 -6
- data/lib/scorpio/openapi/operation.rb +92 -42
- data/lib/scorpio/openapi/operations_scope.rb +13 -11
- data/lib/scorpio/openapi/reference.rb +44 -0
- data/lib/scorpio/openapi/tag.rb +15 -0
- data/lib/scorpio/openapi/v3/server.rb +4 -2
- data/lib/scorpio/openapi.rb +186 -135
- data/lib/scorpio/pickle_adapter.rb +2 -0
- data/lib/scorpio/request.rb +60 -41
- data/lib/scorpio/resource_base.rb +238 -198
- data/lib/scorpio/response.rb +10 -6
- data/lib/scorpio/ur.rb +16 -15
- data/lib/scorpio/version.rb +3 -1
- data/lib/scorpio.rb +5 -6
- data/scorpio.gemspec +16 -23
- metadata +23 -206
- data/.simplecov +0 -1
- data/LICENSE.txt +0 -21
- data/Rakefile +0 -10
- data/bin/documents_to_yml.rb +0 -33
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Scorpio
|
2
4
|
module OpenAPI
|
3
5
|
# An OpenAPI operation
|
@@ -43,37 +45,42 @@ module Scorpio
|
|
43
45
|
end
|
44
46
|
include Configurables
|
45
47
|
|
46
|
-
#
|
48
|
+
# openapi v3?
|
49
|
+
# @return [Boolean]
|
47
50
|
def v3?
|
48
51
|
is_a?(V3::Operation)
|
49
52
|
end
|
50
53
|
|
51
|
-
#
|
54
|
+
# openapi v2?
|
55
|
+
# @return [Boolean]
|
52
56
|
def v2?
|
53
57
|
is_a?(V2::Operation)
|
54
58
|
end
|
55
59
|
|
56
|
-
#
|
60
|
+
# the document whence this operation came
|
61
|
+
# @return [Scorpio::OpenAPI::Document]
|
57
62
|
def openapi_document
|
58
|
-
|
63
|
+
jsi_parent_nodes.detect { |p| p.is_a?(Scorpio::OpenAPI::Document) }
|
59
64
|
end
|
60
65
|
|
66
|
+
# @return [String]
|
61
67
|
def path_template_str
|
62
68
|
return @path_template_str if instance_variable_defined?(:@path_template_str)
|
63
|
-
raise(Bug) unless
|
64
|
-
raise(Bug) unless
|
65
|
-
@path_template_str =
|
69
|
+
raise(Bug) unless jsi_parent_node.is_a?(Scorpio::OpenAPI::V2::PathItem) || jsi_parent_node.is_a?(Scorpio::OpenAPI::V3::PathItem)
|
70
|
+
raise(Bug) unless jsi_parent_node.jsi_parent_node.is_a?(Scorpio::OpenAPI::V2::Paths) || jsi_parent_node.jsi_parent_node.is_a?(Scorpio::OpenAPI::V3::Paths)
|
71
|
+
@path_template_str = jsi_parent_node.jsi_ptr.tokens.last
|
66
72
|
end
|
67
73
|
|
68
|
-
#
|
74
|
+
# the path as an Addressable::Template
|
75
|
+
# @return [Addressable::Template]
|
69
76
|
def path_template
|
70
77
|
return @path_template if instance_variable_defined?(:@path_template)
|
71
78
|
@path_template = Addressable::Template.new(path_template_str)
|
72
79
|
end
|
73
80
|
|
81
|
+
# the URI template, consisting of the base_url concatenated with the path template
|
74
82
|
# @param base_url [#to_str] the base URL to which the path template is appended
|
75
|
-
# @return [Addressable::Template]
|
76
|
-
# concatenated with the path template
|
83
|
+
# @return [Addressable::Template]
|
77
84
|
def uri_template(base_url: self.base_url)
|
78
85
|
unless base_url
|
79
86
|
raise(ArgumentError, "no base_url has been specified for operation #{self}")
|
@@ -83,33 +90,38 @@ module Scorpio
|
|
83
90
|
Addressable::Template.new(File.join(base_url, path_template_str))
|
84
91
|
end
|
85
92
|
|
86
|
-
#
|
87
|
-
#
|
93
|
+
# the HTTP method of this operation as indicated by the attribute name for this operation
|
94
|
+
# from the parent PathItem
|
95
|
+
# @return [String]
|
88
96
|
def http_method
|
89
97
|
return @http_method if instance_variable_defined?(:@http_method)
|
90
|
-
raise(Bug) unless
|
91
|
-
@http_method = jsi_ptr.
|
98
|
+
raise(Bug) unless jsi_parent_node.is_a?(Scorpio::OpenAPI::V2::PathItem) || jsi_parent_node.is_a?(Scorpio::OpenAPI::V3::PathItem)
|
99
|
+
@http_method = jsi_ptr.tokens.last
|
92
100
|
end
|
93
101
|
|
94
|
-
#
|
102
|
+
# a short identifier for this operation appropriate for an error message
|
103
|
+
# @return [String]
|
95
104
|
def human_id
|
96
105
|
operationId || "path: #{path_template_str}, method: #{http_method}"
|
97
106
|
end
|
98
107
|
|
108
|
+
# @param status [String, Integer]
|
99
109
|
# @return [Scorpio::OpenAPI::V3::Response, Scorpio::OpenAPI::V2::Response]
|
100
110
|
def oa_response(status: )
|
101
111
|
status = status.to_s if status.is_a?(Numeric)
|
102
|
-
if
|
103
|
-
_, oa_response =
|
104
|
-
oa_response ||=
|
112
|
+
if responses
|
113
|
+
_, oa_response = responses.detect { |k, v| k.to_s == status }
|
114
|
+
oa_response ||= responses['default']
|
105
115
|
end
|
106
116
|
oa_response
|
107
117
|
end
|
108
118
|
|
119
|
+
# the parameters specified for this operation, plus any others scorpio considers to be parameters.
|
120
|
+
#
|
109
121
|
# this method is not intended to be API-stable at the moment.
|
110
122
|
#
|
111
|
-
# @
|
112
|
-
#
|
123
|
+
# @api private
|
124
|
+
# @return [#to_ary<#to_h>]
|
113
125
|
def inferred_parameters
|
114
126
|
parameters = self.parameters ? self.parameters.to_a.dup : []
|
115
127
|
path_template.variables.each do |var|
|
@@ -127,7 +139,8 @@ module Scorpio
|
|
127
139
|
parameters
|
128
140
|
end
|
129
141
|
|
130
|
-
#
|
142
|
+
# a module with accessor methods for unambiguously named parameters of this operation.
|
143
|
+
# @return [Module]
|
131
144
|
def request_accessor_module
|
132
145
|
return @request_accessor_module if instance_variable_defined?(:@request_accessor_module)
|
133
146
|
@request_accessor_module = begin
|
@@ -136,7 +149,7 @@ module Scorpio
|
|
136
149
|
instance_method_modules = [Request, Request::Configurables]
|
137
150
|
instance_method_names = instance_method_modules.map do |mod|
|
138
151
|
(mod.instance_methods + mod.private_instance_methods).map(&:to_s)
|
139
|
-
end.inject(Set.new,
|
152
|
+
end.inject(Set.new, &:merge)
|
140
153
|
params_by_name.each do |name, params|
|
141
154
|
next if instance_method_names.include?(name)
|
142
155
|
if params.size == 1
|
@@ -149,19 +162,22 @@ module Scorpio
|
|
149
162
|
end
|
150
163
|
end
|
151
164
|
|
152
|
-
#
|
165
|
+
# instantiates a {Scorpio::Request} for this operation.
|
166
|
+
# parameters are all passed to {Scorpio::Request#initialize}.
|
153
167
|
# @return [Scorpio::Request]
|
154
168
|
def build_request(*a, &b)
|
155
169
|
Scorpio::Request.new(self, *a, &b)
|
156
170
|
end
|
157
171
|
|
158
|
-
#
|
172
|
+
# runs a {Scorpio::Request} for this operation, returning a {Scorpio::Ur}.
|
173
|
+
# parameters are all passed to {Scorpio::Request#initialize}.
|
159
174
|
# @return [Scorpio::Ur] response ur
|
160
175
|
def run_ur(*a, &b)
|
161
176
|
build_request(*a, &b).run_ur
|
162
177
|
end
|
163
178
|
|
164
|
-
#
|
179
|
+
# runs a {Scorpio::Request} for this operation - see {Scorpio::Request#run}.
|
180
|
+
# parameters are all passed to {Scorpio::Request#initialize}.
|
165
181
|
# @return response body object
|
166
182
|
def run(*a, &b)
|
167
183
|
build_request(*a, &b).run
|
@@ -174,7 +190,7 @@ module Scorpio
|
|
174
190
|
# Describes a single API operation on a path.
|
175
191
|
#
|
176
192
|
# https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject
|
177
|
-
|
193
|
+
module Operation
|
178
194
|
module Configurables
|
179
195
|
def scheme
|
180
196
|
# not applicable; for OpenAPI v3, scheme is specified by servers.
|
@@ -212,17 +228,19 @@ module Scorpio
|
|
212
228
|
requestBody['content'] &&
|
213
229
|
requestBody['content'][media_type] &&
|
214
230
|
requestBody['content'][media_type]['schema']
|
215
|
-
schema_object ? JSI::Schema.
|
231
|
+
schema_object ? JSI::Schema.ensure_schema(schema_object) : nil
|
216
232
|
end
|
217
233
|
|
218
|
-
# @return [
|
234
|
+
# @return [JSI::SchemaSet]
|
219
235
|
def request_schemas
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
236
|
+
JSI::SchemaSet.build do |schemas|
|
237
|
+
if requestBody && requestBody['content']
|
238
|
+
requestBody['content'].each_value do |oa_media_type|
|
239
|
+
if oa_media_type['schema']
|
240
|
+
schemas << oa_media_type['schema']
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
226
244
|
end
|
227
245
|
end
|
228
246
|
|
@@ -232,13 +250,30 @@ module Scorpio
|
|
232
250
|
oa_media_types = oa_response ? oa_response['content'] : nil # Scorpio::OpenAPI::V3::MediaTypes
|
233
251
|
oa_media_type = oa_media_types ? oa_media_types[media_type] : nil # Scorpio::OpenAPI::V3::MediaType
|
234
252
|
oa_schema = oa_media_type ? oa_media_type['schema'] : nil # Scorpio::OpenAPI::V3::Schema
|
235
|
-
oa_schema ? JSI::Schema.
|
253
|
+
oa_schema ? JSI::Schema.ensure_schema(oa_schema) : nil
|
254
|
+
end
|
255
|
+
|
256
|
+
# @return [JSI::SchemaSet]
|
257
|
+
def response_schemas
|
258
|
+
JSI::SchemaSet.build do |schemas|
|
259
|
+
if responses
|
260
|
+
responses.each_value do |oa_response|
|
261
|
+
if oa_response['content']
|
262
|
+
oa_response['content'].each_value do |oa_media_type|
|
263
|
+
if oa_media_type['schema']
|
264
|
+
schemas << oa_media_type['schema']
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
236
271
|
end
|
237
272
|
end
|
238
273
|
end
|
239
274
|
module V2
|
240
275
|
raise(Bug, 'const_defined? Scorpio::OpenAPI::V2::Operation') unless const_defined?(:Operation)
|
241
|
-
|
276
|
+
module Operation
|
242
277
|
module Configurables
|
243
278
|
attr_writer :scheme
|
244
279
|
def scheme
|
@@ -264,7 +299,8 @@ module Scorpio
|
|
264
299
|
end
|
265
300
|
include Configurables
|
266
301
|
|
267
|
-
#
|
302
|
+
# the body parameter
|
303
|
+
# @return [#to_hash]
|
268
304
|
# @raise [Scorpio::OpenAPI::SemanticError] if there's more than one body param
|
269
305
|
def body_parameter
|
270
306
|
body_parameters = (parameters || []).select { |parameter| parameter['in'] == 'body' }
|
@@ -278,19 +314,20 @@ module Scorpio
|
|
278
314
|
end
|
279
315
|
end
|
280
316
|
|
317
|
+
# request schema for the given media_type
|
281
318
|
# @param media_type unused
|
282
|
-
# @return [JSI::Schema]
|
319
|
+
# @return [JSI::Schema]
|
283
320
|
def request_schema(media_type: nil)
|
284
321
|
if body_parameter && body_parameter['schema']
|
285
|
-
JSI::Schema.
|
322
|
+
JSI::Schema.ensure_schema(body_parameter['schema'])
|
286
323
|
else
|
287
324
|
nil
|
288
325
|
end
|
289
326
|
end
|
290
327
|
|
291
|
-
# @return [
|
328
|
+
# @return [JSI::SchemaSet]
|
292
329
|
def request_schemas
|
293
|
-
request_schema ? [request_schema] : []
|
330
|
+
request_schema ? JSI::SchemaSet[request_schema] : JSI::SchemaSet[]
|
294
331
|
end
|
295
332
|
|
296
333
|
# @param status [Integer, String] response status
|
@@ -299,7 +336,20 @@ module Scorpio
|
|
299
336
|
def response_schema(status: , media_type: nil)
|
300
337
|
oa_response = self.oa_response(status: status)
|
301
338
|
oa_response_schema = oa_response ? oa_response['schema'] : nil # Scorpio::OpenAPI::V2::Schema
|
302
|
-
oa_response_schema ? JSI::Schema.
|
339
|
+
oa_response_schema ? JSI::Schema.ensure_schema(oa_response_schema) : nil
|
340
|
+
end
|
341
|
+
|
342
|
+
# @return [JSI::SchemaSet]
|
343
|
+
def response_schemas
|
344
|
+
JSI::SchemaSet.build do |schemas|
|
345
|
+
if responses
|
346
|
+
responses.each_value do |oa_response|
|
347
|
+
if oa_response['schema']
|
348
|
+
schemas << oa_response['schema']
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
303
353
|
end
|
304
354
|
end
|
305
355
|
end
|
@@ -1,13 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Scorpio
|
2
4
|
module OpenAPI
|
3
5
|
# OperationsScope acts as an Enumerable of the Operations for an openapi_document,
|
4
6
|
# and offers subscripting by operationId.
|
5
7
|
class OperationsScope
|
6
|
-
include JSI::Memoize
|
7
|
-
|
8
8
|
# @param openapi_document [Scorpio::OpenAPI::Document]
|
9
9
|
def initialize(openapi_document)
|
10
10
|
@openapi_document = openapi_document
|
11
|
+
@operations_by_id = Hash.new do |h, operationId|
|
12
|
+
op = detect { |operation| operation.operationId == operationId }
|
13
|
+
unless op
|
14
|
+
raise(::KeyError, "operationId not found: #{operationId.inspect}")
|
15
|
+
end
|
16
|
+
h[operationId] = op
|
17
|
+
end
|
11
18
|
end
|
12
19
|
attr_reader :openapi_document
|
13
20
|
|
@@ -23,17 +30,12 @@ module Scorpio
|
|
23
30
|
end
|
24
31
|
include Enumerable
|
25
32
|
|
26
|
-
#
|
27
|
-
# @
|
33
|
+
# finds an operation with the given `operationId`
|
34
|
+
# @param operationId [String] the operationId of the operation to find
|
35
|
+
# @return [Scorpio::OpenAPI::Operation]
|
28
36
|
# @raise [::KeyError] if the given operationId does not exist
|
29
37
|
def [](operationId)
|
30
|
-
|
31
|
-
detect { |operation| operation.operationId == operationId_ }.tap do |op|
|
32
|
-
unless op
|
33
|
-
raise(::KeyError, "operationId not found: #{operationId_.inspect}")
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
38
|
+
@operations_by_id[operationId]
|
37
39
|
end
|
38
40
|
end
|
39
41
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Scorpio
|
4
|
+
module OpenAPI
|
5
|
+
module Reference
|
6
|
+
# overrides JSI::Base#[] to implicitly dereference this Reference, except when
|
7
|
+
# the given token is present in this Reference's instance (this should usually
|
8
|
+
# only apply to the token '$ref')
|
9
|
+
def [](token, *a, &b)
|
10
|
+
if respond_to?(:to_hash) && !key?(token)
|
11
|
+
deref do |deref_jsi|
|
12
|
+
return deref_jsi[token]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
return super
|
16
|
+
end
|
17
|
+
|
18
|
+
# yields or returns the target of this reference
|
19
|
+
# @yield [JSI::Base] if a block is given
|
20
|
+
# @return [JSI::Base]
|
21
|
+
def deref
|
22
|
+
return unless respond_to?(:to_hash) && self['$ref'].respond_to?(:to_str)
|
23
|
+
|
24
|
+
ref_uri = Addressable::URI.parse(self['$ref'])
|
25
|
+
ref_uri_nofrag = ref_uri.merge(fragment: nil)
|
26
|
+
|
27
|
+
if !ref_uri_nofrag.empty? || ref_uri.fragment.nil?
|
28
|
+
raise(NotImplementedError,
|
29
|
+
"Scorpio currently only supports fragment URIs as OpenAPI references. cannot find reference by uri: #{self['$ref']}"
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
ptr = JSI::Ptr.from_fragment(ref_uri.fragment)
|
34
|
+
deref_jsi = ptr.evaluate(jsi_root_node)
|
35
|
+
|
36
|
+
# TODO type check deref_jsi
|
37
|
+
|
38
|
+
yield deref_jsi if block_given?
|
39
|
+
|
40
|
+
deref_jsi
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Scorpio
|
2
|
+
module OpenAPI
|
3
|
+
module Tag
|
4
|
+
# operations in the openapi document which have a tag with this tag's name
|
5
|
+
# @return [Enumerable<Scorpio::OpenAPI::Operation>]
|
6
|
+
def operations
|
7
|
+
unless jsi_root_node.is_a?(OpenAPI::Document)
|
8
|
+
raise("Tag#operations cannot be used on a Tag that is not inside an OpenAPI document")
|
9
|
+
end
|
10
|
+
|
11
|
+
jsi_root_node.operations.select { |op| op.tags.respond_to?(:to_ary) && op.tags.include?(name) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Scorpio
|
2
4
|
module OpenAPI
|
3
5
|
module V3
|
@@ -6,7 +8,7 @@ module Scorpio
|
|
6
8
|
# An object representing a Server.
|
7
9
|
#
|
8
10
|
# https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#serverObject
|
9
|
-
|
11
|
+
module Server
|
10
12
|
# expands this server's #url using the given_server_variables. any variables
|
11
13
|
# that are in the url but not in the given server variables are filled in
|
12
14
|
# using the default value for the variable.
|
@@ -34,7 +36,7 @@ module Scorpio
|
|
34
36
|
server_variables = given_server_variables
|
35
37
|
end
|
36
38
|
template = Addressable::Template.new(url)
|
37
|
-
template.expand(server_variables)
|
39
|
+
template.expand(server_variables).freeze
|
38
40
|
end
|
39
41
|
end
|
40
42
|
end
|