scorpio 0.5.0 → 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
  class Error < StandardError
@@ -15,15 +17,37 @@ module Scorpio
15
17
  autoload :Operation, 'scorpio/openapi/operation'
16
18
  autoload :Document, 'scorpio/openapi/document'
17
19
  autoload :Reference, 'scorpio/openapi/reference'
20
+ autoload :Tag, 'scorpio/openapi/tag'
18
21
  autoload :OperationsScope, 'scorpio/openapi/operations_scope'
19
22
 
20
23
  module V3
21
- openapi_schema = JSI::Schema.new(::YAML.load_file(Scorpio.root.join('documents/github.com/OAI/OpenAPI-Specification/blob/oas3-schema/schemas/v3.0/schema.yaml')))
24
+ openapi_document_schema = JSI::JSONSchemaOrgDraft04.new_schema(::YAML.load_file(Scorpio.root.join(
25
+ 'documents/github.com/OAI/OpenAPI-Specification/blob/oas3-schema/schemas/v3.0/schema.yaml'
26
+ )))
27
+
28
+ # the schema represented by Scorpio::OpenAPI::V3::Schema will describe schemas itself, so we set it
29
+ # include on its schema module the jsi_schema_instance_modules that implement schema functionality.
30
+ describe_schema = [
31
+ openapi_document_schema.definitions['Schema'],
32
+ openapi_document_schema.definitions['SchemaReference'],
33
+ # instead of the Schema definition allowing boolean, properties['additionalProperties']
34
+ # is a oneOf which allows a Schema, SchemaReference, or boolean.
35
+ # instances of the former two already include the schema implementation (per the previous
36
+ # describes_schema entries), but the boolean does not.
37
+ # including in properties['additionalProperties'] applies to any additionalProperties.
38
+ # (including in properties['additionalProperties'].anyOf[2] would extend booleans too, without
39
+ # the redundant inclusion that results for Schema and SchemaRef, but redundant inclusion is not
40
+ # a problem, and this way also applies when none of the anyOf match due to schema errors.)
41
+ openapi_document_schema.definitions['Schema'].properties['additionalProperties'],
42
+ ]
43
+ describe_schema.each { |s| s.jsi_schema_instance_modules = [JSI::Schema::Draft04] }
22
44
 
23
- Document = openapi_schema.jsi_schema_module
45
+ Document = openapi_document_schema.jsi_schema_module
24
46
 
25
47
  # naming these is not strictly necessary, but is nice to have.
26
- # generated: `puts JSI::Schema.new(::YAML.load_file(Scorpio.root.join('documents/github.com/OAI/OpenAPI-Specification/blob/oas3-schema/schemas/v3.0/schema.yaml')))['definitions'].select { |k,v| ['object', nil].include?(v['type']) }.keys.map { |k| "#{k[0].upcase}#{k[1..-1]} = Document.definitions['#{k}']" }`
48
+ # generated: `puts Scorpio::OpenAPI::V3::Document.schema.definitions.select { |k,v| ['object', nil].include?(v['type']) }.keys.map { |k| "#{k[0].upcase}#{k[1..-1]} = Document.definitions['#{k}']" }`
49
+
50
+
27
51
  Reference = Document.definitions['Reference']
28
52
  SchemaReference = Document.definitions['SchemaReference']
29
53
  Info = Document.definitions['Info']
@@ -86,18 +110,29 @@ module Scorpio
86
110
  Callback = Document.definitions['Callback']
87
111
  Encoding = Document.definitions['Encoding']
88
112
 
89
- # the schema of Scorpio::OpenAPI::V3::Schema describes a schema itself, so we extend it
90
- # with the module indicating that.
91
- Schema.schema.extend(JSI::Schema::DescribesSchema)
92
- SchemaReference.schema.extend(JSI::Schema::DescribesSchema)
113
+ raise(Bug) unless Schema < JSI::Schema
114
+ raise(Bug) unless SchemaReference < JSI::Schema
93
115
  end
94
116
  module V2
