scorpio 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f65533b3372858d91ff71a0ec3847179a8f50a1d825e5f77e94632a249692ecc
4
- data.tar.gz: 7dab437fe9bbe813f2b8a24417d0232fdf39dc861c2723b7f5df74764cf92797
3
+ metadata.gz: 603bcc3914e849b35e8c4519682588fc7f56dda8d52c188fde80eef20bf7b34f
4
+ data.tar.gz: aecb035dd48a48c8f97e7ca60ffb560abde598385db4cbaf4d0444226708a7ef
5
5
  SHA512:
6
- metadata.gz: 13c15b82acc7509c807336037993fa6e97bd7308a37e552974ae259b2665230a78a83235d455df485b51c318c76e8f64ed2e4397bcdc24b4ede52b32e58945f2
7
- data.tar.gz: c25e928331ff2e0a00904c0e7db4fef1fd5d3ed77331acd6e3b2e59905301ef1d958ec3346eca92f98298c8b2a1ef1e3b537eaf874d87e128c933108d31fcea5
6
+ metadata.gz: e462291e8caca9542c8de14c81402be9bb004571c93d2af7ce92d842ee22861df92a10e9be941149950d017d7751d41e70d6ada8bb17fb2822c0ba85a0a8cbd9
7
+ data.tar.gz: 6cc6ebc90878d29cc2861c7c48e2231cd5554b7ed569efcbe529295275969d772f9bb1c328039d539ef4e579489f37269ae2003bd6a2d15da90645548cedafe9
@@ -1,3 +1,8 @@
1
+ # v0.4.1
2
+ - Request#param_for
3
+ - Operation#human_id
4
+ - bugfixes
5
+
1
6
  # v0.4.0
2
7
  - Scorpio::OpenAPI v3 classes updated from OAI/OpenAPI-Specification branch oas3-schema
3
8
  - any uniquely-named request parameter will have accessors on Request and can be passed as config to #initialize
@@ -4,7 +4,13 @@ require 'pathname'
4
4
  require 'json'
5
5
  require 'yaml'
6
6
 
7
- Pathname.glob(Pathname.new(__FILE__).dirname.join('../documents/**/*')).select { |p| p.file? && !['.yml', '.yaml'].include?(p.extname) }.each do |file|
7
+ root = Pathname.new(__FILE__).dirname.join('..')
8
+
9
+ document_dirs = [root.join('documents'), root.join('test/documents')]
10
+ documents = document_dirs.map { |d| Pathname.glob(d.join('**/*')) }.inject([], &:+)
11
+ json_documents = documents.select { |p| p.file? && !['.yml', '.yaml'].include?(p.extname) }
12
+
13
+ json_documents.each do |file|
8
14
  begin
9
15
  json_contents = JSON.parse(file.read)
10
16
  yaml = YAML.dump(json_contents, line_width: -1)
@@ -97,6 +97,8 @@ module Scorpio
97
97
  class ConfigError < Error
98
98
  attr_accessor :name
99
99
  end
100
+ class ParameterError < ConfigError
101
+ end
100
102
  class AmbiguousParameter < ConfigError
101
103
  end
102
104
 
@@ -1,5 +1,13 @@
1
1
  module Scorpio
2
2
  module OpenAPI
3
+ # an error in the semantics of this openapi document. for example, an Operation with
4
+ # two body parameters (in v2, not possible in v3) is a semantic error.
5
+ #
6
+ # an instance of a SemanticError may or may not correspond to a validation error of
7
+ # an OpenAPI document against the OpenAPI schema.
8
+ class SemanticError
9
+ end
10
+
3
11
  autoload :Operation, 'scorpio/openapi/operation'
4
12
  autoload :Document, 'scorpio/openapi/document'
5
13
  autoload :OperationsScope, 'scorpio/openapi/operations_scope'
@@ -97,6 +97,21 @@ module Scorpio
97
97
  end
98
98
  end
99
99
 
100
+ # @return [String] a short identifier for this operation appropriate for an error message
101
+ def human_id
102
+ operationId || "path: #{path_template_str}, method: #{http_method}"
103
+ end
104
+
105
+ # @return [Scorpio::OpenAPI::V3::Response, Scorpio::OpenAPI::V2::Response]
106
+ def oa_response(status: )
107
+ status = status.to_s if status.is_a?(Numeric)
108
+ if self.responses
109
+ _, oa_response = self.responses.detect { |k, v| k.to_s == status }
110
+ oa_response ||= self.responses['default']
111
+ end
112
+ oa_response
113
+ end
114
+
100
115
  # this method is not intended to be API-stable at the moment.
