scorpio 0.5.0 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/LICENSE.md +1 -1
- data/README.md +26 -17
- data/lib/scorpio/google_api_document.rb +9 -1
- data/lib/scorpio/openapi/document.rb +4 -2
- data/lib/scorpio/openapi/operation.rb +90 -40
- data/lib/scorpio/openapi/operations_scope.rb +13 -11
- data/lib/scorpio/openapi/reference.rb +27 -2
- data/lib/scorpio/openapi/tag.rb +15 -0
- data/lib/scorpio/openapi/v3/server.rb +3 -1
- data/lib/scorpio/openapi.rb +55 -17
- data/lib/scorpio/pickle_adapter.rb +2 -0
- data/lib/scorpio/request.rb +47 -32
- data/lib/scorpio/resource_base.rb +234 -201
- data/lib/scorpio/response.rb +6 -4
- data/lib/scorpio/ur.rb +7 -3
- data/lib/scorpio/version.rb +3 -1
- data/lib/scorpio.rb +5 -6
- data/scorpio.gemspec +15 -23
- metadata +21 -220
- data/.simplecov +0 -1
- data/Rakefile +0 -10
- data/bin/documents_to_yml.rb +0 -33
- data/resources/icons/AGPL-3.0.png +0 -0
- data/test/blog.openapi2.yml +0 -113
- data/test/blog.openapi3.yml +0 -131
- data/test/blog.rb +0 -117
- data/test/blog.rest_description.yml +0 -67
- data/test/blog_scorpio_models.rb +0 -49
- data/test/scorpio_test.rb +0 -105
- data/test/test_helper.rb +0 -86
data/lib/scorpio/openapi.rb
CHANGED
@@ -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
|
-
|
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 =
|
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
|
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
|
-
|
90
|
-
|
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
|
-
|
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 =
|
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
|
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
|
-
|
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
|
-
|
162
|
-
|
163
|
-
|
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
|
data/lib/scorpio/request.rb
CHANGED
@@ -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}=").
|
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
|
-
#
|
153
|
+
# the http method for this request
|
154
|
+
# @return [String]
|
152
155
|
def http_method
|
153
|
-
operation.http_method
|
156
|
+
operation.http_method
|
154
157
|
end
|
155
158
|
|
156
|
-
#
|
157
|
-
#
|
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
|
-
#
|
163
|
-
#
|
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)
|
178
|
-
|
179
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
203
|
-
#
|
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
|
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]
|
244
|
-
# @raise
|
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
|
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
|
-
#
|
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
|
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
|
-
#
|
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]
|
298
|
-
# @raise [ArgumentError] invalid
|
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
|