scorpio 0.3.1 → 0.4.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/.yardopts +1 -0
- data/CHANGELOG.md +7 -0
- data/README.md +95 -2
- data/documents/github.com/OAI/OpenAPI-Specification/blob/oas3-schema/schemas/v3.0/schema.yaml +1495 -0
- data/lib/scorpio.rb +18 -0
- data/lib/scorpio/openapi.rb +63 -62
- data/lib/scorpio/openapi/document.rb +17 -0
- data/lib/scorpio/openapi/operation.rb +87 -5
- data/lib/scorpio/openapi/operations_scope.rb +9 -3
- data/lib/scorpio/openapi/v3/server.rb +10 -0
- data/lib/scorpio/request.rb +148 -12
- data/lib/scorpio/resource_base.rb +15 -12
- data/lib/scorpio/response.rb +4 -0
- data/lib/scorpio/ur.rb +12 -0
- data/lib/scorpio/version.rb +1 -1
- data/scorpio.gemspec +1 -0
- metadata +18 -4
- data/documents/openapis.org/v3/schema.json +0 -1239
- data/documents/openapis.org/v3/schema.yml +0 -794
    
        data/lib/scorpio.rb
    CHANGED
    
    | @@ -19,8 +19,18 @@ module Scorpio | |
| 19 19 | 
             
              end
         | 
| 20 20 |  | 
| 21 21 | 
             
              proc { |v| define_singleton_method(:error_classes_by_status) { v } }.call({})
         | 
| 22 | 
            +
              # Scorpio::Error encompasses certain Scorpio-defined errors encountered in using Scorpio.
         | 
| 23 | 
            +
              # at the moment this only includes HTTPError[^1], but will likely cover errors in schemas and
         | 
| 24 | 
            +
              # other things in the future.
         | 
| 25 | 
            +
              #
         | 
| 26 | 
            +
              # [^1]: unless I have, since writing this, implemented other things but forgotten to update this
         | 
| 27 | 
            +
              # comment, which does seem likely enough.
         | 
| 22 28 | 
             
              class Error < StandardError; end
         | 
| 23 29 | 
             
              class HTTPError < Error
         | 
| 30 | 
            +
                # for HTTPError subclasses representing a single status, sets and/or returns the represented status.
         | 
| 31 | 
            +
                #
         | 
| 32 | 
            +
                # @param status [Integer] if specified, sets the HTTP status the class represents
         | 
| 33 | 
            +
                # @return [Integer] the HTTP status the class represents
         | 
| 24 34 | 
             
                def self.status(status = nil)
         | 
| 25 35 | 
             
                  if status
         | 
| 26 36 | 
             
                    @status = status
         | 
| @@ -35,7 +45,9 @@ module Scorpio | |
| 35 45 | 
             
              # be referred to like Scorpio::BadRequest400Error. this is just to avoid clutter in the Scorpio
         | 
| 36 46 | 
             
              # namespace in yardoc.
         | 
| 37 47 | 
             
              module HTTPErrors
         | 
| 48 | 
            +
                # an HTTP response with a status of 400-499
         | 
| 38 49 | 
             
                class ClientError < HTTPError; end
         | 
| 50 | 
            +
                # an HTTP response with a status of 500-599
         | 
| 39 51 | 
             
                class ServerError < HTTPError; end
         | 
| 40 52 |  | 
| 41 53 | 
             
                class BadRequest400Error < ClientError;                    status(400); end
         | 
| @@ -82,6 +94,12 @@ module Scorpio | |
| 82 94 | 
             
              include HTTPErrors
         | 
| 83 95 | 
             
              error_classes_by_status.freeze
         | 
| 84 96 |  | 
| 97 | 
            +
              class ConfigError < Error
         | 
| 98 | 
            +
                attr_accessor :name
         | 
| 99 | 
            +
              end
         | 
| 100 | 
            +
              class AmbiguousParameter < ConfigError
         | 
| 101 | 
            +
              end
         | 
| 102 | 
            +
             | 
| 85 103 | 
             
              autoload :Google, 'scorpio/google_api_document'
         | 
| 86 104 | 
             
              autoload :OpenAPI, 'scorpio/openapi'
         | 
| 87 105 | 
             
              autoload :Ur, 'scorpio/ur'
         | 
    
        data/lib/scorpio/openapi.rb
    CHANGED
    
    | @@ -5,7 +5,7 @@ module Scorpio | |
| 5 5 | 
             
                autoload :OperationsScope, 'scorpio/openapi/operations_scope'
         | 
| 6 6 |  | 
| 7 7 | 
             
                module V3
         | 