95
- openapi_schema = JSI::Schema.new(::JSON.parse(Scorpio.root.join('documents/swagger.io/v2/schema.json').read))
117
+ openapi_document_schema = JSI.new_schema(::JSON.parse(Scorpio.root.join(
118
+ 'documents/swagger.io/v2/schema.json'
119
+ ).read))
120
+
121
+ # the schema represented by Scorpio::OpenAPI::V2::Schema will describe schemas itself, so we set it to
122
+ # include on its schema module the jsi_schema_instance_modules that implement schema functionality.
123
+ describe_schema = [
124
+ openapi_document_schema.definitions['schema'],
125
+ # comments above on v3's definitions['Schema'].properties['additionalProperties'] apply here too
126
+ openapi_document_schema.definitions['schema'].properties['additionalProperties'],
127
+ ]
128
+ describe_schema.each { |s| s.jsi_schema_instance_modules = [JSI::Schema::Draft04] }
96
129
 
97
- Document = openapi_schema.jsi_schema_module
130
+ Document = openapi_document_schema.jsi_schema_module
98
131
 
99
132
  # naming these is not strictly necessary, but is nice to have.
100
- # generated: `puts JSI::Schema.new(::JSON.parse(Scorpio.root.join('documents/swagger.io/v2/schema.json').read))['definitions'].select { |k,v| ['object', nil].include?(v['type']) }.keys.map { |k| "#{k[0].upcase}#{k[1..-1]} = Document.definitions['#{k}']" }`
133
+ # generated: `puts Scorpio::OpenAPI::V2::Document.schema.definitions.select { |k,v| ['object', nil].include?(v['type']) }.keys.map { |k| "#{k[0].upcase}#{k[1..-1]} = Document.definitions['#{k}']" }`
134
+
135
+
101
136
  Info = Document.definitions['info']
102
137
  Contact = Document.definitions['contact']
103
138
  License = Document.definitions['license']
@@ -153,15 +188,12 @@ module Scorpio
153
188
  Enum = Document.definitions['enum']
154
189
  JsonReference = Document.definitions['jsonReference']
155
190
 
156
- # the schema of Scorpio::OpenAPI::V2::Schema describes a schema itself, so we extend it
157
- # with the module indicating that.
158
- Schema.schema.extend(JSI::Schema::DescribesSchema)
191
+ raise(Bug) unless Schema < JSI::Schema
159
192
  end
160
193
 
161
- begin
162
- # the autoloads for OpenAPI::Operation and OpenAPI::Document
163
- # should not be triggered until all the classes their files reference are defined (above)
164
- end # (this block is here just so the above informative comment is not interpreted as module doc)
194
+ # the autoloads for OpenAPI::Operation and OpenAPI::Document are triggered below. these
195
+ # should not be triggered until all the classes their files reference are defined (above).
196
+
165
197
 
166
198
  module V3
167
199
  module Operation
@@ -173,6 +205,9 @@ module Scorpio
173
205
  module Reference
174
206
  include OpenAPI::Reference
175
207
  end
208
+ module Tag
209
+ include OpenAPI::Tag
210
+ end
176
211
  require 'scorpio/openapi/v3/server'
177
212
  end
178
213
 
@@ -186,6 +221,9 @@ module Scorpio
186
221
  module JsonReference
187
222
  include OpenAPI::Reference
188
223
  end
224
+ module Tag
225
+ include OpenAPI::Tag
226
+ end
189
227
  end
190
228
  end
191
229
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'scorpio'
2
4
  require 'pickle'
3
5
 
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Scorpio
2
4
  class Request
3
- SUPPORTED_REQUEST_MEDIA_TYPES = ['application/json', 'application/x-www-form-urlencoded']
4
- FALLBACK_CONTENT_TYPE = 'application/x-www-form-urlencoded'
5
+ SUPPORTED_REQUEST_MEDIA_TYPES = ['application/json'.freeze, 'application/x-www-form-urlencoded'.freeze].freeze
6
+ FALLBACK_CONTENT_TYPE = 'application/x-www-form-urlencoded'.freeze
5
7
 
6
8
  def self.best_media_type(media_types)
7
9
  if media_types.size == 1
@@ -118,12 +120,12 @@ module Scorpio
118
120
  def initialize(operation, configuration = {}, &b)
119
121
  @operation = operation
120
122
 
121
- configuration = JSI.stringify_symbol_keys(configuration)
123
+ configuration = JSI::Util.stringify_symbol_keys(configuration)
122
124
  params_set = Set.new # the set of params that have been set
123
125
  # do the Configurables first
124
126
  configuration.each do |name, value|
