scorpio 0.4.5 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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