| 8 | 
            -
                  openapi_schema = JSI::Schema.new(:: | 
| 8 | 
            +
                  openapi_schema = JSI::Schema.new(::YAML.load_file(Scorpio.root.join('documents/github.com/OAI/OpenAPI-Specification/blob/oas3-schema/schemas/v3.0/schema.yaml')))
         | 
| 9 9 | 
             
                  openapi_class = proc do |*key|
         | 
| 10 10 | 
             
                    JSI.class_for_schema(key.inject(openapi_schema, &:[]))
         | 
| 11 11 | 
             
                  end
         | 
| @@ -13,66 +13,67 @@ module Scorpio | |
| 13 13 | 
             
                  Document = openapi_class.call()
         | 
| 14 14 |  | 
| 15 15 | 
             
                  # naming these is not strictly necessary, but is nice to have.
         | 
| 16 | 
            -
                  # generated: puts  | 
| 17 | 
            -
                   | 
| 18 | 
            -
                   | 
| 19 | 
            -
                   | 
| 20 | 
            -
                   | 
| 21 | 
            -
                   | 
| 22 | 
            -
                   | 
| 23 | 
            -
                   | 
| 24 | 
            -
                   | 
| 25 | 
            -
                   | 
| 26 | 
            -
                   | 
| 27 | 
            -
                   | 
| 28 | 
            -
                   | 
| 29 | 
            -
                   | 
| 30 | 
            -
                   | 
| 31 | 
            -
                   | 
| 32 | 
            -
                   | 
| 33 | 
            -
                   | 
| 34 | 
            -
                   | 
| 35 | 
            -
                   | 
| 36 | 
            -
                   | 
| 37 | 
            -
                   | 
| 38 | 
            -
                   | 
| 39 | 
            -
                   | 
| 40 | 
            -
                   | 
| 41 | 
            -
                   | 
| 42 | 
            -
                   | 
| 43 | 
            -
                   | 
| 44 | 
            -
                   | 
| 45 | 
            -
                   | 
| 46 | 
            -
                   | 
| 47 | 
            -
                   | 
| 48 | 
            -
                   | 
| 49 | 
            -
                   | 
| 50 | 
            -
                   | 
| 51 | 
            -
                   | 
| 52 | 
            -
                   | 
| 53 | 
            -
                   | 
| 54 | 
            -
                   | 
| 55 | 
            -
                   | 
| 56 | 
            -
                   | 
| 57 | 
            -
                   | 
| 58 | 
            -
                   | 
| 59 | 
            -
                   | 
| 60 | 
            -
                   | 
| 61 | 
            -
                   | 
| 62 | 
            -
                   | 
| 63 | 
            -
                   | 
| 64 | 
            -
                   | 
| 65 | 
            -
                   | 
| 66 | 
            -
                   | 
| 67 | 
            -
                   | 
| 68 | 
            -
                   | 
| 69 | 
            -
                   | 
| 70 | 
            -
                   | 
| 71 | 
            -
                   | 
| 72 | 
            -
                   | 
| 73 | 
            -
                   | 
| 74 | 
            -
                   | 
| 75 | 
            -
                   | 
| 16 | 
            +
                  # generated: `puts JSI::Schema.new(::YAML.load_file(Scorpio.root.join('documents/github.com/OAI/OpenAPI-Specification/blob/oas3-schema/schemas/v3.0/schema.yaml')))['definitions'].select { |k,v| ['object', nil].include?(v['type']) }.keys.map { |k| "#{k[0].upcase}#{k[1..-1]} = openapi_class.call('definitions', '#{k}')" }`
         | 
| 17 | 
            +
                  Reference  = openapi_class.call('definitions', 'Reference')
         | 
| 18 | 
            +
                  Info        = openapi_class.call('definitions', 'Info')
         | 
| 19 | 
            +
                  Contact      = openapi_class.call('definitions', 'Contact')
         | 
| 20 | 
            +
                  License       = openapi_class.call('definitions', 'License')
         | 
| 21 | 
            +
                  Server         = openapi_class.call('definitions', 'Server')
         | 
| 22 | 
            +
                  ServerVariable  = openapi_class.call('definitions', 'ServerVariable')
         | 
| 23 | 
            +
                  Components       = openapi_class.call('definitions', 'Components')
         | 
| 24 | 
            +
                  Schema            = openapi_class.call('definitions', 'Schema')
         | 
| 25 | 
            +
                  Discriminator      = openapi_class.call('definitions', 'Discriminator')
         | 
| 26 | 
            +
                  XML                 = openapi_class.call('definitions', 'XML')
         | 
| 27 | 
            +
                  Response             = openapi_class.call('definitions', 'Response')
         | 
| 28 | 
            +
                  MediaType             = openapi_class.call('definitions', 'MediaType')
         | 
| 29 | 
            +
                  MediaTypeWithExample   = openapi_class.call('definitions', 'MediaTypeWithExample')
         | 
| 30 | 
            +
                  MediaTypeWithExamples   = openapi_class.call('definitions', 'MediaTypeWithExamples')
         | 
| 31 | 
            +
                  Example                  = openapi_class.call('definitions', 'Example')
         | 
| 32 | 
            +
                  Header                    = openapi_class.call('definitions', 'Header')
         | 
| 33 | 
            +
                  HeaderWithSchema           = openapi_class.call('definitions', 'HeaderWithSchema')
         | 
| 34 | 
            +
                  HeaderWithSchemaWithExample = openapi_class.call('definitions', 'HeaderWithSchemaWithExample')
         | 
| 35 | 
            +
                  HeaderWithSchemaWithExamples = openapi_class.call('definitions', 'HeaderWithSchemaWithExamples')
         | 
| 36 | 
            +
                  HeaderWithContent           = openapi_class.call('definitions', 'HeaderWithContent')
         | 
| 37 | 
            +
                  Paths                      = openapi_class.call('definitions', 'Paths')
         | 
| 38 | 
            +
                  PathItem                    = openapi_class.call('definitions', 'PathItem')
         | 
| 39 | 
            +
                  Operation                    = openapi_class.call('definitions', 'Operation')
         | 
| 40 | 
            +
                  Responses                     = openapi_class.call('definitions', 'Responses')
         | 
| 41 | 
            +
                  SecurityRequirement            = openapi_class.call('definitions', 'SecurityRequirement')
         | 
| 42 | 
            +
                  Tag                             = openapi_class.call('definitions', 'Tag')
         | 
| 43 | 
            +
                  ExternalDocumentation            = openapi_class.call('definitions', 'ExternalDocumentation')
         | 
| 44 | 
            +
                  Parameter                         = openapi_class.call('definitions', 'Parameter')
         | 
| 45 | 
            +
                  ParameterWithSchema                = openapi_class.call('definitions', 'ParameterWithSchema')
         | 
| 46 | 
            +
                  ParameterWithSchemaWithExample      = openapi_class.call('definitions', 'ParameterWithSchemaWithExample')
         | 
| 47 | 
            +
                  ParameterWithSchemaWithExampleInPath = openapi_class.call('definitions', 'ParameterWithSchemaWithExampleInPath')
         | 
| 48 | 
            +
                  ParameterWithSchemaWithExampleInQuery = openapi_class.call('definitions', 'ParameterWithSchemaWithExampleInQuery')
         | 
| 49 | 
            +
                  ParameterWithSchemaWithExampleInHeader = openapi_class.call('definitions', 'ParameterWithSchemaWithExampleInHeader')
         | 
| 50 | 
            +
                  ParameterWithSchemaWithExampleInCookie = openapi_class.call('definitions', 'ParameterWithSchemaWithExampleInCookie')
         | 
| 51 | 
            +
                  ParameterWithSchemaWithExamples       = openapi_class.call('definitions', 'ParameterWithSchemaWithExamples')
         | 
| 52 | 
            +
                  ParameterWithSchemaWithExamplesInPath = openapi_class.call('definitions', 'ParameterWithSchemaWithExamplesInPath')
         | 
| 53 | 
            +
                  ParameterWithSchemaWithExamplesInQuery = openapi_class.call('definitions', 'ParameterWithSchemaWithExamplesInQuery')
         | 
| 54 | 
            +
                  ParameterWithSchemaWithExamplesInHeader = openapi_class.call('definitions', 'ParameterWithSchemaWithExamplesInHeader')
         | 
| 55 | 
            +
                  ParameterWithSchemaWithExamplesInCookie = openapi_class.call('definitions', 'ParameterWithSchemaWithExamplesInCookie')
         | 
| 56 | 
            +
                  ParameterWithContent                   = openapi_class.call('definitions', 'ParameterWithContent')
         | 
| 57 | 
            +
                  ParameterWithContentInPath            = openapi_class.call('definitions', 'ParameterWithContentInPath')
         | 
| 58 | 
            +
                  ParameterWithContentNotInPath        = openapi_class.call('definitions', 'ParameterWithContentNotInPath')
         | 
| 59 | 
            +
                  RequestBody                         = openapi_class.call('definitions', 'RequestBody')
         | 
| 60 | 
            +
                  SecurityScheme                     = openapi_class.call('definitions', 'SecurityScheme')
         | 
| 61 | 
            +
                  APIKeySecurityScheme              = openapi_class.call('definitions', 'APIKeySecurityScheme')
         | 
| 62 | 
            +
                  HTTPSecurityScheme               = openapi_class.call('definitions', 'HTTPSecurityScheme')
         | 
| 63 | 
            +
                  NonBearerHTTPSecurityScheme     = openapi_class.call('definitions', 'NonBearerHTTPSecurityScheme')
         | 
| 64 | 
            +
                  BearerHTTPSecurityScheme       = openapi_class.call('definitions', 'BearerHTTPSecurityScheme')
         | 
| 65 | 
            +
                  OAuth2SecurityScheme          = openapi_class.call('definitions', 'OAuth2SecurityScheme')
         | 
| 66 | 
            +
                  OpenIdConnectSecurityScheme  = openapi_class.call('definitions', 'OpenIdConnectSecurityScheme')
         | 
| 67 | 
            +
                  OAuthFlows                  = openapi_class.call('definitions', 'OAuthFlows')
         | 
| 68 | 
            +
                  ImplicitOAuthFlow          = openapi_class.call('definitions', 'ImplicitOAuthFlow')
         | 
| 69 | 
            +
                  PasswordOAuthFlow         = openapi_class.call('definitions', 'PasswordOAuthFlow')
         | 
| 70 | 
            +
                  ClientCredentialsFlow     = openapi_class.call('definitions', 'ClientCredentialsFlow')
         | 
| 71 | 
            +
                  AuthorizationCodeOAuthFlow = openapi_class.call('definitions', 'AuthorizationCodeOAuthFlow')
         | 
| 72 | 
            +
                  Link                      = openapi_class.call('definitions', 'Link')
         | 
| 73 | 
            +
                  LinkWithOperationRef     = openapi_class.call('definitions', 'LinkWithOperationRef')
         | 
| 74 | 
            +
                  LinkWithOperationId     = openapi_class.call('definitions', 'LinkWithOperationId')
         | 
| 75 | 
            +
                  Callback               = openapi_class.call('definitions', 'Callback')
         | 
| 76 | 
            +
                  Encoding              = openapi_class.call('definitions', 'Encoding')
         | 
| 76 77 | 
             
                end
         | 
| 77 78 | 
             
                module V2
         | 
| 78 79 | 
             
                  openapi_schema = JSI::Schema.new(::JSON.parse(Scorpio.root.join('documents/swagger.io/v2/schema.json').read))
         | 
| @@ -83,7 +84,7 @@ module Scorpio | |
| 83 84 | 
             
                  Document = openapi_class.call()
         | 
| 84 85 |  | 
| 85 86 | 
             
                  # naming these is not strictly necessary, but is nice to have.
         | 
| 86 | 
            -
                  # 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]} = openapi_class.call('definitions', '#{k}')" }
         | 
| 87 | 
            +
                  # 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]} = openapi_class.call('definitions', '#{k}')" }`
         | 
| 87 88 | 
             
                  Info            = openapi_class.call('definitions', 'info')
         | 
| 88 89 | 
             
                  Contact          = openapi_class.call('definitions', 'contact')
         | 
| 89 90 | 
             
                  License           = openapi_class.call('definitions', 'license')
         | 
| @@ -1,7 +1,16 @@ | |
| 1 1 | 
             
            module Scorpio
         | 
| 2 2 | 
             
              module OpenAPI
         | 
| 3 | 
            +
                # A document that defines or describes an API.
         | 
| 4 | 
            +
                # An OpenAPI definition uses and conforms to the OpenAPI Specification.
         | 
| 5 | 
            +
                #
         | 
| 6 | 
            +
                # Scorpio::OpenAPI::Document is a module common to V2 and V3 documents.
         | 
| 3 7 | 
             
                module Document
         | 
| 4 8 | 
             
                  class << self
         | 
| 9 | 
            +
                    # takes a document, generally a Hash, and returns a Scorpio OpenAPI Document
         | 
| 10 | 
            +
                    # instantiating it.
         | 
| 11 | 
            +
                    #
         | 
| 12 | 
            +
                    # @param instance [#to_hash] the document to represent as a Scorpio OpenAPI Document
         | 
| 13 | 
            +
                    # @return [Scorpio::OpenAPI::V2::Document, Scorpio::OpenAPI::V3::Document]
         | 
| 5 14 | 
             
                    def from_instance(instance)
         | 
| 6 15 | 
             
                      if instance.is_a?(Hash)
         | 
| 7 16 | 
             
                        instance = JSI::JSON::Node.new_doc(instance)
         | 
| @@ -76,6 +85,10 @@ module Scorpio | |
| 76 85 |  | 
| 77 86 | 
             
                module V3
         | 
| 78 87 | 
             
                  raise(Bug) unless const_defined?(:Document)
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  # A document that defines or describes an API conforming to the OpenAPI Specification v3.
         | 
| 90 | 
            +
                  #
         | 
| 91 | 
            +
                  # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#oasObject
         | 
| 79 92 | 
             
                  class Document
         | 
| 80 93 | 
             
                    module Configurables
         | 
| 81 94 | 
             
                      def scheme
         | 
| @@ -115,6 +128,10 @@ module Scorpio | |
| 115 128 |  | 
| 116 129 | 
             
                module V2
         | 
| 117 130 | 
             
                  raise(Bug) unless const_defined?(:Document)
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                  # A document that defines or describes an API conforming to the OpenAPI Specification v2 (aka Swagger).
         | 
| 133 | 
            +
                  #
         | 
| 134 | 
            +
                  # The root document is known as the Swagger Object.
         | 
| 118 135 | 
             
                  class Document
         | 
| 119 136 | 
             
                    module Configurables
         | 
| 120 137 | 
             
                      attr_writer :scheme
         | 
| @@ -1,5 +1,8 @@ | |
| 1 1 | 
             
            module Scorpio
         | 
| 2 2 | 
             
              module OpenAPI
         | 
| 3 | 
            +
                # An OpenAPI operation
         | 
| 4 | 
            +
                #
         | 
| 5 | 
            +
                # Scorpio::OpenAPI::Operation is a module common to V2 and V3 operations.
         | 
| 3 6 | 
             
                module Operation
         | 
| 4 7 | 
             
                  module Configurables
         | 
| 5 8 | 
             
                    attr_writer :base_url
         | 
| @@ -40,13 +43,24 @@ module Scorpio | |
| 40 43 | 
             
                  end
         | 
| 41 44 | 
             
                  include Configurables
         | 
| 42 45 |  | 
| 46 | 
            +
                  # @return [Boolean] v3?
         | 
| 47 | 
            +
                  def v3?
         | 
| 48 | 
            +
                    is_a?(V3::Operation)
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  # @return [Boolean] v2?
         | 
| 52 | 
            +
                  def v2?
         | 
| 53 | 
            +
                    is_a?(V2::Operation)
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  # @return [Scorpio::OpenAPI::Document] the document whence this operation came
         | 
| 43 57 | 
             
                  def openapi_document
         | 
| 44 58 | 
             
                    parents.detect { |p| p.is_a?(Scorpio::OpenAPI::Document) }
         | 
| 45 59 | 
             
                  end
         | 
| 46 60 |  | 
| 47 | 
            -
                  def  | 
| 48 | 
            -
                    return @ | 
| 49 | 
            -
                    @ | 
| 61 | 
            +
                  def path_template_str
         | 
| 62 | 
            +
                    return @path_template_str if instance_variable_defined?(:@path_template_str)
         | 
| 63 | 
            +
                    @path_template_str = begin
         | 
| 50 64 | 
             
                      parent_is_pathitem = parent.is_a?(Scorpio::OpenAPI::V2::PathItem) || parent.is_a?(Scorpio::OpenAPI::V3::PathItem)
         | 
| 51 65 | 
             
                      parent_parent_is_paths = parent.parent.is_a?(Scorpio::OpenAPI::V2::Paths) || parent.parent.is_a?(Scorpio::OpenAPI::V3::Paths)
         | 
| 52 66 | 
             
                      if parent_is_pathitem && parent_parent_is_paths
         | 
| @@ -55,6 +69,24 @@ module Scorpio | |
| 55 69 | 
             
                    end
         | 
| 56 70 | 
             
                  end
         | 
| 57 71 |  | 
| 72 | 
            +
                  # @return [Addressable::Template] the path as an Addressable::Template
         | 
| 73 | 
            +
                  def path_template
         | 
| 74 | 
            +
                    return @path_template if instance_variable_defined?(:@path_template)
         | 
| 75 | 
            +
                    @path_template = Addressable::Template.new(path_template_str)
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  # @param base_url [#to_str] the base URL to which the path template is appended
         | 
| 79 | 
            +
                  # @return [Addressable::Template] the URI template, consisting of the base_url
         | 
| 80 | 
            +
                  #   concatenated with the path template
         | 
| 81 | 
            +
                  def uri_template(base_url: self.base_url)
         | 
| 82 | 
            +
                    unless base_url
         | 
| 83 | 
            +
                      raise(ArgumentError, "no base_url has been specified for operation #{self}")
         | 
| 84 | 
            +
                    end
         | 
| 85 | 
            +
                    # we do not use Addressable::URI#join as the paths should just be concatenated, not resolved.
         | 
| 86 | 
            +
                    # we use File.join just to deal with consecutive slashes.
         | 
| 87 | 
            +
                    Addressable::Template.new(File.join(base_url, path_template_str))
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
             | 
| 58 90 | 
             
                  def http_method
         | 
| 59 91 | 
             
                    return @http_method if instance_variable_defined?(:@http_method)
         | 
| 60 92 | 
             
                    @http_method = begin
         | 
| @@ -65,6 +97,49 @@ module Scorpio | |
| 65 97 | 
             
                    end
         | 
| 66 98 | 
             
                  end
         | 
| 67 99 |  | 
| 100 | 
            +
                  # this method is not intended to be API-stable at the moment.
         | 
| 101 | 
            +
                  #
         | 
| 102 | 
            +
                  # @return [#to_ary<#to_h>] the parameters specified for this operation, plus any others
         | 
| 103 | 
            +
                  #   scorpio considers to be parameters
         | 
| 104 | 
            +
                  def inferred_parameters
         | 
| 105 | 
            +
                    parameters = self.parameters ? self.parameters.to_a.dup : []
         | 
| 106 | 
            +
                    path_template.variables.each do |var|
         | 
| 107 | 
            +
                      unless parameters.any? { |p| p['in'] == 'path' && p['name'] == var }
         | 
| 108 | 
            +
                        # we could instantiate this as a V2::Parameter or a V3::Parameter
         | 
| 109 | 
            +
                        # or a ParameterWithContentInPath or whatever. but I can't be bothered.
         | 
| 110 | 
            +
                        parameters << {
         | 
| 111 | 
            +
                          'name' => var,
         | 
| 112 | 
            +
                          'in' => 'path',
         | 
| 113 | 
            +
                          'required' => true,
         | 
| 114 | 
            +
                          'type' => 'string',
         | 
| 115 | 
            +
                        }
         | 
| 116 | 
            +
                      end
         | 
| 117 | 
            +
                    end
         | 
| 118 | 
            +
                    parameters
         | 
| 119 | 
            +
                  end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                  # @return [Module] a module with accessor methods for unambiguously named parameters of this operation.
         | 
| 122 | 
            +
                  def request_accessor_module
         | 
| 123 | 
            +
                    return @request_accessor_module if instance_variable_defined?(:@request_accessor_module)
         | 
| 124 | 
            +
                    @request_accessor_module = begin
         | 
| 125 | 
            +
                      params_by_name = inferred_parameters.group_by { |p| p['name'] }
         | 
| 126 | 
            +
                      Module.new do
         | 
| 127 | 
            +
                        instance_method_modules = [Request, Request::Configurables]
         | 
| 128 | 
            +
                        instance_method_names = instance_method_modules.map do |mod|
         | 
| 129 | 
            +
                          (mod.instance_methods + mod.private_instance_methods).map(&:to_s)
         | 
| 130 | 
            +
                        end.inject(Set.new, &:|)
         | 
| 131 | 
            +
                        params_by_name.each do |name, params|
         | 
| 132 | 
            +
                          next if instance_method_names.include?(name)
         | 
| 133 | 
            +
                          if params.size == 1
         | 
| 134 | 
            +
                            param = params.first
         | 
| 135 | 
            +
                            define_method("#{name}=") { |value| set_param_from(param['in'], param['name'], value) }
         | 
| 136 | 
            +
                            define_method(name) { get_param_from(param['in'], param['name']) }
         | 
| 137 | 
            +
                          end
         | 
| 138 | 
            +
                        end
         | 
| 139 | 
            +
                      end
         | 
| 140 | 
            +
                    end
         | 
| 141 | 
            +
                  end
         | 
| 142 | 
            +
             | 
| 68 143 | 
             
                  def build_request(*a, &b)
         | 
| 69 144 | 
             
                    request = Scorpio::Request.new(self, *a, &b)
         | 
| 70 145 | 
             
                  end
         | 
| @@ -80,9 +155,14 @@ module Scorpio | |
| 80 155 |  | 
| 81 156 | 
             
                module V3
         | 
| 82 157 | 
             
                  raise(Bug) unless const_defined?(:Operation)
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                  # Describes a single API operation on a path.
         | 
| 160 | 
            +
                  #
         | 
| 161 | 
            +
                  # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject
         | 
| 83 162 | 
             
                  class Operation
         | 
| 84 163 | 
             
                    module Configurables
         | 
| 85 164 | 
             
                      def scheme
         | 
| 165 | 
            +
                        # not applicable; for OpenAPI v3, scheme is specified by servers.
         | 
| 86 166 | 
             
                        nil
         | 
| 87 167 | 
             
                      end
         | 
| 88 168 |  | 
| @@ -177,18 +257,20 @@ module Scorpio | |
| 177 257 | 
             
                      elsif body_parameters.size == 1
         | 
| 178 258 | 
             
                        body_parameters.first
         | 
| 179 259 | 
             
                      else
         | 
| 180 | 
            -
                        raise(Bug) # TODO BLAME
         | 
| 260 | 
            +
                        raise(Bug, "multiple body parameters on operation #{operation.pretty_inspect.chomp}") # TODO BLAME
         | 
| 181 261 | 
             
                      end
         | 
| 182 262 | 
             
                    end
         | 
| 183 263 |  | 
| 184 264 | 
             
                    def request_schema(media_type: nil)
         | 
| 185 265 | 
             
                      if body_parameter && body_parameter['schema']
         | 
| 186 266 | 
             
                        JSI::Schema.new(body_parameter['schema'])
         | 
| 267 | 
            +
                      else
         | 
| 268 | 
            +
                        nil
         | 
| 187 269 | 
             
                      end
         | 
| 188 270 | 
             
                    end
         | 
| 189 271 |  | 
| 190 272 | 
             
                    def request_schemas
         | 
| 191 | 
            -
                      [request_schema]
         | 
| 273 | 
            +
                      request_schema ? [request_schema] : []
         | 
| 192 274 | 
             
                    end
         | 
| 193 275 |  | 
| 194 276 | 
             
                    # @return JSI::Schema
         | 
| @@ -1,13 +1,17 @@ | |
| 1 1 | 
             
            module Scorpio
         | 
| 2 2 | 
             
              module OpenAPI
         | 
| 3 | 
            +
                # OperationsScope acts as an Enumerable of the Operations for an openapi_document,
         | 
| 4 | 
            +
                # and offers subscripting by operationId.
         | 
| 3 5 | 
             
                class OperationsScope
         | 
| 4 6 | 
             
                  include JSI::Memoize
         | 
| 5 7 |  | 
| 8 | 
            +
                  # @param openapi_document [Scorpio::OpenAPI::Document]
         | 
| 6 9 | 
             
                  def initialize(openapi_document)
         | 
| 7 10 | 
             
                    @openapi_document = openapi_document
         | 
| 8 11 | 
             
                  end
         | 
| 9 12 | 
             
                  attr_reader :openapi_document
         | 
| 10 13 |  | 
| 14 | 
            +
                  # @yield [Scorpio::OpenAPI::Operation]
         | 
| 11 15 | 
             
                  def each
         | 
| 12 16 | 
             
                    openapi_document.paths.each do |path, path_item|
         | 
| 13 17 | 
             
                      path_item.each do |http_method, operation|
         | 
| @@ -19,9 +23,11 @@ module Scorpio | |
| 19 23 | 
             
                  end
         | 
| 20 24 | 
             
                  include Enumerable
         | 
| 21 25 |  | 
| 22 | 
            -
                   | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 26 | 
            +
                  # @param operationId
         | 
| 27 | 
            +
                  # @return [Scorpio::OpenAPI::Operation] the operation with the given operationId
         | 
| 28 | 
            +
                  def [](operationId)
         | 
| 29 | 
            +
                    memoize(:[], operationId) do |operationId_|
         | 
| 30 | 
            +
                      detect { |operation| operation.operationId == operationId_ }
         | 
| 25 31 | 
             
                    end
         | 
| 26 32 | 
             
                  end
         | 
| 27 33 | 
             
                end
         | 
| @@ -2,7 +2,17 @@ module Scorpio | |
| 2 2 | 
             
              module OpenAPI
         | 
| 3 3 | 
             
                module V3
         | 
| 4 4 | 
             
                  raise(Bug) unless const_defined?(:Server)
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  # An object representing a Server.
         | 
| 7 | 
            +
                  #
         | 
| 8 | 
            +
                  # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#serverObject
         | 
| 5 9 | 
             
                  class Server
         | 
| 10 | 
            +
                    # expands this server's #url using the given_server_variables. any variables
         | 
| 11 | 
            +
                    # that are in the url but not in the given server variables are filled in
         | 
| 12 | 
            +
                    # using the default value for the variable.
         | 
| 13 | 
            +
                    #
         | 
| 14 | 
            +
                    # @param given_server_variables [Hash<String, String>]
         | 
| 15 | 
            +
                    # @return [Addressable::URI] the expanded url
         | 
| 6 16 | 
             
                    def expanded_url(given_server_variables)
         | 
| 7 17 | 
             
                      if variables
         | 
| 8 18 | 
             
                        server_variables = (given_server_variables.keys | variables.keys).map do |key|
         | 
    
        data/lib/scorpio/request.rb
    CHANGED
    
    | @@ -110,45 +110,72 @@ module Scorpio | |
| 110 110 | 
             
                end
         | 
| 111 111 | 
             
                include Configurables
         | 
| 112 112 |  | 
| 113 | 
            -
                 | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 113 | 
            +
                # @param operation [Scorpio::OpenAPI::Operation]
         | 
| 114 | 
            +
                # @param configuration [#to_hash] a hash keyed with configurable attributes for
         | 
| 115 | 
            +
                #   the request - instance methods of Scorpio::Request::Configurables, whose values
         | 
| 116 | 
            +
                #   will be assigned for those attributes.
         | 
| 117 | 
            +
                def initialize(operation, configuration = {}, &b)
         | 
| 118 | 
            +
                  @operation = operation
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                  configuration = JSI.stringify_symbol_keys(configuration)
         | 
| 121 | 
            +
                  params_set = Set.new # the set of params that have been set
         | 
| 122 | 
            +
                  # do the Configurables first
         | 
| 123 | 
            +
                  configuration.each do |name, value|
         | 
| 124 | 
            +
                    if Configurables.public_method_defined?("#{name}=")
         | 
| 125 | 
            +
                      Configurables.instance_method("#{name}=").bind(self).call(value)
         | 
| 126 | 
            +
                      params_set << name
         | 
| 127 | 
            +
                    end
         | 
| 128 | 
            +
                  end
         | 
| 129 | 
            +
                  # then do other top-level params
         | 
| 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}")
         | 