125
127
  if Configurables.public_method_defined?("#{name}=")
126
- Configurables.instance_method("#{name}=").bind(self).call(value)
128
+ Configurables.instance_method("#{name}=").bind_call(self, value)
127
129
  params_set << name
128
130
  end
129
131
  end
@@ -148,21 +150,23 @@ module Scorpio
148
150
  operation.openapi_document
149
151
  end
150
152
 
151
- # @return [Symbol] the http method for this request - :get, :post, etc.
153
+ # the http method for this request
154
+ # @return [String]
152
155
  def http_method
153
- operation.http_method.downcase.to_sym
156
+ operation.http_method
154
157
  end
155
158
 
156
- # @return [Addressable::Template] the template for the request's path, to be expanded
157
- # with path_params and appended to the request's base_url
159
+ # the template for the request's path, to be expanded with {Configurables#path_params} and appended to
160
+ # the request's {Configurables#base_url}
161
+ # @return [Addressable::Template]
158
162
  def path_template
159
163
  operation.path_template
160
164
  end
161
165
 
162
- # @return [Addressable::URI] an Addressable::URI containing only the path to append to
163
- # the base_url for this request
166
+ # an Addressable::URI containing only the path to append to the {Configurables#base_url} for this request
167
+ # @return [Addressable::URI]
164
168
  def path
165
- path_params = JSI.stringify_symbol_keys(self.path_params)
169
+ path_params = JSI::Util.stringify_symbol_keys(self.path_params)
166
170
  missing_variables = path_template.variables - path_params.keys
167
171
  if missing_variables.any?
168
172
  raise(ArgumentError, "path #{operation.path_template_str} for operation #{operation.human_id} requires path_params " +
@@ -174,24 +178,26 @@ module Scorpio
174
178
  "which were empty: #{empty_variables.inspect}")
175
179
  end
176
180
 
177
- path_template.expand(path_params).tap do |path|
178
- if query_params
179
- path.query_values = query_params
180
- end
181
+ path = path_template.expand(path_params)
182
+ if query_params
183
+ path.query_values = query_params
181
184
  end
185
+ path.freeze
182
186
  end
183
187
 
184
- # @return [Addressable::URI] the full URL for this request
188
+ # the full URL for this request
189
+ # @return [Addressable::URI]
185
190
  def url
186
191
  unless base_url
187
192
  raise(ArgumentError, "no base_url has been specified for request")
188
193
  end
189
194
  # we do not use Addressable::URI#join as the paths should just be concatenated, not resolved.
190
195
  # we use File.join just to deal with consecutive slashes.
191
- Addressable::URI.parse(File.join(base_url, path))
196
+ Addressable::URI.parse(File.join(base_url, path)).freeze
192
197
  end
193
198
 
194
- # @return [::Ur::ContentType] the value of the request Content-Type header
199
+ # the value of the request Content-Type header
200
+ # @return [::Ur::ContentType]
195
201
  def content_type_header
196
202
  headers.each do |k, v|
197
203
  return ::Ur::ContentType.new(v) if k =~ /\Acontent[-_]type\z/i
@@ -199,8 +205,9 @@ module Scorpio
199
205
  nil
200
206
  end
201
207
 
202
- # @return [::Ur::ContentType] Content-Type for this request, taken from request headers if
203
- # present, or the request media_type.
208
+ # Content-Type for this request, taken from request headers if present, or the
209
+ # request {Configurables#media_type}.
210
+ # @return [::Ur::ContentType]
204
211
  def content_type
205
212
  content_type_header || (media_type ? ::Ur::ContentType.new(media_type) : nil)
206
213
  end
@@ -232,16 +239,17 @@ module Scorpio
232
239
  # @param name [String, Symbol] the 'name' property of one applicable parameter
233
240
  # @param value [Object] the applicable parameter will be applied to the request with the given value.
234
241
  # @return [Object] echoes the value param
235
- # @raise [Scorpio::AmbiguousParameter] if more than one parameter has the given name
242
+ # @raise (see #param_for!)
236
243
  def set_param(name, value)
237
244
  param = param_for!(name)
238
245
  set_param_from(param['in'], param['name'], value)
239
246
  value
240
247
  end
241
248
 
249
+ # returns the value of the named parameter on this request
242
250
  # @param name [String, Symbol] the 'name' property of one applicable parameter
