scorpio 0.6.2 → 0.6.4
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 +9 -0
- data/README.md +2 -4
- data/lib/scorpio/google_api_document.rb +11 -9
- data/lib/scorpio/openapi/operation.rb +52 -6
- data/lib/scorpio/openapi/reference.rb +2 -2
- data/lib/scorpio/openapi.rb +1 -1
- data/lib/scorpio/request.rb +20 -9
- data/lib/scorpio/resource_base.rb +70 -17
- data/lib/scorpio/response.rb +1 -1
- data/lib/scorpio/version.rb +1 -1
- data/scorpio.gemspec +3 -6
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad4505d3bd4e9dc299b95b21ce5305e7864200fb0481be3f2600f75c3476f73d
|
4
|
+
data.tar.gz: 613abb854fffaf49a45f8c9e19a0f150f0fef9b7500e50cf20dc2a6f58023347
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26cd883ee719f0e2683ac53c1be85d9296c90b92e80a799d938cf361c431ca486dd4e6a2db73539d7f133fa162fd104f85f4d9c9ec89b7f74e2187e8218b543b
|
7
|
+
data.tar.gz: 945afcb210c0b0ea8801393a3586b83045f82bcb94724021e3c69a6320ba32c23ddbeb6652644a6c3ea063830550f97e3001e661251662a8e6d976bc2cfdce62
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -172,11 +172,9 @@ pet_by_id = pet_store_doc.operations['getPetById'].run(petId: pet['id'])
|
|
172
172
|
|
173
173
|
# unlike ResourceBase instances above, JSI instances have stricter
|
174
174
|
# equality and the pets returned from different operations are not
|
175
|
-
# equal,
|
175
|
+
# equal, because they are in different JSON documents.
|
176
176
|
pet_by_id == pet
|
177
177
|
# => false
|
178
|
-
pet_by_id.jsi_instance == pet.jsi_instance
|
179
|
-
# => true
|
180
178
|
|
181
179
|
# let's name the pet after ourself
|
182
180
|
pet.name = ENV['USER']
|
@@ -210,7 +208,7 @@ A class which subclasses Scorpio::ResourceBase directly (such as PetStore::Resou
|
|
210
208
|
|
211
209
|
A model representing a resource needs to be configured, minimally, with:
|
212
210
|
|
213
|
-
- the OpenAPI document
|
211
|
+
- the OpenAPI document describing the API
|
214
212
|
- the schemas that represent instances of the model, if any
|
215
213
|
|
216
214
|
If the resource has HTTP operations associated with it (most, but not all resources will):
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Scorpio
|
4
4
|
module Google
|
5
5
|
discovery_rest_description_doc = ::JSON.parse(Scorpio.root.join('documents/www.googleapis.com/discovery/v1/apis/discovery/v1/rest').read)
|
6
|
-
discovery_rest_description = JSI::
|
6
|
+
discovery_rest_description = JSI::MetaSchemaNode.new(
|
7
7
|
discovery_rest_description_doc,
|
8
8
|
metaschema_root_ptr: JSI::Ptr['schemas']['JsonSchema'],
|
9
9
|
root_schema_ptr: JSI::Ptr['schemas']['RestDescription'],
|
@@ -29,13 +29,16 @@ module Scorpio
|
|
29
29
|
# google does a weird thing where it defines a schema with a $ref property where a json-schema is to be used in the document (method request and response fields), instead of just setting the schema to be the json-schema schema. we'll share a module across those schema classes that really represent schemas. is this confusingly meta enough?
|
30
30
|
module SchemaLike
|
31
31
|
def to_openapi
|
32
|
-
dup_doc = JSI::Util.as_json(self)
|
33
32
|
# openapi does not want an id field on schemas
|
34
|
-
dup_doc.
|
33
|
+
dup_doc = jsi_node_content.reject { |k, _| k == 'id' }
|
35
34
|
if dup_doc['properties'].is_a?(Hash)
|
36
|
-
required_properties =
|
37
|
-
|
38
|
-
|
35
|
+
required_properties = []
|
36
|
+
dup_doc['properties'].each do |key, value|
|
37
|
+
if value.is_a?(Hash) && value.key?('required')
|
38
|
+
required_properties.push(key) if value['required']
|
39
|
+
dup_doc = dup_doc.merge({'properties' => value.reject { |vk, _| vk == 'required' }})
|
40
|
+
end
|
41
|
+
end
|
39
42
|
# put required before properties
|
40
43
|
unless required_properties.empty?
|
41
44
|
dup_doc = dup_doc.map do |k, v|
|
@@ -55,8 +58,7 @@ module Scorpio
|
|
55
58
|
end
|
56
59
|
|
57
60
|
def to_openapi_hash(options = {})
|
58
|
-
|
59
|
-
ad = self.class.new(JSI::Util.as_json(self))
|
61
|
+
ad = self
|
60
62
|
ad_methods = []
|
61
63
|
if ad['methods']
|
62
64
|
ad_methods += ad['methods'].map do |mn, m|
|
@@ -164,7 +166,7 @@ module Scorpio
|
|
164
166
|
#'url' => '',
|
165
167
|
#},
|
166
168
|
},
|
167
|
-
'host' => ad.rootUrl ? Addressable::URI.parse(ad.rootUrl).host : ad.baseUrl ? Addressable::URI.parse(ad.
|
169
|
+
'host' => ad.rootUrl ? Addressable::URI.parse(ad.rootUrl).host : ad.baseUrl ? Addressable::URI.parse(ad.baseUrl).host : ad.name, # uhh ... got nothin' better
|
168
170
|
'basePath' => begin
|
169
171
|
path = ad.servicePath || ad.basePath || (ad.baseUrl ? Addressable::URI.parse(ad.baseUrl).path : '/')
|
170
172
|
path =~ %r(\A/) ? path : "/" + path
|
@@ -165,22 +165,68 @@ module Scorpio
|
|
165
165
|
# instantiates a {Scorpio::Request} for this operation.
|
166
166
|
# parameters are all passed to {Scorpio::Request#initialize}.
|
167
167
|
# @return [Scorpio::Request]
|
168
|
-
def build_request(
|
169
|
-
Scorpio::Request.
|
168
|
+
def build_request(configuration = {}, &b)
|
169
|
+
@request_class ||= Scorpio::Request.request_class_by_operation(self)
|
170
|
+
@request_class.new(configuration, &b)
|
170
171
|
end
|
171
172
|
|
172
173
|
# runs a {Scorpio::Request} for this operation, returning a {Scorpio::Ur}.
|
173
174
|
# parameters are all passed to {Scorpio::Request#initialize}.
|
174
175
|
# @return [Scorpio::Ur] response ur
|
175
|
-
def run_ur(
|
176
|
-
build_request(
|
176
|
+
def run_ur(configuration = {}, &b)
|
177
|
+
build_request(configuration, &b).run_ur
|
177
178
|
end
|
178
179
|
|
179
180
|
# runs a {Scorpio::Request} for this operation - see {Scorpio::Request#run}.
|
180
181
|
# parameters are all passed to {Scorpio::Request#initialize}.
|
181
182
|
# @return response body object
|
182
|
-
def run(
|
183
|
-
build_request(
|
183
|
+
def run(configuration = {}, &b)
|
184
|
+
build_request(configuration, &b).run
|
185
|
+
end
|
186
|
+
|
187
|
+
# Runs this operation with the given request config, and yields the resulting {Scorpio::Ur}.
|
188
|
+
# If the response contains a `Link` header with a `next` link (and that link's URL
|
189
|
+
# corresponds to this operation), this operation is run again to that link's URL, that
|
190
|
+
# request's Ur yielded, and a `next` link in that response is followed.
|
191
|
+
# This repeats until a response does not contain a `Link` header with a `next` link.
|
192
|
+
#
|
193
|
+
# @param configuration (see Scorpio::Request#initialize)
|
194
|
+
# @yield [Scorpio::Ur]
|
195
|
+
# @return [Enumerator, nil]
|
196
|
+
def each_link_page(configuration = {}, &block)
|
197
|
+
init_request = build_request(configuration)
|
198
|
+
next_page = proc do |last_page_ur|
|
199
|
+
nextlinks = last_page_ur.response.links.select { |link| link.rel?('next') }
|
200
|
+
if nextlinks.size == 0
|
201
|
+
# no next link; we are at the end
|
202
|
+
nil
|
203
|
+
elsif nextlinks.size == 1
|
204
|
+
nextlink = nextlinks.first
|
205
|
+
# we do not use Addressable::URI#join as the paths should just be concatenated, not resolved.
|
206
|
+
# we use File.join just to deal with consecutive slashes.
|
207
|
+
template = Addressable::Template.new(File.join(init_request.base_url, path_template_str))
|
208
|
+
target_uri = nextlink.absolute_target_uri
|
209
|
+
path_params = template.extract(target_uri.merge(query: nil))
|
210
|
+
unless path_params
|
211
|
+
raise("the URI of the link to the next page did not match the URI of this operation")
|
212
|
+
end
|
213
|
+
query_params = target_uri.query_values
|
214
|
+
run_ur(
|
215
|
+
path_params: path_params,
|
216
|
+
query_params: query_params,
|
217
|
+
)
|
218
|
+
else
|
219
|
+
# TODO better error class / context / message
|
220
|
+
raise("response included multiple links with rel=next")
|
221
|
+
end
|
222
|
+
end
|
223
|
+
init_request.each_page_ur(next_page: next_page, &block)
|
224
|
+
end
|
225
|
+
|
226
|
+
private
|
227
|
+
|
228
|
+
def jsi_object_group_text
|
229
|
+
[*super, http_method, path_template_str].freeze
|
184
230
|
end
|
185
231
|
end
|
186
232
|
|
@@ -6,10 +6,10 @@ module Scorpio
|
|
6
6
|
# overrides JSI::Base#[] to implicitly dereference this Reference, except when
|
7
7
|
# the given token is present in this Reference's instance (this should usually
|
8
8
|
# only apply to the token '$ref')
|
9
|
-
def [](token,
|
9
|
+
def [](token, **kw)
|
10
10
|
if respond_to?(:to_hash) && !key?(token)
|
11
11
|
deref do |deref_jsi|
|
12
|
-
return
|
12
|
+
return(deref_jsi[token, **kw])
|
13
13
|
end
|
14
14
|
end
|
15
15
|
return super
|
data/lib/scorpio/openapi.rb
CHANGED
@@ -21,7 +21,7 @@ module Scorpio
|
|
21
21
|
autoload :OperationsScope, 'scorpio/openapi/operations_scope'
|
22
22
|
|
23
23
|
module V3
|
24
|
-
openapi_document_schema = JSI::
|
24
|
+
openapi_document_schema = JSI::JSONSchemaDraft04.new_schema(::YAML.load_file(Scorpio.root.join(
|
25
25
|
'documents/github.com/OAI/OpenAPI-Specification/blob/oas3-schema/schemas/v3.0/schema.yaml'
|
26
26
|
)))
|
27
27
|
|
data/lib/scorpio/request.rb
CHANGED
@@ -129,19 +129,27 @@ module Scorpio
|
|
129
129
|
end
|
130
130
|
include Configurables
|
131
131
|
|
132
|
-
|
132
|
+
@request_class_by_operation = Hash.new do |h, op|
|
133
|
+
h[op] = Class.new(Request) do
|
134
|
+
define_method(:operation) { op }
|
135
|
+
include(op.request_accessor_module)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.request_class_by_operation(operation)
|
140
|
+
@request_class_by_operation[operation]
|
141
|
+
end
|
142
|
+
|
133
143
|
# @param configuration [#to_hash] a hash keyed with configurable attributes for
|
134
144
|
# the request - instance methods of Scorpio::Request::Configurables, whose values
|
135
145
|
# will be assigned for those attributes.
|
136
|
-
def initialize(
|
137
|
-
@operation = operation
|
138
|
-
|
146
|
+
def initialize(configuration = {}, &b)
|
139
147
|
configuration = JSI::Util.stringify_symbol_keys(configuration)
|
140
148
|
params_set = Set.new # the set of params that have been set
|
141
149
|
# do the Configurables first
|
142
150
|
configuration.each do |name, value|
|
143
151
|
if Configurables.public_method_defined?("#{name}=")
|
144
|
-
Configurables.instance_method("#{name}=").
|
152
|
+
Configurables.instance_method("#{name}=").bind(self).call(value)
|
145
153
|
params_set << name
|
146
154
|
end
|
147
155
|
end
|
@@ -151,8 +159,6 @@ module Scorpio
|
|
151
159
|
set_param_from(param['in'], param['name'], value)
|
152
160
|
end
|
153
161
|
|
154
|
-
extend operation.request_accessor_module
|
155
|
-
|
156
162
|
if block_given?
|
157
163
|
yield self
|
158
164
|
end
|
@@ -384,12 +390,17 @@ module Scorpio
|
|
384
390
|
ur.response.body_object
|
385
391
|
end
|
386
392
|
|
387
|
-
#
|
393
|
+
# Runs this request, passing the resulting Ur to the given block.
|
394
|
+
# The `next_page` callable is then called with that Ur and results in the next page's Ur, or nil.
|
395
|
+
# This repeats until the `next_page` call results in nil.
|
396
|
+
#
|
397
|
+
# See {OpenAPI::Operation#each_link_page} for integration with an OpenAPI Operation.
|
398
|
+
#
|
388
399
|
# @param next_page [#call] a callable which will take a parameter `page_ur`, which is a {Scorpio::Ur},
|
389
400
|
# and must result in an Ur representing the next page, which will be yielded to the block.
|
390
401
|
# @yield [Scorpio::Ur] yields the first page, and each subsequent result of calls to `next_page` until
|
391
402
|
# that results in nil
|
392
|
-
# @return [
|
403
|
+
# @return [Enumerator, nil]
|
393
404
|
def each_page_ur(next_page: , raise_on_http_error: true)
|
394
405
|
return to_enum(__method__, next_page: next_page, raise_on_http_error: raise_on_http_error) unless block_given?
|
395
406
|
page_ur = run_ur
|
@@ -211,7 +211,7 @@ module Scorpio
|
|
211
211
|
end
|
212
212
|
|
213
213
|
# @private
|
214
|
-
# @param
|
214
|
+
# @param operation [Scorpio::OpenAPI::Operation]
|
215
215
|
# @return [String, nil]
|
216
216
|
def api_method_name_by_operation(operation)
|
217
217
|
raise(ArgumentError, operation.pretty_inspect) unless operation.is_a?(Scorpio::OpenAPI::Operation)
|
@@ -439,7 +439,7 @@ module Scorpio
|
|
439
439
|
end
|
440
440
|
end
|
441
441
|
|
442
|
-
include
|
442
|
+
include(JSI::Util::Private::FingerprintHash)
|
443
443
|
|
444
444
|
def jsi_fingerprint
|
445
445
|
{class: self.class, contained_object: as_json}
|
@@ -497,39 +497,92 @@ module Scorpio
|
|
497
497
|
|
498
498
|
class ResourceBase
|
499
499
|
class Container
|
500
|
-
@container_classes = Hash.new do |h,
|
501
|
-
|
502
|
-
|
503
|
-
|
500
|
+
@container_classes = Hash.new do |h, key|
|
501
|
+
modules, schemas = key[:modules], key[:schemas]
|
502
|
+
|
503
|
+
container_class = Class.new(Container) do
|
504
|
+
modules.each do |mod|
|
505
|
+
include(mod)
|
506
|
+
end
|
507
|
+
|
508
|
+
schemas.each do |schema|
|
509
|
+
include(JSI::SchemaClasses.schema_property_reader_module(schema, conflicting_modules: modules + [Container]))
|
510
|
+
include(JSI::SchemaClasses.schema_property_writer_module(schema, conflicting_modules: modules + [Container]))
|
511
|
+
end
|
504
512
|
end
|
505
|
-
h[
|
513
|
+
h[key] = container_class
|
506
514
|
end
|
507
515
|
|
508
516
|
class << self
|
509
517
|
def new_container(object, openapi_document_class, options = {})
|
510
518
|
container_modules = Set[]
|
511
519
|
|
512
|
-
# TODO this is JSI internals that scorpio shouldn't really be using
|
513
520
|
if object.respond_to?(:to_hash)
|
514
|
-
container_modules <<
|
521
|
+
container_modules << Container::Hash
|
515
522
|
end
|
516
523
|
if object.respond_to?(:to_ary)
|
517
|
-
container_modules <<
|
524
|
+
container_modules << Container::Array
|
518
525
|
end
|
519
526
|
|
520
|
-
container_modules
|
521
|
-
JSI::SchemaClasses.accessor_module_for_schema(schema,
|
522
|
-
conflicting_modules: container_modules + [Container],
|
523
|
-
)
|
524
|
-
end
|
525
|
-
|
526
|
-
container_class = @container_classes[container_modules.freeze]
|
527
|
+
container_class = @container_classes[{modules: container_modules.freeze, schemas: object.jsi_schemas}]
|
527
528
|
|
528
529
|
container_class.new(object, openapi_document_class, options)
|
529
530
|
end
|
530
531
|
end
|
531
532
|
end
|
532
533
|
|
534
|
+
module Container::Hash
|
535
|
+
include(Enumerable)
|
536
|
+
include(JSI::Util::Hashlike) # TODO this is JSI internals that scorpio shouldn't really be using
|
537
|
+
|
538
|
+
def each(**kw, &block)
|
539
|
+
return(to_enum(__method__, **kw) { contained_object.size }) unless block
|
540
|
+
if block.arity > 1
|
541
|
+
contained_object.each_key { |k| yield(k, self[k, **kw]) }
|
542
|
+
else
|
543
|
+
contained_object.each_key { |k| yield([k, self[k, **kw]]) }
|
544
|
+
end
|
545
|
+
self
|
546
|
+
end
|
547
|
+
|
548
|
+
def to_hash(**kw)
|
549
|
+
hash = {}
|
550
|
+
contained_object.each_key { |k| hash[k] = self[k, **kw] }
|
551
|
+
hash
|
552
|
+
end
|
553
|
+
|
554
|
+
def as_json(options = {})
|
555
|
+
hash = {}
|
556
|
+
each_key do |k|
|
557
|
+
ks = k.is_a?(String) ? k :
|
558
|
+
k.is_a?(Symbol) ? k.to_s :
|
559
|
+
k.respond_to?(:to_str) && (kstr = k.to_str).is_a?(String) ? kstr :
|
560
|
+
raise(TypeError, "JSON object (Hash) cannot be keyed with: #{k.pretty_inspect.chomp}")
|
561
|
+
hash[ks] = JSI::Util.as_json(self[k], **options)
|
562
|
+
end
|
563
|
+
hash
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
module Container::Array
|
568
|
+
include(Enumerable)
|
569
|
+
include(JSI::Util::Arraylike) # TODO this is JSI internals that scorpio shouldn't really be using
|
570
|
+
|
571
|
+
def each(**kw, &block)
|
572
|
+
return(to_enum(__method__, **kw) { contained_object.size }) unless block
|
573
|
+
contained_object.each_index { |i| yield(self[i, **kw]) }
|
574
|
+
self
|
575
|
+
end
|
576
|
+
|
577
|
+
def to_ary(**kw)
|
578
|
+
to_a(**kw)
|
579
|
+
end
|
580
|
+
|
581
|
+
def as_json(options = {})
|
582
|
+
each_index.map { |i| JSI::Util.as_json(self[i], **options) }
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
533
586
|
class Container
|
534
587
|
include Containment
|
535
588
|
|
data/lib/scorpio/response.rb
CHANGED
@@ -26,7 +26,7 @@ module Scorpio
|
|
26
26
|
end
|
27
27
|
|
28
28
|
if response_schema && (body_object.respond_to?(:to_hash) || body_object.respond_to?(:to_ary))
|
29
|
-
body_object = response_schema.new_jsi(body_object)
|
29
|
+
body_object = response_schema.new_jsi(body_object, mutable: true)
|
30
30
|
end
|
31
31
|
|
32
32
|
body_object
|
data/lib/scorpio/version.rb
CHANGED
data/scorpio.gemspec
CHANGED
@@ -1,7 +1,4 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'scorpio/version'
|
1
|
+
require_relative('lib/scorpio/version')
|
5
2
|
|
6
3
|
Gem::Specification.new do |spec|
|
7
4
|
spec.name = "scorpio"
|
@@ -26,8 +23,8 @@ Gem::Specification.new do |spec|
|
|
26
23
|
|
27
24
|
spec.require_paths = ["lib"]
|
28
25
|
|
29
|
-
spec.add_dependency "jsi", "~> 0.
|
30
|
-
spec.add_dependency "ur", "~> 0.2.
|
26
|
+
spec.add_dependency "jsi", "~> 0.8.1"
|
27
|
+
spec.add_dependency "ur", "~> 0.2.5"
|
31
28
|
spec.add_dependency "faraday", "< 3.0"
|
32
29
|
spec.add_dependency "addressable", '~> 2.3'
|
33
30
|
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.6.
|
4
|
+
version: 0.6.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ethan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-07-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jsi
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.8.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.8.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: ur
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.2.
|
33
|
+
version: 0.2.5
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.2.
|
40
|
+
version: 0.2.5
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: faraday
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -117,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
117
|
- !ruby/object:Gem::Version
|
118
118
|
version: '0'
|
119
119
|
requirements: []
|
120
|
-
rubygems_version: 3.
|
120
|
+
rubygems_version: 3.4.10
|
121
121
|
signing_key:
|
122
122
|
specification_version: 4
|
123
123
|
summary: Scorpio REST client
|