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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c63ccc2d793787385be12175b68b1aca1b68372ee72bae4cb78e434d931aa941
4
- data.tar.gz: 3e9bec23f6ca7ad6905ca1fcd33ce185f1dd5741ad3a1097d0cbc82fea09fef1
3
+ metadata.gz: ad4505d3bd4e9dc299b95b21ce5305e7864200fb0481be3f2600f75c3476f73d
4
+ data.tar.gz: 613abb854fffaf49a45f8c9e19a0f150f0fef9b7500e50cf20dc2a6f58023347
5
5
  SHA512:
6
- metadata.gz: c7f265f3e6f68c340a2720a1f09238cd0f95d4a1dcfa8fb225ad4afa6e211fbe715c337d37faf7d8037ab0267b142af523182cf1d7eecca4e4cd80af16956a54
7
- data.tar.gz: 0a3601a2380269a73fde895e1a2d9c80617a2643ead58420866baa04f5d4a2bad5ce06c54b43f155283f53286b2c1bca09b527b2f7d494d9b766e9a188ae31b6
6
+ metadata.gz: 26cd883ee719f0e2683ac53c1be85d9296c90b92e80a799d938cf361c431ca486dd4e6a2db73539d7f133fa162fd104f85f4d9c9ec89b7f74e2187e8218b543b
7
+ data.tar.gz: 945afcb210c0b0ea8801393a3586b83045f82bcb94724021e3c69a6320ba32c23ddbeb6652644a6c3ea063830550f97e3001e661251662a8e6d976bc2cfdce62
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ # v0.6.4
2
+
3
+ - JSI ~> v0.8.1
4
+ - OpenAPI::Operation#each_link_page
5
+
6
+ # v0.6.3
7
+
8
+ - JSI = v0.8.0
9
+
1
10
  # v0.6.2
2
11
  - replace deprecated calls to JSI
3
12
 
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, though the underlying JSON instance is.
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 for the REST API
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::MetaschemaNode.new(
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.delete('id')
33
+ dup_doc = jsi_node_content.reject { |k, _| k == 'id' }
35
34
  if dup_doc['properties'].is_a?(Hash)
36
- required_properties = dup_doc['properties'].select do |key, value|
37
- value.is_a?(Hash) ? value.delete('required') : nil
38
- end.keys
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
- # we will be modifying the api document (RestDescription). clone self and modify that one.
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.rootUrl).host : ad.name, # uhh ... got nothin' better
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(*a, &b)
169
- Scorpio::Request.new(self, *a, &b)
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(*a, &b)
176
- build_request(*a, &b).run_ur
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(*a, &b)
183
- build_request(*a, &b).run
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, *a, &b)
9
+ def [](token, **kw)
10
10
  if respond_to?(:to_hash) && !key?(token)
11
11
  deref do |deref_jsi|
12
- return deref_jsi[token]
12
+ return(deref_jsi[token, **kw])
13
13
  end
14
14
  end
15
15
  return super
@@ -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::JSONSchemaOrgDraft04.new_schema(::YAML.load_file(Scorpio.root.join(
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
 
@@ -129,19 +129,27 @@ module Scorpio
129
129
  end
130
130
  include Configurables
131
131
 
132
- # @param operation [Scorpio::OpenAPI::Operation]
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(operation, configuration = {}, &b)
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}=").bind_call(self, value)
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
- # todo make a proper iterator interface
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 [void]
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 name [Scorpio::OpenAPI::Operation]
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 JSI::Util::FingerprintHash
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, modules|
501
- container_class = Class.new(Container)
502
- modules.each do |mod|
503
- container_class.include(mod)
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[modules] = container_class
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 << JSI::Base::HashNode
521
+ container_modules << Container::Hash
515
522
  end
516
523
  if object.respond_to?(:to_ary)
517
- container_modules << JSI::Base::ArrayNode
524
+ container_modules << Container::Array
518
525
  end
519
526
 
520
- container_modules += object.jsi_schemas.map do |schema|
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
 
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Scorpio
4
- VERSION = "0.6.2".freeze
4
+ VERSION = "0.6.4".freeze
5
5
  end
data/scorpio.gemspec CHANGED
@@ -1,7 +1,4 @@
1
- # coding: utf-8
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.7.0"
30
- spec.add_dependency "ur", "~> 0.2.1"
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.2
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: 2023-01-28 00:00:00.000000000 Z
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.7.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.7.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.1
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.1
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.1.6
120
+ rubygems_version: 3.4.10
121
121
  signing_key:
122
122
  specification_version: 4
123
123
  summary: Scorpio REST client