101
116
  #
102
117
  # @return [#to_ary<#to_h>] the parameters specified for this operation, plus any others
@@ -141,7 +156,7 @@ module Scorpio
141
156
  end
142
157
 
143
158
  def build_request(*a, &b)
144
- request = Scorpio::Request.new(self, *a, &b)
159
+ Scorpio::Request.new(self, *a, &b)
145
160
  end
146
161
 
147
162
  def run_ur(*a, &b)
@@ -192,28 +207,26 @@ module Scorpio
192
207
 
193
208
  def request_schema(media_type: self.request_media_type)
194
209
  # TODO typechecking on requestBody & children
195
- requestBody &&
210
+ schema_object = requestBody &&
196
211
  requestBody['content'] &&
197
212
  requestBody['content'][media_type] &&
198
- requestBody['content'][media_type]['schema'] &&
199
- requestBody['content'][media_type]['schema'].deref
213
+ requestBody['content'][media_type]['schema']
214
+ schema_object ? JSI::Schema.from_object(schema_object) : nil
200
215
  end
201
216
 
202
217
  def request_schemas
203
218
  if requestBody && requestBody['content']
204
219
  # oamt is for Scorpio::OpenAPI::V3::MediaType
205
- requestBody['content'].values.map { |oamt| oamt['schema'] }.compact.map(&:deref)
220
+ oamts = requestBody['content'].values.select { |oamt| oamt.key?('schema') }
221
+ oamts.map { |oamt| JSI::Schema.from_object(oamt['schema']) }
222
+ else
223
+ []
206
224
  end
207
225
  end
208
226
 
209
227
  # @return JSI::Schema
210
228
  def response_schema(status: , media_type: )
211
- status = status.to_s if status.is_a?(Numeric)
212
- if self.responses
213
- # Scorpio::OpenAPI::V3::Response
214
- _, oa_response = self.responses.detect { |k, v| k.to_s == status }
215
- oa_response ||= self.responses['default']
216
- end
229
+ oa_response = self.oa_response(status: status)
217
230
  oa_media_types = oa_response ? oa_response['content'] : nil # Scorpio::OpenAPI::V3::MediaTypes
218
231
  oa_media_type = oa_media_types ? oa_media_types[media_type] : nil # Scorpio::OpenAPI::V3::MediaType
219
232
  oa_schema = oa_media_type ? oa_media_type['schema'] : nil # Scorpio::OpenAPI::V3::Schema
@@ -257,7 +270,8 @@ module Scorpio
257
270
  elsif body_parameters.size == 1
258
271
  body_parameters.first
259
272
  else
260
- raise(Bug, "multiple body parameters on operation #{operation.pretty_inspect.chomp}") # TODO BLAME
273
+ # TODO blame
274
+ raise(OpenAPI::SemanticError, "multiple body parameters on operation #{operation.pretty_inspect.chomp}")
261
275
  end
262
276
  end
263
277
 
@@ -275,12 +289,7 @@ module Scorpio
275
289
 
276
290
  # @return JSI::Schema
277
291
  def response_schema(status: , media_type: nil)
278
- status = status.to_s if status.is_a?(Numeric)
279
- if self.responses
280
- # Scorpio::OpenAPI::V2::Response
281
- _, oa_response = self.responses.detect { |k, v| k.to_s == status }
282
- oa_response ||= self.responses['default']
283
- end
292
+ oa_response = self.oa_response(status: status)
284
293
  oa_response_schema = oa_response ? oa_response['schema'] : nil # Scorpio::OpenAPI::V2::Schema
285
294
  oa_response_schema ? JSI::Schema.new(oa_response_schema) : nil
286
295
  end
@@ -62,7 +62,7 @@ module Scorpio
62
62
  if body_object.respond_to?(:to_str)
63
63
  body_object
64
64
  else
65
- raise(NotImplementedError)
65
+ raise(NotImplementedError, "Scorpio does not know how to generate the request body with media_type = #{media_type.respond_to?(:to_str) ? media_type : media_type.inspect} for operation: #{operation.human_id}. Scorpio supports media types: #{SUPPORTED_REQUEST_MEDIA_TYPES.join(', ')}. body_object was: #{body_object.pretty_inspect.chomp}")
66
66
  end