243
- # @return [Object] the value of the named parameter on this request
244
- # @raise [Scorpio::AmbiguousParameter] if more than one parameter has the given name
251
+ # @return [Object]
252
+ # @raise (see #param_for!)
245
253
  def get_param(name)
246
254
  param = param_for!(name)
247
255
  get_param_from(param['in'], param['name'])
@@ -249,6 +257,7 @@ module Scorpio
249
257
 
250
258
  # @param name [String, Symbol] the 'name' property of one applicable parameter
251
259
  # @return [#to_hash, nil]
260
+ # @raise [Scorpio::AmbiguousParameter] if more than one parameter has the given name
252
261
  def param_for(name)
253
262
  name = name.to_s if name.is_a?(Symbol)
254
263
  params = operation.inferred_parameters.select { |p| p['name'] == name }
@@ -263,17 +272,21 @@ module Scorpio
263
272
  end
264
273
  end
265
274
 
266
- # @param name [String, Symbol] the name or {in}.{name} (e.g. "query.search") for the applicable parameter.
275
+ # @param name [String, Symbol] the 'name' property of one applicable parameter
267
276
  # @return [#to_hash]
277
+ # @raise [Scorpio::ParameterError] if no parameter has the given name
278
+ # @raise (see #param_for)
268
279
  def param_for!(name)
269
280
  param_for(name) || raise(ParameterError, "There is no parameter named #{name} on operation #{operation.human_id}:\n#{operation.pretty_inspect.chomp}")
270
281
  end
271
282
 
272
- # @param in [String, Symbol] one of 'path', 'query', 'header', or 'cookie' - where to apply the named value
283
+ # applies the named value to the appropriate parameter of the request
284
+ # @param param_in [String, Symbol] one of 'path', 'query', 'header', or 'cookie' - where to apply
285
+ # the named value
273
286
  # @param name [String, Symbol] the parameter name to apply the value to
274
287
  # @param value [Object] the value
275
288
  # @return [Object] echoes the value param
276
- # @raise [ArgumentError] invalid 'in' parameter
289
+ # @raise [ArgumentError] invalid `param_in` parameter
277
290
  # @raise [NotImplementedError] cookies aren't implemented
278
291
  def set_param_from(param_in, name, value)
279
292
  param_in = param_in.to_s if param_in.is_a?(Symbol)
@@ -292,10 +305,12 @@ module Scorpio
292
305
  value
293
306
  end
294
307
 
295
- # @param in [String, Symbol] one of 'path', 'query', 'header', or 'cookie' - where to apply the named value
308
+ # returns the value of the named parameter from the specified `param_in` on this request
309
+ # @param param_in [String, Symbol] one of 'path', 'query', 'header', or 'cookie' - where to retrieve
310
+ # the named value
296
311
  # @param name [String, Symbol] the parameter name
297
- # @return [Object] the value of the named parameter on this request
298
- # @raise [ArgumentError] invalid 'in' parameter
312
+ # @return [Object]
313
+ # @raise [ArgumentError] invalid `param_in` parameter
299
314
  # @raise [NotImplementedError] cookies aren't implemented
300
315
  def get_param_from(param_in, name)
301
316
  if param_in == 'path'
@@ -333,17 +348,17 @@ module Scorpio
333
348
  headers.update(self.headers)
334
349
  end
335
350
  ur = nil
336
- faraday_connection(-> (yur) { ur = yur }).run_request(http_method, url, body, headers)
351
+ faraday_connection(-> (yur) { ur = yur }).run_request(http_method.downcase.to_sym, url, body, headers)
337
352
  ur.scorpio_request = self
338
353
  ur
339
354
  end
340
355
 
341
356
  # runs this request. returns the response body object - that is, the response body
342
357
  # parsed according to an understood media type, and instantiated with the applicable
343
- # response schema if one is specified. see Scorpio::Response#body_object for more detail.
358
+ # response schema if one is specified. see {Scorpio::Response#body_object} for more detail.
344
359
  #
345
360
  # @raise [Scorpio::HTTPError] if the request returns a 4xx or 5xx status, the appropriate
346
- # error is raised - see Scorpio::HTTPErrors
361
+ # error is raised - see {Scorpio::HTTPErrors}
347
362
  def run
348
363
  ur = run_ur
349
364
  ur.raise_on_http_error