| 118 136 | 
             
                    else
         | 
| 119 | 
            -
                      raise( | 
| 137 | 
            +
                      raise(AmbiguousParameter.new("There are multiple parameters named #{name.inspect} - cannot use it as a configuration key").tap { |e| e.name = name })
         | 
| 120 138 | 
             
                    end
         | 
| 121 139 | 
             
                  end
         | 
| 122 140 |  | 
| 123 | 
            -
                   | 
| 141 | 
            +
                  extend operation.request_accessor_module
         | 
| 142 | 
            +
             | 
| 124 143 | 
             
                  if block_given?
         | 
| 125 144 | 
             
                    yield self
         | 
| 126 145 | 
             
                  end
         | 
| 127 146 | 
             
                end
         | 
| 128 147 |  | 
| 148 | 
            +
                # @return [Scorpio::OpenAPI::Operation]
         | 
| 129 149 | 
             
                attr_reader :operation
         | 
| 130 150 |  | 
| 151 | 
            +
                # @return [Scorpio::OpenAPI::Document]
         | 
| 131 152 | 
             
                def openapi_document
         | 
| 132 153 | 
             
                  operation.openapi_document
         | 
| 133 154 | 
             
                end
         | 
| 134 155 |  | 
| 156 | 
            +
                # @return [Symbol] the http method for this request - :get, :post, etc.
         | 
| 135 157 | 
             
                def http_method
         | 
| 136 158 | 
             
                  operation.http_method.downcase.to_sym
         | 
| 137 159 | 
             
                end
         | 
| 138 160 |  | 
| 161 | 
            +
                # @return [Addressable::Template] the template for the request's path, to be expanded
         | 
| 162 | 
            +
                #   with path_params and appended to the request's base_url
         | 
| 139 163 | 
             
                def path_template
         | 
| 140 | 
            -
                   | 
| 164 | 
            +
                  operation.path_template
         | 
| 141 165 | 
             
                end
         | 
| 142 166 |  | 
| 167 | 
            +
                # @return [Addressable::URI] an Addressable::URI containing only the path to append to
         | 
| 168 | 
            +
                #   the base_url for this request
         | 
| 143 169 | 
             
                def path
         | 
| 170 | 
            +
                  path_params = JSI.stringify_symbol_keys(self.path_params)
         | 
| 144 171 | 
             
                  missing_variables = path_template.variables - path_params.keys
         | 
| 145 172 | 
             
                  if missing_variables.any?
         | 
| 146 | 
            -
                    raise(ArgumentError, "path #{operation. | 
| 173 | 
            +
                    raise(ArgumentError, "path #{operation.path_template_str} for operation #{operation.operationId} requires path_params " +
         | 
| 147 174 | 
             
                      "which were missing: #{missing_variables.inspect}")
         | 
| 148 175 | 
             
                  end
         | 
| 149 176 | 
             
                  empty_variables = path_template.variables.select { |v| path_params[v].to_s.empty? }
         | 
| 150 177 | 
             
                  if empty_variables.any?
         | 
| 151 | 
            -
                    raise(ArgumentError, "path #{operation. | 
| 178 | 
            +
                    raise(ArgumentError, "path #{operation.path_template_str} for operation #{operation.operationId} requires path_params " +
         | 
| 152 179 | 
             
                      "which were empty: #{empty_variables.inspect}")
         | 
| 153 180 | 
             
                  end
         | 
| 154 181 |  | 
| @@ -159,20 +186,22 @@ module Scorpio | |
| 159 186 | 
             
                  end
         | 
| 160 187 | 
             
                end
         | 
| 161 188 |  | 
| 189 | 
            +
                # @return [Addressable::URI] the full URL for this request
         | 
| 162 190 | 
             
                def url
         | 
| 163 191 | 
             
                  unless base_url
         | 
| 164 192 | 
             
                    raise(ArgumentError, "no base_url has been specified for request")
         | 
| 165 193 | 
             
                  end
         | 
| 166 194 | 
             
                  # we do not use Addressable::URI#join as the paths should just be concatenated, not resolved.
         | 
| 167 195 | 
             
                  # we use File.join just to deal with consecutive slashes.
         | 
| 168 | 
            -
                   | 
| 169 | 
            -
                  url = Addressable::URI.parse(url)
         | 
| 196 | 
            +
                  Addressable::URI.parse(File.join(base_url, path))
         | 
| 170 197 | 
             
                end
         | 
| 171 198 |  | 
| 199 | 
            +
                # @return [::Ur::ContentTypeAttrs] content type attributes for this request's Content-Type
         | 
| 172 200 | 
             
                def content_type_attrs
         | 
| 173 201 | 
             
                  Ur::ContentTypeAttrs.new(content_type)
         | 
| 174 202 | 
             
                end
         | 
| 175 203 |  | 
| 204 | 
            +
                # @return [String] the value of the request Content-Type header
         | 
| 176 205 | 
             
                def content_type_header
         | 
| 177 206 | 
             
                  headers.each do |k, v|
         | 
| 178 207 | 
             
                    return v if k =~ /\Acontent[-_]type\z/i
         | 
| @@ -180,18 +209,27 @@ module Scorpio | |
| 180 209 | 
             
                  nil
         | 
| 181 210 | 
             
                end
         | 
| 182 211 |  | 
| 212 | 
            +
                # @return [String] Content-Type for this request, taken from request headers if
         | 
| 213 | 
            +
                #   present, or the request media_type.
         | 
| 183 214 | 
             
                def content_type
         | 
| 184 215 | 
             
                  content_type_header || media_type
         | 
| 185 216 | 
             
                end
         | 
| 186 217 |  | 
| 218 | 
            +
                # @return [::JSI::Schema]
         | 
| 187 219 | 
             
                def request_schema(media_type: self.media_type)
         | 
| 188 220 | 
             
                  operation.request_schema(media_type: media_type)
         | 
| 189 221 | 
             
                end
         | 
| 190 222 |  | 
| 223 | 
            +
                # @return [Class subclassing JSI::Base]
         | 
| 191 224 | 
             
                def request_schema_class(media_type: self.media_type)
         | 
| 192 225 | 
             
                  JSI.class_for_schema(request_schema(media_type: media_type))
         | 
| 193 226 | 
             
                end
         | 
| 194 227 |  | 
| 228 | 
            +
                # builds a Faraday connection with this Request's faraday_builder and faraday_adapter.
         | 
| 229 | 
            +
                # passes a given proc yield_ur to middleware to yield an Ur for requests made with the connection.
         | 
| 230 | 
            +
                #
         | 
| 231 | 
            +
                # @param yield_ur [Proc]
         | 
| 232 | 
            +
                # @return [::Faraday::Connection]
         | 
| 195 233 | 
             
                def faraday_connection(yield_ur = nil)
         | 
| 196 234 | 
             
                  Faraday.new do |faraday_connection|
         | 
| 197 235 | 
             
                    faraday_builder.call(faraday_connection)
         | 
| @@ -203,6 +241,81 @@ module Scorpio | |
| 203 241 | 
             
                  end
         | 
| 204 242 | 
             
                end
         | 
| 205 243 |  | 
| 244 | 
            +
                # if there is only one parameter with the given name, of any sort, this will set it.
         | 
| 245 | 
            +
                #
         | 
| 246 | 
            +
                # @param name [String, Symbol] the 'name' property of one applicable parameter
         | 
| 247 | 
            +
                # @param value [Object] the applicable parameter will be applied to the request with the given value.
         | 
| 248 | 
            +
                # @return [Object] echoes the value param
         | 
| 249 | 
            +
                # @raise [Scorpio::AmbiguousParameter] if more than one paramater has the given name
         | 
| 250 | 
            +
                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
         | 
| 258 | 
            +
                  value
         | 
| 259 | 
            +
                end
         | 
| 260 | 
            +
             | 
| 261 | 
            +
                # @param name [String, Symbol] the 'name' property of one applicable parameter
         | 
| 262 | 
            +
                # @return [Object] the value of the named parameter on this request
         | 
| 263 | 
            +
                # @raise [Scorpio::AmbiguousParameter] if more than one paramater has the given name
         | 
| 264 | 
            +
                def get_param(name)
         | 
| 265 | 
            +
                  name = name.to_s if name.is_a?(Symbol)
         | 
| 266 | 
            +
                  params = operation.inferred_parameters.select { |p| p['name'] == name }
         | 
| 267 | 
            +
                  if params.size == 1
         | 
| 268 | 
            +
                    get_param_from(params.first['in'], name)
         | 
| 269 | 
            +
                  else
         | 
| 270 | 
            +
                    raise(AmbiguousParameter.new("There are multiple parameters named #{name}; cannot use #get_param").tap { |e| e.name = name })
         | 
| 271 | 
            +
                  end
         | 
| 272 | 
            +
                end
         | 
| 273 | 
            +
             | 
| 274 | 
            +
                # @param in [String, Symbol] one of 'path', 'query', 'header', or 'cookie' - where to apply the named value
         | 
| 275 | 
            +
                # @param name [String, Symbol] the parameter name to apply the value to
         | 
| 276 | 
            +
                # @param value [Object] the value
         | 
| 277 | 
            +
                # @return [Object] echoes the value param
         | 
| 278 | 
            +
                # @raise [ArgumentError] invalid 'in' parameter
         | 
| 279 | 
            +
                # @raise [NotImplementedError] cookies aren't implemented
         | 
| 280 | 
            +
                def set_param_from(param_in, name, value)
         | 
| 281 | 
            +
                  param_in = param_in.to_s if param_in.is_a?(Symbol)
         | 
| 282 | 
            +
                  name = name.to_s if name.is_a?(Symbol)
         | 
| 283 | 
            +
                  if param_in == 'path'
         | 
| 284 | 
            +
                    self.path_params = self.path_params.merge(name => value)
         | 
| 285 | 
            +
                  elsif param_in == 'query'
         | 
| 286 | 
            +
                    self.query_params = (self.query_params || {}).merge(name => value)
         | 
| 287 | 
            +
                  elsif param_in == 'header'
         | 
| 288 | 
            +
                    self.headers = self.headers.merge(name => value)
         | 
| 289 | 
            +
                  elsif param_in == 'cookie'
         | 
| 290 | 
            +
                    raise(NotImplementedError, "cookies not implemented: #{name.inspect} => #{value.inspect}")
         | 
| 291 | 
            +
                  else
         | 
| 292 | 
            +
                    raise(ArgumentError, "cannot set param from param_in = #{param_in.inspect} (name: #{name.pretty_inspect.chomp}, value: #{value.pretty_inspect.chomp})")
         | 
| 293 | 
            +
                  end
         | 
| 294 | 
            +
                  value
         | 
| 295 | 
            +
                end
         | 
| 296 | 
            +
             | 
| 297 | 
            +
                # @param in [String, Symbol] one of 'path', 'query', 'header', or 'cookie' - where to apply the named value
         | 
| 298 | 
            +
                # @param name [String, Symbol] the parameter name
         | 
| 299 | 
            +
                # @return [Object] the value of the named parameter on this request
         | 
| 300 | 
            +
                # @raise [ArgumentError] invalid 'in' parameter
         | 
| 301 | 
            +
                # @raise [NotImplementedError] cookies aren't implemented
         | 
| 302 | 
            +
                def get_param_from(param_in, name)
         | 
| 303 | 
            +
                  if param_in == 'path'
         | 
| 304 | 
            +
                    path_params[name]
         | 
| 305 | 
            +
                  elsif param_in == 'query'
         | 
| 306 | 
            +
                    query_params ? query_params[name] : nil
         | 
| 307 | 
            +
                  elsif param_in == 'header'
         | 
| 308 | 
            +
                    headers[name]
         | 
| 309 | 
            +
                  elsif param_in == 'cookie'
         | 
| 310 | 
            +
                    raise(NotImplementedError, "cookies not implemented: #{name.inspect}")
         | 
| 311 | 
            +
                  else
         | 
| 312 | 
            +
                    raise(ArgumentError, "cannot get param from param_in = #{param_in.inspect} (name: #{name.pretty_inspect.chomp})")
         | 
| 313 | 
            +
                  end
         | 
| 314 | 
            +
                end
         | 
| 315 | 
            +
             | 
| 316 | 
            +
                # runs this request and returns the full representation of the request that was run and its response.
         | 
| 317 | 
            +
                #
         | 
| 318 | 
            +
                # @return [Scorpio::Ur]
         | 
| 206 319 | 
             
                def run_ur
         | 
| 207 320 | 
             
                  headers = {}
         | 
| 208 321 | 
             
                  if user_agent
         | 
| @@ -220,10 +333,33 @@ module Scorpio | |
| 220 333 | 
             
                  ur
         | 
| 221 334 | 
             
                end
         | 
| 222 335 |  | 
| 336 | 
            +
                # runs this request. returns the response body object - that is, the response body
         | 
| 337 | 
            +
                # parsed according to an understood media type, and instantiated with the applicable
         | 
| 338 | 
            +
                # response schema if one is specified. see Scorpio::Response#body_object for more detail.
         | 
| 339 | 
            +
                #
         | 
| 340 | 
            +
                # @raise [Scorpio::HTTPError] if the request returns a 4xx or 5xx status, the appropriate
         | 
| 341 | 
            +
                #   error is raised - see Scorpio::HTTPErrors
         | 
| 223 342 | 
             
                def run
         | 
| 224 343 | 
             
                  ur = run_ur
         | 
| 225 344 | 
             
                  ur.raise_on_http_error
         | 
| 226 345 | 
             
                  ur.response.body_object
         | 
| 227 346 | 
             
                end
         | 
| 347 | 
            +
             | 
| 348 | 
            +
                # todo make a proper iterator interface
         | 
| 349 | 
            +
                # @param next_page [#call] a callable which will take a parameter `page_ur`, which is a {Scorpio::Ur},
         | 
| 350 | 
            +
                #   and must result in an Ur representing the next page, which will be yielded to the block.
         | 
| 351 | 
            +
                # @yield [Scorpio::Ur] yields the first page, and each subsequent result of calls to `next_page` until
         | 
| 352 | 
            +
                #   that results in nil
         | 
| 353 | 
            +
                # @return [void]
         | 
| 354 | 
            +
                def each_page_ur(next_page: , raise_on_http_error: true)
         | 
| 355 | 
            +
                  return to_enum(__method__, next_page: next_page, raise_on_http_error: raise_on_http_error) unless block_given?
         | 
| 356 | 
            +
                  page_ur = run_ur
         | 
| 357 | 
            +
                  while page_ur
         | 
| 358 | 
            +
                    page_ur.raise_on_http_error if raise_on_http_error
         | 
| 359 | 
            +
                    yield page_ur
         | 
| 360 | 
            +
                    page_ur = next_page.call(page_ur)
         | 
| 361 | 
            +
                  end
         | 
| 362 | 
            +
                  nil
         | 
| 363 | 
            +
                end
         | 
| 228 364 | 
             
              end
         | 
| 229 365 | 
             
            end
         |