67
67
  end
68
68
  else
@@ -128,14 +128,8 @@ module Scorpio
128
128
  end
129
129
  # then do other top-level params
130
130
  configuration.reject { |name, _| params_set.include?(name) }.each do |name, value|
131
- params = operation.inferred_parameters.select { |p| p['name'] == name }
132
- if params.size == 1
133
- set_param_from(params.first['in'], name, value)
134
- elsif params.size == 0
135
- raise(ArgumentError, "unrecognized configuration value passed: #{name.inspect}")
136
- else
137
- raise(AmbiguousParameter.new("There are multiple parameters named #{name.inspect} - cannot use it as a configuration key").tap { |e| e.name = name })
138
- end
131
+ param = param_for(name) || raise(ArgumentError, "unrecognized configuration value passed: #{name.inspect}")
132
+ set_param_from(param['in'], param['name'], value)
139
133
  end
140
134
 
141
135
  extend operation.request_accessor_module
@@ -170,12 +164,12 @@ module Scorpio
170
164
  path_params = JSI.stringify_symbol_keys(self.path_params)
171
165
  missing_variables = path_template.variables - path_params.keys
172
166
  if missing_variables.any?
173
- raise(ArgumentError, "path #{operation.path_template_str} for operation #{operation.operationId} requires path_params " +
167
+ raise(ArgumentError, "path #{operation.path_template_str} for operation #{operation.human_id} requires path_params " +
174
168
  "which were missing: #{missing_variables.inspect}")
175
169
  end
176
170
  empty_variables = path_template.variables.select { |v| path_params[v].to_s.empty? }
177
171
  if empty_variables.any?
178
- raise(ArgumentError, "path #{operation.path_template_str} for operation #{operation.operationId} requires path_params " +
172
+ raise(ArgumentError, "path #{operation.path_template_str} for operation #{operation.human_id} requires path_params " +
179
173
  "which were empty: #{empty_variables.inspect}")
180
174
  end
181
175
 
@@ -248,13 +242,8 @@ module Scorpio
248
242
  # @return [Object] echoes the value param
249
243
  # @raise [Scorpio::AmbiguousParameter] if more than one paramater has the given name
250
244
  def set_param(name, value)
251
- name = name.to_s if name.is_a?(Symbol)
252
- params = operation.inferred_parameters.select { |p| p['name'] == name }
253
- if params.size == 1
254
- set_param_from(params.first['in'], name, value)
255
- else
256
- raise(AmbiguousParameter.new("There are multiple parameters named #{name}; cannot use #set_param").tap { |e| e.name = name })
257
- end
245
+ param = param_for!(name)
246
+ set_param_from(param['in'], param['name'], value)
258
247
  value
259
248
  end
260
249
 
@@ -262,15 +251,32 @@ module Scorpio
262
251
  # @return [Object] the value of the named parameter on this request
263
252
  # @raise [Scorpio::AmbiguousParameter] if more than one paramater has the given name
264
253
  def get_param(name)
254
+ param = param_for!(name)
255
+ get_param_from(param['in'], param['name'])
256
+ end
257
+
258
+ # @param name [String, Symbol] the 'name' property of one applicable parameter
259
+ # @return [#to_hash, nil]
260
+ def param_for(name)
265
261
  name = name.to_s if name.is_a?(Symbol)
266
262
  params = operation.inferred_parameters.select { |p| p['name'] == name }
267
263
  if params.size == 1
268
- get_param_from(params.first['in'], name)
264
+ params.first
265
+ elsif params.size == 0
266
+ nil
269
267
  else
270
- raise(AmbiguousParameter.new("There are multiple parameters named #{name}; cannot use #get_param").tap { |e| e.name = name })
268
+ raise(AmbiguousParameter.new(
269
+ "There are multiple parameters for #{name}. matched parameters were: #{params.pretty_inspect.chomp}"
270
+ ).tap { |e| e.name = name })
271
271
  end
272
272
  end
273
273
 
274
+ # @param name [String, Symbol] the name or {in}.{name} (e.g. "query.search") for the applicable parameter.
275
+ # @return [#to_hash]
276
+ def param_for!(name)
277
+ param_for(name) || raise(ParameterError, "There is no parameter named #{name} on operation #{operation.human_id}:\n#{operation.pretty_inspect.chomp}")
278
+ end
279
+
274
280
  # @param in [String, Symbol] one of 'path', 'query', 'header', or 'cookie' - where to apply the named value
275
281
  # @param name [String, Symbol] the parameter name to apply the value to
276
282
  # @param value [Object] the value
@@ -305,7 +311,8 @@ module Scorpio
305
311
  elsif param_in == 'query'
306
312
  query_params ? query_params[name] : nil
307
313
  elsif param_in == 'header'
308
- headers[name]
314
+ _, value = headers.detect { |headername, _| headername.downcase == name.downcase }
315
+ value
309
316
  elsif param_in == 'cookie'
310
317
  raise(NotImplementedError, "cookies not implemented: #{name.inspect}")
311
318
  else
@@ -116,10 +116,7 @@ module Scorpio
116
116
  raise(ArgumentError, "openapi_document may not be overridden on subclass #{self.inspect} after it was set on #{openapi_document_class.inspect}")
117
117
  end
118
118
  end
119
- update_dynamic_methods
120
-
121
119
  # TODO blame validate openapi_document
122
-
123
120
  update_dynamic_methods
124
121
  end
125
122
 
@@ -129,9 +126,8 @@ module Scorpio
129
126
 
130
127
  def tag_name=(tag_name)
131
128
  unless tag_name.respond_to?(:to_str)
132
- raise(TypeError)
129
+ raise(TypeError, "tag_name must be a string; got: #{tag_name.inspect}")
133
130
  end
134
- set_on_class = self
135
131
  tag_name = tag_name.to_str
136
132
 
137
133
  begin
@@ -187,7 +183,9 @@ module Scorpio
187
183
  return false unless operation_for_resource_class?(operation)
188
184
 
189
185
  # define an instance method if the request schema is for this model
190
- request_resource_is_self = operation.request_schema && represented_schemas.include?(operation.request_schema)
186
+ request_resource_is_self = operation.request_schemas.any? do |request_schema|
187
+ represented_schemas.include?(request_schema)
188
+ end
191
189
 
192
190
  # also define an instance method depending on certain attributes the request description
193
191
  # might have in common with the model's schema attributes
@@ -222,6 +220,7 @@ module Scorpio
222
220
  tag_name_match = tag_name &&
223
221
  operation.tags.respond_to?(:to_ary) && # TODO maybe operation.tags.valid?
224
222
  operation.tags.include?(tag_name) &&
223
+ operation.operationId &&
225
224
  operation.operationId.match(/\A#{Regexp.escape(tag_name)}\.(\w+)\z/)
226
225
 
227
226
  if tag_name_match
@@ -360,11 +359,11 @@ module Scorpio
360
359
  if schema
361
360
  if schema['type'] == 'object'
362
361
  # TODO code dup with response_object_to_instances
363
- if schema['properties'] && schema['properties'][key]
362
+ if schema['properties'].respond_to?(:to_hash) && schema['properties'][key]
364
363
  subschema = schema['properties'][key]
365
364
  include_pair = true
366
365
  else
367
- if schema['patternProperties']
366
+ if schema['patternProperties'].respond_to?(:to_hash)
368
367
  _, pattern_schema = schema['patternProperties'].detect do |pattern, _|
369
368
  key =~ Regexp.new(pattern) # TODO map pattern to ruby syntax
370
369
  end
@@ -375,8 +374,7 @@ module Scorpio
375
374
  else
376
375
  if schema['additionalProperties'] == false
377
376
  include_pair = false
378
- elsif schema['additionalProperties'] == nil
379
- # TODO decide on this (can combine with `else` if treating nil same as schema present)
377
+ elsif [nil, true].include?(schema['additionalProperties'])
380
378
  include_pair = true
381
379
  subschema = nil
382
380
  else
@@ -20,7 +20,7 @@ module Scorpio
20
20
  HTTPError
21
21
  end
22
22
  if error_class
23
- message = "Error calling operation #{scorpio_request.operation.operationId}:\n" + response.body
23
+ message = "Error calling operation #{scorpio_request.operation.human_id}:\n" + response.body
24
24
  raise(error_class.new(message).tap do |e|
25
25
  e.ur = self
26
26
  e.response_object = response.body_object
@@ -1,3 +1,3 @@
1
1
  module Scorpio
2
- VERSION = "0.4.0"
2
+ VERSION = "0.4.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scorpio
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ethan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-04-27 00:00:00.000000000 Z
11
+ date: 2019-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jsi