scorpio 0.6.2 → 0.6.4

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: 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