scorpio 0.4.5 → 0.6.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.
@@ -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
- # @return [Boolean] v3?
48
+ # openapi v3?
49
+ # @return [Boolean]
47
50
  def v3?
48
51
  is_a?(V3::Operation)
49
52
  end
50
53
 
51
- # @return [Boolean] v2?
54
+ # openapi v2?
55
+ # @return [Boolean]
52
56
  def v2?
53
57
  is_a?(V2::Operation)
54
58
  end
55
59
 
56
- # @return [Scorpio::OpenAPI::Document] the document whence this operation came
60
+ # the document whence this operation came
61
+ # @return [Scorpio::OpenAPI::Document]
57
62
  def openapi_document
58
- parent_jsis.detect { |p| p.is_a?(Scorpio::OpenAPI::Document) }
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 parent_jsi.is_a?(Scorpio::OpenAPI::V2::PathItem) || parent_jsi.is_a?(Scorpio::OpenAPI::V3::PathItem)
64
- raise(Bug) unless parent_jsi.parent_jsi.is_a?(Scorpio::OpenAPI::V2::Paths) || parent_jsi.parent_jsi.is_a?(Scorpio::OpenAPI::V3::Paths)
65
- @path_template_str = parent_jsi.jsi_ptr.reference_tokens.last
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
- # @return [Addressable::Template] the path as an Addressable::Template
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] the URI template, consisting of the base_url
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
- # @return the HTTP method of this operation as indicated by the attribute name
87
- # for this operation from the parent PathItem
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 parent_jsi.is_a?(Scorpio::OpenAPI::V2::PathItem) || parent_jsi.is_a?(Scorpio::OpenAPI::V3::PathItem)
91
- @http_method = jsi_ptr.reference_tokens.last
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
- # @return [String] a short identifier for this operation appropriate for an error message
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 self.responses
103
- _, oa_response = self.responses.detect { |k, v| k.to_s == status }
104
- oa_response ||= self.responses['default']
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
- # @return [#to_ary<#to_h>] the parameters specified for this operation, plus any others
112
- # scorpio considers to be parameters
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
- # @return [Module] a module with accessor methods for unambiguously named parameters of this operation.
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
- # @param a, b are passed to Scorpio::Request#initialize
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
- # @param a, b are passed to Scorpio::Request#initialize
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
- # @param a, b are passed to Scorpio::Request#initialize
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
- class Operation
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.from_object(schema_object) : nil
231
+ schema_object ? JSI::Schema.ensure_schema(schema_object) : nil
216
232
  end
217
233
 
218
- # @return [Array<JSI::Schema>]
234
+ # @return [JSI::SchemaSet]
219
235
  def request_schemas
220
- if requestBody && requestBody['content']
221
- # oamt is for Scorpio::OpenAPI::V3::MediaType
222
- oamts = requestBody['content'].values.select { |oamt| oamt.key?('schema') }
223
- oamts.map { |oamt| JSI::Schema.from_object(oamt['schema']) }
224
- else
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.new(oa_schema) : nil
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
- class Operation
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
- # @return [#to_hash] the body parameter
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] request schema for the given media_type
319
+ # @return [JSI::Schema]
283
320
  def request_schema(media_type: nil)
284
321
  if body_parameter && body_parameter['schema']
285
- JSI::Schema.new(body_parameter['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 [Array<JSI::Schema>]
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.new(oa_response_schema) : nil
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
- # @param operationId
27
- # @return [Scorpio::OpenAPI::Operation] the operation with the given operationId
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
- memoize(:[], operationId) do |operationId_|
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
- class Server
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