aptible-resource 1.0.2 → 1.1.0.pre.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/.travis.yml +0 -1
 - data/aptible-resource.gemspec +1 -2
 - data/lib/aptible/resource.rb +5 -0
 - data/lib/aptible/resource/base.rb +1 -11
 - data/lib/aptible/resource/default_retry_coordinator.rb +19 -3
 - data/lib/aptible/resource/version.rb +1 -1
 - data/lib/hyper_resource.rb +2 -9
 - data/lib/hyper_resource/exceptions.rb +1 -1
 - data/lib/hyper_resource/link.rb +2 -2
 - data/lib/hyper_resource/modules/http.rb +101 -42
 - data/lib/hyper_resource/modules/http/wrap_errors.rb +1 -7
 - data/lib/hyper_resource/modules/internal_attributes.rb +0 -2
 - data/spec/aptible/resource/base_spec.rb +120 -24
 - data/spec/aptible/resource/retry_spec.rb +2 -32
 - data/spec/fixtures/api.rb +1 -0
 - data/spec/spec_helper.rb +2 -0
 - metadata +13 -19
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: a73b16e30cf7b0bb43875b1836684ef93844e8b6
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 8f1ab370731c0eab7af7ad16e672186f9a21e265
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: ebfa485b577102776eee1481fbf4da3e0cb220dbb36e95a5af39148af7b72150645d6b0ffcdc2125d832fa1ae6613d06edfc9f46c70970ccd34fd8ceee5071ac
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: dde6aa360f1567af93327340ddd05493cdfbb9d19953692f72dff5fea8bed638c3a06d44061885ddb0f63108b56b90c21b072fb497d55d710b10b8dade111462
         
     | 
    
        data/.travis.yml
    CHANGED
    
    
    
        data/aptible-resource.gemspec
    CHANGED
    
    | 
         @@ -21,9 +21,8 @@ Gem::Specification.new do |spec| 
     | 
|
| 
       21 
21 
     | 
    
         | 
| 
       22 
22 
     | 
    
         
             
              # HyperResource dependencies
         
     | 
| 
       23 
23 
     | 
    
         
             
              spec.add_dependency 'uri_template', '>= 0.5.2'
         
     | 
| 
       24 
     | 
    
         
            -
              spec.add_dependency 'faraday',      '>= 0.9.2', '< 0.14'
         
     | 
| 
       25 
24 
     | 
    
         
             
              spec.add_dependency 'json'
         
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
      
 25 
     | 
    
         
            +
              spec.add_dependency 'httpclient', '~> 2.8'
         
     | 
| 
       27 
26 
     | 
    
         
             
              spec.add_dependency 'fridge'
         
     | 
| 
       28 
27 
     | 
    
         
             
              spec.add_dependency 'activesupport', '>= 4.0', '< 6.0'
         
     | 
| 
       29 
28 
     | 
    
         
             
              spec.add_dependency 'gem_config', '~> 0.3.1'
         
     | 
    
        data/lib/aptible/resource.rb
    CHANGED
    
    | 
         @@ -3,6 +3,7 @@ require 'aptible/resource/base' 
     | 
|
| 
       3 
3 
     | 
    
         
             
            require 'aptible/resource/default_retry_coordinator'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'aptible/resource/null_retry_coordinator'
         
     | 
| 
       5 
5 
     | 
    
         
             
            require 'gem_config'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'logger'
         
     | 
| 
       6 
7 
     | 
    
         | 
| 
       7 
8 
     | 
    
         
             
            module Aptible
         
     | 
| 
       8 
9 
     | 
    
         
             
              module Resource
         
     | 
| 
         @@ -18,6 +19,10 @@ module Aptible 
     | 
|
| 
       18 
19 
     | 
    
         
             
                  has :user_agent,
         
     | 
| 
       19 
20 
     | 
    
         
             
                      classes: [String],
         
     | 
| 
       20 
21 
     | 
    
         
             
                      default: "aptible-resource #{Aptible::Resource::VERSION}"
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  has :logger,
         
     | 
| 
      
 24 
     | 
    
         
            +
                      classes: [Logger],
         
     | 
| 
      
 25 
     | 
    
         
            +
                      default: Logger.new(STDERR).tap { |l| l.level = Logger::WARN }
         
     | 
| 
       21 
26 
     | 
    
         
             
                end
         
     | 
| 
       22 
27 
     | 
    
         | 
| 
       23 
28 
     | 
    
         
             
                class << self
         
     | 
| 
         @@ -224,16 +224,6 @@ module Aptible 
     | 
|
| 
       224 
224 
     | 
    
         
             
                    end
         
     | 
| 
       225 
225 
     | 
    
         
             
                  end
         
     | 
| 
       226 
226 
     | 
    
         | 
| 
       227 
     | 
    
         
            -
                  def self.faraday_options
         
     | 
| 
       228 
     | 
    
         
            -
                    # Default Faraday options. May be overridden by passing
         
     | 
| 
       229 
     | 
    
         
            -
                    # faraday_options to the initializer.
         
     | 
| 
       230 
     | 
    
         
            -
                    {
         
     | 
| 
       231 
     | 
    
         
            -
                      request: {
         
     | 
| 
       232 
     | 
    
         
            -
                        open_timeout: 10
         
     | 
| 
       233 
     | 
    
         
            -
                      }
         
     | 
| 
       234 
     | 
    
         
            -
                    }
         
     | 
| 
       235 
     | 
    
         
            -
                  end
         
     | 
| 
       236 
     | 
    
         
            -
             
     | 
| 
       237 
227 
     | 
    
         
             
                  def initialize(options = {})
         
     | 
| 
       238 
228 
     | 
    
         
             
                    return super(options) unless options.is_a?(Hash)
         
     | 
| 
       239 
229 
     | 
    
         | 
| 
         @@ -305,7 +295,7 @@ module Aptible 
     | 
|
| 
       305 
295 
     | 
    
         
             
                    # Already deleted
         
     | 
| 
       306 
296 
     | 
    
         
             
                    raise unless e.response.status == 404
         
     | 
| 
       307 
297 
     | 
    
         
             
                  rescue HyperResource::ResponseError
         
     | 
| 
       308 
     | 
    
         
            -
                    # HyperResource 
     | 
| 
      
 298 
     | 
    
         
            +
                    # HyperResource chokes on empty response bodies
         
     | 
| 
       309 
299 
     | 
    
         
             
                    nil
         
     | 
| 
       310 
300 
     | 
    
         
             
                  end
         
     | 
| 
       311 
301 
     | 
    
         | 
| 
         @@ -5,11 +5,27 @@ module Aptible 
     | 
|
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
                  IDEMPOTENT_METHODS = [
         
     | 
| 
       7 
7 
     | 
    
         
             
                    # Idempotent as per RFC
         
     | 
| 
       8 
     | 
    
         
            -
                     
     | 
| 
      
 8 
     | 
    
         
            +
                    'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PUT',
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
       9 
10 
     | 
    
         
             
                    # Idempotent on our APIs
         
     | 
| 
       10 
     | 
    
         
            -
                     
     | 
| 
      
 11 
     | 
    
         
            +
                    'PATCH'
         
     | 
| 
      
 12 
     | 
    
         
            +
                  ].freeze
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  RETRY_ERRORS = [
         
     | 
| 
      
 15 
     | 
    
         
            +
                    # Ancestor for Errno::X
         
     | 
| 
      
 16 
     | 
    
         
            +
                    SystemCallError,
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                    # Might be caused by e.g. DNS failure
         
     | 
| 
      
 19 
     | 
    
         
            +
                    SocketError,
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    # HTTPClient transfer error
         
     | 
| 
      
 22 
     | 
    
         
            +
                    HTTPClient::TimeoutError,
         
     | 
| 
      
 23 
     | 
    
         
            +
                    HTTPClient::KeepAliveDisconnected,
         
     | 
| 
      
 24 
     | 
    
         
            +
                    HTTPClient::BadResponseError,
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                    # Bad response
         
     | 
| 
      
 27 
     | 
    
         
            +
                    HyperResource::ServerError
         
     | 
| 
       11 
28 
     | 
    
         
             
                  ].freeze
         
     | 
| 
       12 
     | 
    
         
            -
                  RETRY_ERRORS = [Faraday::Error, HyperResource::ServerError].freeze
         
     | 
| 
       13 
29 
     | 
    
         | 
| 
       14 
30 
     | 
    
         
             
                  def initialize(resource)
         
     | 
| 
       15 
31 
     | 
    
         
             
                    @resource = resource
         
     | 
    
        data/lib/hyper_resource.rb
    CHANGED
    
    | 
         @@ -46,9 +46,6 @@ public 
     | 
|
| 
       46 
46 
     | 
    
         
             
              ## [headers] Headers to send along with requests for this resource (as
         
     | 
| 
       47 
47 
     | 
    
         
             
              ##           well as its eventual child resources, if any).
         
     | 
| 
       48 
48 
     | 
    
         
             
              ##
         
     | 
| 
       49 
     | 
    
         
            -
              ## [faraday_options] Configuration passed to +Faraday::Connection.initialize+,
         
     | 
| 
       50 
     | 
    
         
            -
              ##                   such as +{request: {timeout: 30}}+.
         
     | 
| 
       51 
     | 
    
         
            -
              ##
         
     | 
| 
       52 
49 
     | 
    
         
             
              def initialize(opts={})
         
     | 
| 
       53 
50 
     | 
    
         
             
                return init_from_resource(opts) if opts.kind_of?(HyperResource)
         
     | 
| 
       54 
51 
     | 
    
         | 
| 
         @@ -58,8 +55,6 @@ public 
     | 
|
| 
       58 
55 
     | 
    
         
             
                self.namespace  = opts[:namespace] || self.class.namespace
         
     | 
| 
       59 
56 
     | 
    
         
             
                self.headers    = DEFAULT_HEADERS.merge(self.class.headers || {}).
         
     | 
| 
       60 
57 
     | 
    
         
             
                                                  merge(opts[:headers]     || {})
         
     | 
| 
       61 
     | 
    
         
            -
                self.faraday_options = opts[:faraday_options] ||
         
     | 
| 
       62 
     | 
    
         
            -
                                           self.class.faraday_options || {}
         
     | 
| 
       63 
58 
     | 
    
         | 
| 
       64 
59 
     | 
    
         
             
                ## There's a little acrobatics in getting Attributes, Links, and Objects
         
     | 
| 
       65 
60 
     | 
    
         
             
                ## into the correct subclass.
         
     | 
| 
         @@ -130,14 +125,14 @@ public 
     | 
|
| 
       130 
125 
     | 
    
         
             
              ## in this resource.  Returns nil on failure.
         
     | 
| 
       131 
126 
     | 
    
         
             
              def [](i)
         
     | 
| 
       132 
127 
     | 
    
         
             
                get unless loaded
         
     | 
| 
       133 
     | 
    
         
            -
                self.objects.first[1][i] 
     | 
| 
      
 128 
     | 
    
         
            +
                self.objects.first[1][i]
         
     | 
| 
       134 
129 
     | 
    
         
             
              end
         
     | 
| 
       135 
130 
     | 
    
         | 
| 
       136 
131 
     | 
    
         
             
              ## Iterates over the objects in the first collection of embedded objects
         
     | 
| 
       137 
132 
     | 
    
         
             
              ## in this resource.
         
     | 
| 
       138 
133 
     | 
    
         
             
              def each(&block)
         
     | 
| 
       139 
134 
     | 
    
         
             
                get unless loaded
         
     | 
| 
       140 
     | 
    
         
            -
                self.objects.first[1].each(&block) 
     | 
| 
      
 135 
     | 
    
         
            +
                self.objects.first[1].each(&block)
         
     | 
| 
       141 
136 
     | 
    
         
             
              end
         
     | 
| 
       142 
137 
     | 
    
         | 
| 
       143 
138 
     | 
    
         
             
              #### Magic
         
     | 
| 
         @@ -199,7 +194,6 @@ public 
     | 
|
| 
       199 
194 
     | 
    
         
             
                               :auth            => self.auth,
         
     | 
| 
       200 
195 
     | 
    
         
             
                               :headers         => self.headers,
         
     | 
| 
       201 
196 
     | 
    
         
             
                               :namespace       => self.namespace,
         
     | 
| 
       202 
     | 
    
         
            -
                               :faraday_options => self.faraday_options,
         
     | 
| 
       203 
197 
     | 
    
         
             
                               :token           => self.token,
         
     | 
| 
       204 
198 
     | 
    
         
             
                               :href            => href)
         
     | 
| 
       205 
199 
     | 
    
         
             
              end
         
     | 
| 
         @@ -253,7 +247,6 @@ public 
     | 
|
| 
       253 
247 
     | 
    
         
             
                self.class.response_class(self.response, self.namespace)
         
     | 
| 
       254 
248 
     | 
    
         
             
              end
         
     | 
| 
       255 
249 
     | 
    
         | 
| 
       256 
     | 
    
         
            -
             
     | 
| 
       257 
250 
     | 
    
         
             
              ## Inspects the given Faraday::Response, and returns a string describing
         
     | 
| 
       258 
251 
     | 
    
         
             
              ## this resource's data type.
         
     | 
| 
       259 
252 
     | 
    
         
             
              ##
         
     | 
| 
         @@ -10,7 +10,7 @@ class HyperResource 
     | 
|
| 
       10 
10 
     | 
    
         
             
              end
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
              class ResponseError < Exception
         
     | 
| 
       13 
     | 
    
         
            -
                ## The + 
     | 
| 
      
 13 
     | 
    
         
            +
                ## The +HTTPClient::Message+ object which led to this exception.
         
     | 
| 
       14 
14 
     | 
    
         
             
                attr_accessor :response
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
16 
     | 
    
         
             
                ## The deserialized response body which led to this exception.
         
     | 
    
        data/lib/hyper_resource/link.rb
    CHANGED
    
    | 
         @@ -53,7 +53,7 @@ class HyperResource::Link 
     | 
|
| 
       53 
53 
     | 
    
         | 
| 
       54 
54 
     | 
    
         
             
              ## If we were called with a method we don't know, load this resource
         
     | 
| 
       55 
55 
     | 
    
         
             
              ## and pass the message along.  This achieves implicit loading.
         
     | 
| 
       56 
     | 
    
         
            -
              def method_missing(method, *args)
         
     | 
| 
       57 
     | 
    
         
            -
                self.get.send(method, *args)
         
     | 
| 
      
 56 
     | 
    
         
            +
              def method_missing(method, *args, &block)
         
     | 
| 
      
 57 
     | 
    
         
            +
                self.get.send(method, *args, &block)
         
     | 
| 
       58 
58 
     | 
    
         
             
              end
         
     | 
| 
       59 
59 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require ' 
     | 
| 
      
 1 
     | 
    
         
            +
            require 'httpclient'
         
     | 
| 
       2 
2 
     | 
    
         
             
            require 'uri'
         
     | 
| 
       3 
3 
     | 
    
         
             
            require 'json'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'digest/md5'
         
     | 
| 
         @@ -11,11 +11,42 @@ class HyperResource 
     | 
|
| 
       11 
11 
     | 
    
         
             
                  # things over and over again.
         
     | 
| 
       12 
12 
     | 
    
         
             
                  MAX_COORDINATOR_RETRIES = 16
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
      
 14 
     | 
    
         
            +
                  CONTENT_TYPE_HEADERS = {
         
     | 
| 
      
 15 
     | 
    
         
            +
                    'Content-Type' => 'application/json; charset=utf-8'
         
     | 
| 
      
 16 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 19 
     | 
    
         
            +
                    attr_reader :http_client
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    def initialize_http_client!
         
     | 
| 
      
 22 
     | 
    
         
            +
                      @http_client = HTTPClient.new.tap do |c|
         
     | 
| 
      
 23 
     | 
    
         
            +
                        c.cookie_manager = nil
         
     | 
| 
      
 24 
     | 
    
         
            +
                        c.connect_timeout = 30
         
     | 
| 
      
 25 
     | 
    
         
            +
                        c.send_timeout = 45
         
     | 
| 
      
 26 
     | 
    
         
            +
                        c.receive_timeout = 30
         
     | 
| 
      
 27 
     | 
    
         
            +
                        c.keep_alive_timeout = 15
         
     | 
| 
      
 28 
     | 
    
         
            +
                        c.ssl_config.set_default_paths
         
     | 
| 
      
 29 
     | 
    
         
            +
                      end
         
     | 
| 
      
 30 
     | 
    
         
            +
                    end
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  # We use this accessor / initialize as opposed to a simple constant
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # because during specs, Webmock stubs the HTTPClient class, but that's
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # happens after we initialized  the constant (we could work around that
         
     | 
| 
      
 36 
     | 
    
         
            +
                  # by loading Webmock first, but this is just as simple.
         
     | 
| 
      
 37 
     | 
    
         
            +
                  initialize_http_client!
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
       14 
39 
     | 
    
         
             
                  ## Loads and returns the resource pointed to by +href+.  The returned
         
     | 
| 
       15 
40 
     | 
    
         
             
                  ## resource will be blessed into its "proper" class, if
         
     | 
| 
       16 
41 
     | 
    
         
             
                  ## +self.class.namespace != nil+.
         
     | 
| 
       17 
42 
     | 
    
         
             
                  def get
         
     | 
| 
       18 
     | 
    
         
            -
                    execute_request 
     | 
| 
      
 43 
     | 
    
         
            +
                    execute_request('GET') do |uri, headers|
         
     | 
| 
      
 44 
     | 
    
         
            +
                      HTTP.http_client.get(
         
     | 
| 
      
 45 
     | 
    
         
            +
                        uri,
         
     | 
| 
      
 46 
     | 
    
         
            +
                        follow_redirect: true,
         
     | 
| 
      
 47 
     | 
    
         
            +
                        header: headers
         
     | 
| 
      
 48 
     | 
    
         
            +
                      )
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
       19 
50 
     | 
    
         
             
                  end
         
     | 
| 
       20 
51 
     | 
    
         | 
| 
       21 
52 
     | 
    
         
             
                  ## By default, calls +post+ with the given arguments. Override to
         
     | 
| 
         @@ -28,8 +59,13 @@ class HyperResource 
     | 
|
| 
       28 
59 
     | 
    
         
             
                  ## the response resource.
         
     | 
| 
       29 
60 
     | 
    
         
             
                  def post(attrs = nil)
         
     | 
| 
       30 
61 
     | 
    
         
             
                    attrs ||= attributes
         
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    execute_request('POST') do |uri, headers|
         
     | 
| 
      
 64 
     | 
    
         
            +
                      HTTP.http_client.post(
         
     | 
| 
      
 65 
     | 
    
         
            +
                        uri,
         
     | 
| 
      
 66 
     | 
    
         
            +
                        body: adapter.serialize(attrs),
         
     | 
| 
      
 67 
     | 
    
         
            +
                        header: headers.merge(CONTENT_TYPE_HEADERS)
         
     | 
| 
      
 68 
     | 
    
         
            +
                      )
         
     | 
| 
       33 
69 
     | 
    
         
             
                    end
         
     | 
| 
       34 
70 
     | 
    
         
             
                  end
         
     | 
| 
       35 
71 
     | 
    
         | 
| 
         @@ -44,8 +80,13 @@ class HyperResource 
     | 
|
| 
       44 
80 
     | 
    
         
             
                  ## instead.
         
     | 
| 
       45 
81 
     | 
    
         
             
                  def put(attrs = nil)
         
     | 
| 
       46 
82 
     | 
    
         
             
                    attrs ||= attributes
         
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                    execute_request('PUT') do |uri, headers|
         
     | 
| 
      
 85 
     | 
    
         
            +
                      HTTP.http_client.put(
         
     | 
| 
      
 86 
     | 
    
         
            +
                        uri,
         
     | 
| 
      
 87 
     | 
    
         
            +
                        body: adapter.serialize(attrs),
         
     | 
| 
      
 88 
     | 
    
         
            +
                        header: headers.merge(CONTENT_TYPE_HEADERS)
         
     | 
| 
      
 89 
     | 
    
         
            +
                      )
         
     | 
| 
       49 
90 
     | 
    
         
             
                    end
         
     | 
| 
       50 
91 
     | 
    
         
             
                  end
         
     | 
| 
       51 
92 
     | 
    
         | 
| 
         @@ -54,49 +95,62 @@ class HyperResource 
     | 
|
| 
       54 
95 
     | 
    
         
             
                  ## uses those instead.
         
     | 
| 
       55 
96 
     | 
    
         
             
                  def patch(attrs = nil)
         
     | 
| 
       56 
97 
     | 
    
         
             
                    attrs ||= attributes.changed_attributes
         
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                    execute_request('PATCH') do |uri, headers|
         
     | 
| 
      
 100 
     | 
    
         
            +
                      HTTP.http_client.patch(
         
     | 
| 
      
 101 
     | 
    
         
            +
                        uri,
         
     | 
| 
      
 102 
     | 
    
         
            +
                        body: adapter.serialize(attrs),
         
     | 
| 
      
 103 
     | 
    
         
            +
                        header: headers.merge(CONTENT_TYPE_HEADERS)
         
     | 
| 
      
 104 
     | 
    
         
            +
                      )
         
     | 
| 
       59 
105 
     | 
    
         
             
                    end
         
     | 
| 
       60 
106 
     | 
    
         
             
                  end
         
     | 
| 
       61 
107 
     | 
    
         | 
| 
       62 
108 
     | 
    
         
             
                  ## DELETEs this resource's href, and returns the response resource.
         
     | 
| 
       63 
109 
     | 
    
         
             
                  def delete
         
     | 
| 
       64 
     | 
    
         
            -
                    execute_request  
     | 
| 
       65 
     | 
    
         
            -
             
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
     | 
    
         
            -
                  ## Returns a raw Faraday connection to this resource's URL, with proper
         
     | 
| 
       68 
     | 
    
         
            -
                  ## headers (including auth).
         
     | 
| 
       69 
     | 
    
         
            -
                  def faraday_connection(url = nil)
         
     | 
| 
       70 
     | 
    
         
            -
                    url ||= URI.join(root, href)
         
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
                    Faraday.new(faraday_options.merge(url: url)) do |builder|
         
     | 
| 
       73 
     | 
    
         
            -
                      builder.headers.merge!(headers || {})
         
     | 
| 
       74 
     | 
    
         
            -
                      builder.headers['User-Agent'] = Aptible::Resource.configuration
         
     | 
| 
       75 
     | 
    
         
            -
                                                                       .user_agent
         
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
                      if (ba = auth[:basic])
         
     | 
| 
       78 
     | 
    
         
            -
                        builder.basic_auth(*ba)
         
     | 
| 
       79 
     | 
    
         
            -
                      end
         
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
       81 
     | 
    
         
            -
                      builder.use WrapErrors # This has to be first!
         
     | 
| 
       82 
     | 
    
         
            -
                      builder.request :url_encoded
         
     | 
| 
       83 
     | 
    
         
            -
                      builder.adapter Faraday.default_adapter
         
     | 
| 
      
 110 
     | 
    
         
            +
                    execute_request('DELETE') do |uri, headers|
         
     | 
| 
      
 111 
     | 
    
         
            +
                      HTTP.http_client.delete(uri, header: headers)
         
     | 
| 
       84 
112 
     | 
    
         
             
                    end
         
     | 
| 
       85 
113 
     | 
    
         
             
                  end
         
     | 
| 
       86 
114 
     | 
    
         | 
| 
       87 
115 
     | 
    
         
             
                  private
         
     | 
| 
       88 
116 
     | 
    
         | 
| 
       89 
     | 
    
         
            -
                  def execute_request
         
     | 
| 
      
 117 
     | 
    
         
            +
                  def execute_request(method)
         
     | 
| 
       90 
118 
     | 
    
         
             
                    raise 'execute_request needs a block!' unless block_given?
         
     | 
| 
       91 
119 
     | 
    
         
             
                    retry_coordinator = Aptible::Resource.retry_coordinator_class.new(self)
         
     | 
| 
       92 
120 
     | 
    
         | 
| 
      
 121 
     | 
    
         
            +
                    uri = URI.join(root, href)
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                    h = headers || {}
         
     | 
| 
      
 124 
     | 
    
         
            +
                    h['User-Agent'] = Aptible::Resource.configuration.user_agent
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
       93 
126 
     | 
    
         
             
                    n_retry = 0
         
     | 
| 
       94 
127 
     | 
    
         | 
| 
       95 
128 
     | 
    
         
             
                    begin
         
     | 
| 
      
 129 
     | 
    
         
            +
                      t0 = Time.now
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
       96 
131 
     | 
    
         
             
                      begin
         
     | 
| 
       97 
     | 
    
         
            -
                         
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
      
 132 
     | 
    
         
            +
                        res = yield(uri, h)
         
     | 
| 
      
 133 
     | 
    
         
            +
                        entity = finish_up(res)
         
     | 
| 
      
 134 
     | 
    
         
            +
                      rescue StandardError => e
         
     | 
| 
      
 135 
     | 
    
         
            +
                        Aptible::Resource.configuration.logger.info([
         
     | 
| 
      
 136 
     | 
    
         
            +
                          method,
         
     | 
| 
      
 137 
     | 
    
         
            +
                          uri,
         
     | 
| 
      
 138 
     | 
    
         
            +
                          "(#{n_retry})",
         
     | 
| 
      
 139 
     | 
    
         
            +
                          "#{(Time.now - t0).round(2)}s",
         
     | 
| 
      
 140 
     | 
    
         
            +
                          "ERR[#{e.class}: #{e}]"
         
     | 
| 
      
 141 
     | 
    
         
            +
                        ].join(' '))
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                        raise WrapErrors::WrappedError.new(method, e)
         
     | 
| 
      
 144 
     | 
    
         
            +
                      else
         
     | 
| 
      
 145 
     | 
    
         
            +
                        Aptible::Resource.configuration.logger.info([
         
     | 
| 
      
 146 
     | 
    
         
            +
                          method,
         
     | 
| 
      
 147 
     | 
    
         
            +
                          uri,
         
     | 
| 
      
 148 
     | 
    
         
            +
                          "(#{n_retry})",
         
     | 
| 
      
 149 
     | 
    
         
            +
                          "#{(Time.now - t0).round(2)}s",
         
     | 
| 
      
 150 
     | 
    
         
            +
                          res.status
         
     | 
| 
      
 151 
     | 
    
         
            +
                        ].join(' '))
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                        entity
         
     | 
| 
       100 
154 
     | 
    
         
             
                      end
         
     | 
| 
       101 
155 
     | 
    
         
             
                    rescue WrapErrors::WrappedError => e
         
     | 
| 
       102 
156 
     | 
    
         
             
                      n_retry += 1
         
     | 
| 
         @@ -121,18 +175,23 @@ class HyperResource 
     | 
|
| 
       121 
175 
     | 
    
         
             
                    elsif status / 100 == 3
         
     | 
| 
       122 
176 
     | 
    
         
             
                      raise 'HyperResource does not handle redirects'
         
     | 
| 
       123 
177 
     | 
    
         
             
                    elsif status / 100 == 4
         
     | 
| 
       124 
     | 
    
         
            -
                      raise HyperResource::ClientError.new( 
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
       126 
     | 
    
         
            -
             
     | 
| 
      
 178 
     | 
    
         
            +
                      raise HyperResource::ClientError.new(
         
     | 
| 
      
 179 
     | 
    
         
            +
                        status.to_s,
         
     | 
| 
      
 180 
     | 
    
         
            +
                        response: response,
         
     | 
| 
      
 181 
     | 
    
         
            +
                        body: body
         
     | 
| 
      
 182 
     | 
    
         
            +
                      )
         
     | 
| 
       127 
183 
     | 
    
         
             
                    elsif status / 100 == 5
         
     | 
| 
       128 
     | 
    
         
            -
                      raise HyperResource::ServerError.new( 
     | 
| 
       129 
     | 
    
         
            -
             
     | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
       131 
     | 
    
         
            -
             
     | 
| 
      
 184 
     | 
    
         
            +
                      raise HyperResource::ServerError.new(
         
     | 
| 
      
 185 
     | 
    
         
            +
                        status.to_s,
         
     | 
| 
      
 186 
     | 
    
         
            +
                        response: response,
         
     | 
| 
      
 187 
     | 
    
         
            +
                        body: body
         
     | 
| 
      
 188 
     | 
    
         
            +
                      )
         
     | 
| 
       132 
189 
     | 
    
         
             
                    else ## 1xx? really?
         
     | 
| 
       133 
     | 
    
         
            -
                      raise HyperResource::ResponseError.new( 
     | 
| 
       134 
     | 
    
         
            -
             
     | 
| 
       135 
     | 
    
         
            -
             
     | 
| 
      
 190 
     | 
    
         
            +
                      raise HyperResource::ResponseError.new(
         
     | 
| 
      
 191 
     | 
    
         
            +
                        "Got status #{status}, wtf?",
         
     | 
| 
      
 192 
     | 
    
         
            +
                        response: response,
         
     | 
| 
      
 193 
     | 
    
         
            +
                        body: body
         
     | 
| 
      
 194 
     | 
    
         
            +
                      )
         
     | 
| 
       136 
195 
     | 
    
         | 
| 
       137 
196 
     | 
    
         
             
                    end
         
     | 
| 
       138 
197 
     | 
    
         | 
| 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            class HyperResource
         
     | 
| 
       2 
2 
     | 
    
         
             
              module Modules
         
     | 
| 
       3 
3 
     | 
    
         
             
                module HTTP
         
     | 
| 
       4 
     | 
    
         
            -
                   
     | 
| 
      
 4 
     | 
    
         
            +
                  module WrapErrors
         
     | 
| 
       5 
5 
     | 
    
         
             
                    class WrappedError < StandardError
         
     | 
| 
       6 
6 
     | 
    
         
             
                      attr_reader :method, :err
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
         @@ -10,12 +10,6 @@ class HyperResource 
     | 
|
| 
       10 
10 
     | 
    
         
             
                        @err = err
         
     | 
| 
       11 
11 
     | 
    
         
             
                      end
         
     | 
| 
       12 
12 
     | 
    
         
             
                    end
         
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
                    def call(env)
         
     | 
| 
       15 
     | 
    
         
            -
                      @app.call(env)
         
     | 
| 
       16 
     | 
    
         
            -
                    rescue StandardError => e
         
     | 
| 
       17 
     | 
    
         
            -
                      raise WrappedError.new(env.method, e)
         
     | 
| 
       18 
     | 
    
         
            -
                    end
         
     | 
| 
       19 
13 
     | 
    
         
             
                  end
         
     | 
| 
       20 
14 
     | 
    
         
             
                end
         
     | 
| 
       21 
15 
     | 
    
         
             
              end
         
     | 
| 
         @@ -26,7 +26,6 @@ module HyperResource::Modules 
     | 
|
| 
       26 
26 
     | 
    
         
             
                      :headers,          ## e.g. {'Accept' => 'application/vnd.example+json'}
         
     | 
| 
       27 
27 
     | 
    
         
             
                      :namespace,        ## e.g. 'ExampleAPI', or the class ExampleAPI itself
         
     | 
| 
       28 
28 
     | 
    
         
             
                      :adapter,          ## subclass of HR::Adapter
         
     | 
| 
       29 
     | 
    
         
            -
                      :faraday_options   ## e.g. {:request => {:timeout => 30}}
         
     | 
| 
       30 
29 
     | 
    
         
             
                    ]
         
     | 
| 
       31 
30 
     | 
    
         
             
                  end
         
     | 
| 
       32 
31 
     | 
    
         | 
| 
         @@ -37,7 +36,6 @@ module HyperResource::Modules 
     | 
|
| 
       37 
36 
     | 
    
         
             
                      :headers,
         
     | 
| 
       38 
37 
     | 
    
         
             
                      :namespace,
         
     | 
| 
       39 
38 
     | 
    
         
             
                      :adapter,
         
     | 
| 
       40 
     | 
    
         
            -
                      :faraday_options,
         
     | 
| 
       41 
39 
     | 
    
         
             
                      :token,
         
     | 
| 
       42 
40 
     | 
    
         | 
| 
       43 
41 
     | 
    
         
             
                      :request,
         
     | 
| 
         @@ -52,16 +52,31 @@ describe Aptible::Resource::Base do 
     | 
|
| 
       52 
52 
     | 
    
         
             
              end
         
     | 
| 
       53 
53 
     | 
    
         | 
| 
       54 
54 
     | 
    
         
             
              describe '.find' do
         
     | 
| 
       55 
     | 
    
         
            -
                it 'should  
     | 
| 
       56 
     | 
    
         
            -
                   
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
                   
     | 
| 
      
 55 
     | 
    
         
            +
                it 'should find' do
         
     | 
| 
      
 56 
     | 
    
         
            +
                  stub_request(
         
     | 
| 
      
 57 
     | 
    
         
            +
                    :get, 'https://resource.example.com/mainframes/42'
         
     | 
| 
      
 58 
     | 
    
         
            +
                  ).to_return(body: { id: 42 }.to_json, status: 200)
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  m = Api::Mainframe.find(42)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  expect(m.id).to eq(42)
         
     | 
| 
       59 
62 
     | 
    
         
             
                end
         
     | 
| 
       60 
63 
     | 
    
         | 
| 
       61 
     | 
    
         
            -
                it 'should  
     | 
| 
       62 
     | 
    
         
            -
                   
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
                   
     | 
| 
      
 64 
     | 
    
         
            +
                it 'should find with query params' do
         
     | 
| 
      
 65 
     | 
    
         
            +
                  stub_request(
         
     | 
| 
      
 66 
     | 
    
         
            +
                    :get, 'https://resource.example.com/mainframes/42?test=123'
         
     | 
| 
      
 67 
     | 
    
         
            +
                  ).to_return(body: { id: 42 }.to_json, status: 200)
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  m = Api::Mainframe.find(42, test: 123)
         
     | 
| 
      
 70 
     | 
    
         
            +
                  expect(m.id).to eq(42)
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                it 'should return an instance of the correct class' do
         
     | 
| 
      
 74 
     | 
    
         
            +
                  stub_request(
         
     | 
| 
      
 75 
     | 
    
         
            +
                    :get, 'https://resource.example.com/mainframes/42'
         
     | 
| 
      
 76 
     | 
    
         
            +
                  ).to_return(body: { id: 42 }.to_json, status: 200)
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                  m = Api::Mainframe.find(42)
         
     | 
| 
      
 79 
     | 
    
         
            +
                  expect(m).to be_a(Api::Mainframe)
         
     | 
| 
       65 
80 
     | 
    
         
             
                end
         
     | 
| 
       66 
81 
     | 
    
         
             
              end
         
     | 
| 
       67 
82 
     | 
    
         | 
| 
         @@ -453,7 +468,7 @@ describe Aptible::Resource::Base do 
     | 
|
| 
       453 
468 
     | 
    
         
             
              end
         
     | 
| 
       454 
469 
     | 
    
         | 
| 
       455 
470 
     | 
    
         
             
              context 'configuration' do
         
     | 
| 
       456 
     | 
    
         
            -
                subject { Api.new(root: 'http:// 
     | 
| 
      
 471 
     | 
    
         
            +
                subject { Api.new(root: 'http://example.com') }
         
     | 
| 
       457 
472 
     | 
    
         | 
| 
       458 
473 
     | 
    
         
             
                def configure_new_coordinator(&block)
         
     | 
| 
       459 
474 
     | 
    
         
             
                  Aptible::Resource.configure do |config|
         
     | 
| 
         @@ -468,7 +483,7 @@ describe Aptible::Resource::Base do 
     | 
|
| 
       468 
483 
     | 
    
         
             
                  it 'should not retry if the proc returns false' do
         
     | 
| 
       469 
484 
     | 
    
         
             
                    configure_new_coordinator { define_method(:retry?) { |_, _e| false } }
         
     | 
| 
       470 
485 
     | 
    
         | 
| 
       471 
     | 
    
         
            -
                    stub_request(:get, ' 
     | 
| 
      
 486 
     | 
    
         
            +
                    stub_request(:get, 'example.com')
         
     | 
| 
       472 
487 
     | 
    
         
             
                      .to_return(body: { error: 'foo' }.to_json, status: 401).then
         
     | 
| 
       473 
488 
     | 
    
         
             
                      .to_return(body: { status: 'ok' }.to_json, status: 200)
         
     | 
| 
       474 
489 
     | 
    
         | 
| 
         @@ -479,7 +494,7 @@ describe Aptible::Resource::Base do 
     | 
|
| 
       479 
494 
     | 
    
         
             
                  it 'should retry if the proc returns true' do
         
     | 
| 
       480 
495 
     | 
    
         
             
                    configure_new_coordinator { define_method(:retry?) { |_, _e| true } }
         
     | 
| 
       481 
496 
     | 
    
         | 
| 
       482 
     | 
    
         
            -
                    stub_request(:get, ' 
     | 
| 
      
 497 
     | 
    
         
            +
                    stub_request(:get, 'example.com')
         
     | 
| 
       483 
498 
     | 
    
         
             
                      .to_return(body: { error: 'foo' }.to_json, status: 401).then
         
     | 
| 
       484 
499 
     | 
    
         
             
                      .to_return(body: { error: 'foo' }.to_json, status: 401).then
         
     | 
| 
       485 
500 
     | 
    
         
             
                      .to_return(body: { status: 'ok' }.to_json, status: 200)
         
     | 
| 
         @@ -494,7 +509,7 @@ describe Aptible::Resource::Base do 
     | 
|
| 
       494 
509 
     | 
    
         
             
                      define_method(:retry?) { |_, _e| failures += 1 || true }
         
     | 
| 
       495 
510 
     | 
    
         
             
                    end
         
     | 
| 
       496 
511 
     | 
    
         | 
| 
       497 
     | 
    
         
            -
                    stub_request(:get, ' 
     | 
| 
      
 512 
     | 
    
         
            +
                    stub_request(:get, 'example.com')
         
     | 
| 
       498 
513 
     | 
    
         
             
                      .to_return(body: { error: 'foo' }.to_json, status: 401).then
         
     | 
| 
       499 
514 
     | 
    
         
             
                      .to_return(body: { status: 'ok' }.to_json, status: 200).then
         
     | 
| 
       500 
515 
     | 
    
         
             
                      .to_return(body: { error: 'foo' }.to_json, status: 401)
         
     | 
| 
         @@ -505,7 +520,7 @@ describe Aptible::Resource::Base do 
     | 
|
| 
       505 
520 
     | 
    
         
             
                  end
         
     | 
| 
       506 
521 
     | 
    
         | 
| 
       507 
522 
     | 
    
         
             
                  it 'should not retry with the default proc' do
         
     | 
| 
       508 
     | 
    
         
            -
                    stub_request(:get, ' 
     | 
| 
      
 523 
     | 
    
         
            +
                    stub_request(:get, 'example.com')
         
     | 
| 
       509 
524 
     | 
    
         
             
                      .to_return(body: { error: 'foo' }.to_json, status: 401).then
         
     | 
| 
       510 
525 
     | 
    
         
             
                      .to_return(body: { status: 'ok' }.to_json, status: 200)
         
     | 
| 
       511 
526 
     | 
    
         | 
| 
         @@ -522,7 +537,7 @@ describe Aptible::Resource::Base do 
     | 
|
| 
       522 
537 
     | 
    
         
             
                      define_method(:retry?) { |_, e| (exception = e) && false }
         
     | 
| 
       523 
538 
     | 
    
         
             
                    end
         
     | 
| 
       524 
539 
     | 
    
         | 
| 
       525 
     | 
    
         
            -
                    stub_request(:get, ' 
     | 
| 
      
 540 
     | 
    
         
            +
                    stub_request(:get, 'example.com')
         
     | 
| 
       526 
541 
     | 
    
         
             
                      .to_return(body: { error: 'foo' }.to_json, status: 401)
         
     | 
| 
       527 
542 
     | 
    
         | 
| 
       528 
543 
     | 
    
         
             
                    expect { subject.get.body }
         
     | 
| 
         @@ -544,11 +559,11 @@ describe Aptible::Resource::Base do 
     | 
|
| 
       544 
559 
     | 
    
         
             
                      end
         
     | 
| 
       545 
560 
     | 
    
         
             
                    end
         
     | 
| 
       546 
561 
     | 
    
         | 
| 
       547 
     | 
    
         
            -
                    stub_request(:get, ' 
     | 
| 
      
 562 
     | 
    
         
            +
                    stub_request(:get, 'example.com')
         
     | 
| 
       548 
563 
     | 
    
         
             
                      .with(headers: { 'Authorization' => /foo/ })
         
     | 
| 
       549 
564 
     | 
    
         
             
                      .to_return(body: { error: 'foo' }.to_json, status: 401)
         
     | 
| 
       550 
565 
     | 
    
         | 
| 
       551 
     | 
    
         
            -
                    stub_request(:get, ' 
     | 
| 
      
 566 
     | 
    
         
            +
                    stub_request(:get, 'example.com')
         
     | 
| 
       552 
567 
     | 
    
         
             
                      .with(headers: { 'Authorization' => /bar/ })
         
     | 
| 
       553 
568 
     | 
    
         
             
                      .to_return(body: { status: 'ok' }.to_json, status: 200)
         
     | 
| 
       554 
569 
     | 
    
         | 
| 
         @@ -563,7 +578,7 @@ describe Aptible::Resource::Base do 
     | 
|
| 
       563 
578 
     | 
    
         
             
                      define_method(:retry?) { |_, _e| n += 1 || true }
         
     | 
| 
       564 
579 
     | 
    
         
             
                    end
         
     | 
| 
       565 
580 
     | 
    
         | 
| 
       566 
     | 
    
         
            -
                    stub_request(:get, ' 
     | 
| 
      
 581 
     | 
    
         
            +
                    stub_request(:get, 'example.com')
         
     | 
| 
       567 
582 
     | 
    
         
             
                      .to_return(body: { error: 'foo' }.to_json, status: 401)
         
     | 
| 
       568 
583 
     | 
    
         | 
| 
       569 
584 
     | 
    
         
             
                    expect { subject.get.body }
         
     | 
| 
         @@ -579,7 +594,7 @@ describe Aptible::Resource::Base do 
     | 
|
| 
       579 
594 
     | 
    
         
             
                      config.user_agent = 'foo ua'
         
     | 
| 
       580 
595 
     | 
    
         
             
                    end
         
     | 
| 
       581 
596 
     | 
    
         | 
| 
       582 
     | 
    
         
            -
                    stub_request(:get, ' 
     | 
| 
      
 597 
     | 
    
         
            +
                    stub_request(:get, 'example.com')
         
     | 
| 
       583 
598 
     | 
    
         
             
                      .with(headers: { 'User-Agent' => 'foo ua' })
         
     | 
| 
       584 
599 
     | 
    
         
             
                      .to_return(body: { status: 'ok' }.to_json, status: 200)
         
     | 
| 
       585 
600 
     | 
    
         | 
| 
         @@ -589,22 +604,22 @@ describe Aptible::Resource::Base do 
     | 
|
| 
       589 
604 
     | 
    
         
             
              end
         
     | 
| 
       590 
605 
     | 
    
         | 
| 
       591 
606 
     | 
    
         
             
              context 'token' do
         
     | 
| 
       592 
     | 
    
         
            -
                subject { Api.new(root: 'http:// 
     | 
| 
      
 607 
     | 
    
         
            +
                subject { Api.new(root: 'http://example.com', token: 'bar') }
         
     | 
| 
       593 
608 
     | 
    
         | 
| 
       594 
609 
     | 
    
         
             
                before do
         
     | 
| 
       595 
     | 
    
         
            -
                  stub_request(:get, ' 
     | 
| 
      
 610 
     | 
    
         
            +
                  stub_request(:get, 'example.com/')
         
     | 
| 
       596 
611 
     | 
    
         
             
                    .with(headers: { 'Authorization' => /Bearer (bar|foo)/ })
         
     | 
| 
       597 
612 
     | 
    
         
             
                    .to_return(body: {
         
     | 
| 
       598 
     | 
    
         
            -
                      _links: { some: { href: 'http:// 
     | 
| 
       599 
     | 
    
         
            -
                                mainframes: { href: 'http:// 
     | 
| 
      
 613 
     | 
    
         
            +
                      _links: { some: { href: 'http://example.com/some' },
         
     | 
| 
      
 614 
     | 
    
         
            +
                                mainframes: { href: 'http://example.com/mainframes' } },
         
     | 
| 
       600 
615 
     | 
    
         
             
                      _embedded: { best_mainframe: { _type: 'mainframe', status: 'ok' } }
         
     | 
| 
       601 
616 
     | 
    
         
             
                    }.to_json, status: 200)
         
     | 
| 
       602 
617 
     | 
    
         | 
| 
       603 
     | 
    
         
            -
                  stub_request(:get, ' 
     | 
| 
      
 618 
     | 
    
         
            +
                  stub_request(:get, 'example.com/some')
         
     | 
| 
       604 
619 
     | 
    
         
             
                    .with(headers: { 'Authorization' => /Bearer (bar|foo)/ })
         
     | 
| 
       605 
620 
     | 
    
         
             
                    .to_return(body: { status: 'ok' }.to_json, status: 200)
         
     | 
| 
       606 
621 
     | 
    
         | 
| 
       607 
     | 
    
         
            -
                  stub_request(:get, ' 
     | 
| 
      
 622 
     | 
    
         
            +
                  stub_request(:get, 'example.com/mainframes')
         
     | 
| 
       608 
623 
     | 
    
         
             
                    .with(headers: { 'Authorization' => /Bearer (bar|foo)/ })
         
     | 
| 
       609 
624 
     | 
    
         
             
                    .to_return(body: { _embedded: {
         
     | 
| 
       610 
625 
     | 
    
         
             
                      mainframes: [{ status: 'ok' }]
         
     | 
| 
         @@ -646,4 +661,85 @@ describe Aptible::Resource::Base do 
     | 
|
| 
       646 
661 
     | 
    
         
             
                  expect(m.token).to eq('bar')
         
     | 
| 
       647 
662 
     | 
    
         
             
                end
         
     | 
| 
       648 
663 
     | 
    
         
             
              end
         
     | 
| 
      
 664 
     | 
    
         
            +
             
     | 
| 
      
 665 
     | 
    
         
            +
              context 'lazy fetching' do
         
     | 
| 
      
 666 
     | 
    
         
            +
                subject { Api.new(root: 'http://foo.com') }
         
     | 
| 
      
 667 
     | 
    
         
            +
             
     | 
| 
      
 668 
     | 
    
         
            +
                it 'should support enumerable methods' do
         
     | 
| 
      
 669 
     | 
    
         
            +
                  index = {
         
     | 
| 
      
 670 
     | 
    
         
            +
                    _links: {
         
     | 
| 
      
 671 
     | 
    
         
            +
                      some_items: { href: 'http://foo.com/some_items' }
         
     | 
| 
      
 672 
     | 
    
         
            +
                    }
         
     | 
| 
      
 673 
     | 
    
         
            +
                  }
         
     | 
| 
      
 674 
     | 
    
         
            +
             
     | 
| 
      
 675 
     | 
    
         
            +
                  some_items = {
         
     | 
| 
      
 676 
     | 
    
         
            +
                    _embedded: {
         
     | 
| 
      
 677 
     | 
    
         
            +
                      some_items: [
         
     | 
| 
      
 678 
     | 
    
         
            +
                        { id: 1, handle: 'foo' },
         
     | 
| 
      
 679 
     | 
    
         
            +
                        { id: 2, handle: 'bar' },
         
     | 
| 
      
 680 
     | 
    
         
            +
                        { id: 3, handle: 'qux' }
         
     | 
| 
      
 681 
     | 
    
         
            +
                      ]
         
     | 
| 
      
 682 
     | 
    
         
            +
                    }
         
     | 
| 
      
 683 
     | 
    
         
            +
                  }
         
     | 
| 
      
 684 
     | 
    
         
            +
             
     | 
| 
      
 685 
     | 
    
         
            +
                  stub_request(:get, 'foo.com')
         
     | 
| 
      
 686 
     | 
    
         
            +
                    .to_return(body: index.to_json, status: 200)
         
     | 
| 
      
 687 
     | 
    
         
            +
             
     | 
| 
      
 688 
     | 
    
         
            +
                  stub_request(:get, 'foo.com/some_items')
         
     | 
| 
      
 689 
     | 
    
         
            +
                    .to_return(body: some_items.to_json, status: 200)
         
     | 
| 
      
 690 
     | 
    
         
            +
             
     | 
| 
      
 691 
     | 
    
         
            +
                  bar = subject.some_items.find { |m| m.id == 2 }
         
     | 
| 
      
 692 
     | 
    
         
            +
                  expect(bar.handle).to eq('bar')
         
     | 
| 
      
 693 
     | 
    
         
            +
                end
         
     | 
| 
      
 694 
     | 
    
         
            +
              end
         
     | 
| 
      
 695 
     | 
    
         
            +
             
     | 
| 
      
 696 
     | 
    
         
            +
              describe '_type' do
         
     | 
| 
      
 697 
     | 
    
         
            +
                subject { Api.new(root: 'http://example.com', token: 'bar') }
         
     | 
| 
      
 698 
     | 
    
         
            +
             
     | 
| 
      
 699 
     | 
    
         
            +
                it 'uses the correct class for an expected linked instance' do
         
     | 
| 
      
 700 
     | 
    
         
            +
                  stub_request(:get, 'example.com/')
         
     | 
| 
      
 701 
     | 
    
         
            +
                    .to_return(body: {
         
     | 
| 
      
 702 
     | 
    
         
            +
                      _links: {
         
     | 
| 
      
 703 
     | 
    
         
            +
                        worst_mainframe: { href: 'http://example.com/mainframes/123' }
         
     | 
| 
      
 704 
     | 
    
         
            +
                      }
         
     | 
| 
      
 705 
     | 
    
         
            +
                    }.to_json, status: 200)
         
     | 
| 
      
 706 
     | 
    
         
            +
             
     | 
| 
      
 707 
     | 
    
         
            +
                  stub_request(:get, 'example.com/mainframes/123')
         
     | 
| 
      
 708 
     | 
    
         
            +
                    .to_return(body: { _type: 'mainframe', id: 123 }.to_json, status: 200)
         
     | 
| 
      
 709 
     | 
    
         
            +
             
     | 
| 
      
 710 
     | 
    
         
            +
                  expect(subject.worst_mainframe).to be_a(Api::Mainframe)
         
     | 
| 
      
 711 
     | 
    
         
            +
                end
         
     | 
| 
      
 712 
     | 
    
         
            +
             
     | 
| 
      
 713 
     | 
    
         
            +
                it 'uses the correct class for an unexpected linked instance' do
         
     | 
| 
      
 714 
     | 
    
         
            +
                  stub_request(:get, 'example.com/')
         
     | 
| 
      
 715 
     | 
    
         
            +
                    .to_return(body: {
         
     | 
| 
      
 716 
     | 
    
         
            +
                      _links: {
         
     | 
| 
      
 717 
     | 
    
         
            +
                        some: { href: 'http://example.com/mainframes/123' }
         
     | 
| 
      
 718 
     | 
    
         
            +
                      }
         
     | 
| 
      
 719 
     | 
    
         
            +
                    }.to_json, status: 200)
         
     | 
| 
      
 720 
     | 
    
         
            +
             
     | 
| 
      
 721 
     | 
    
         
            +
                  stub_request(:get, 'example.com/mainframes/123')
         
     | 
| 
      
 722 
     | 
    
         
            +
                    .to_return(body: { _type: 'mainframe', id: 123 }.to_json, status: 200)
         
     | 
| 
      
 723 
     | 
    
         
            +
             
     | 
| 
      
 724 
     | 
    
         
            +
                  expect(subject.some.get).to be_a(Api::Mainframe)
         
     | 
| 
      
 725 
     | 
    
         
            +
                end
         
     | 
| 
      
 726 
     | 
    
         
            +
             
     | 
| 
      
 727 
     | 
    
         
            +
                it 'uses the correct class for an expected embedded instance' do
         
     | 
| 
      
 728 
     | 
    
         
            +
                  stub_request(:get, 'example.com/')
         
     | 
| 
      
 729 
     | 
    
         
            +
                    .to_return(body: {
         
     | 
| 
      
 730 
     | 
    
         
            +
                      _embedded: { best_mainframe: { _type: 'mainframe', id: 123 } }
         
     | 
| 
      
 731 
     | 
    
         
            +
                    }.to_json, status: 200)
         
     | 
| 
      
 732 
     | 
    
         
            +
             
     | 
| 
      
 733 
     | 
    
         
            +
                  expect(subject.best_mainframe).to be_a(Api::Mainframe)
         
     | 
| 
      
 734 
     | 
    
         
            +
                end
         
     | 
| 
      
 735 
     | 
    
         
            +
             
     | 
| 
      
 736 
     | 
    
         
            +
                it 'uses the correct class for an unexpected embedded instance' do
         
     | 
| 
      
 737 
     | 
    
         
            +
                  stub_request(:get, 'example.com/')
         
     | 
| 
      
 738 
     | 
    
         
            +
                    .to_return(body: {
         
     | 
| 
      
 739 
     | 
    
         
            +
                      _embedded: { some: { _type: 'mainframe', id: 123 } }
         
     | 
| 
      
 740 
     | 
    
         
            +
                    }.to_json, status: 200)
         
     | 
| 
      
 741 
     | 
    
         
            +
             
     | 
| 
      
 742 
     | 
    
         
            +
                  expect(subject.some).to be_a(Api::Mainframe)
         
     | 
| 
      
 743 
     | 
    
         
            +
                end
         
     | 
| 
      
 744 
     | 
    
         
            +
              end
         
     | 
| 
       649 
745 
     | 
    
         
             
            end
         
     | 
| 
         @@ -136,15 +136,6 @@ describe Aptible::Resource::Base do 
     | 
|
| 
       136 
136 
     | 
    
         
             
                    expect(subject.get.body).to eq(body)
         
     | 
| 
       137 
137 
     | 
    
         
             
                  end
         
     | 
| 
       138 
138 
     | 
    
         | 
| 
       139 
     | 
    
         
            -
                  it 'should retry timeout errors (Net::OpenTimeout)' do
         
     | 
| 
       140 
     | 
    
         
            -
                    stub_request(:get, href)
         
     | 
| 
       141 
     | 
    
         
            -
                      .to_raise(Net::OpenTimeout).then
         
     | 
| 
       142 
     | 
    
         
            -
                      .to_raise(Net::OpenTimeout).then
         
     | 
| 
       143 
     | 
    
         
            -
                      .to_return(body: json_body)
         
     | 
| 
       144 
     | 
    
         
            -
             
     | 
| 
       145 
     | 
    
         
            -
                    expect(subject.get.body).to eq(body)
         
     | 
| 
       146 
     | 
    
         
            -
                  end
         
     | 
| 
       147 
     | 
    
         
            -
             
     | 
| 
       148 
139 
     | 
    
         
             
                  it 'should retry connection errors' do
         
     | 
| 
       149 
140 
     | 
    
         
             
                    stub_request(:get, href)
         
     | 
| 
       150 
141 
     | 
    
         
             
                      .to_raise(Errno::ECONNREFUSED).then
         
     | 
| 
         @@ -159,29 +150,8 @@ describe Aptible::Resource::Base do 
     | 
|
| 
       159 
150 
     | 
    
         
             
                      .to_timeout.then
         
     | 
| 
       160 
151 
     | 
    
         
             
                      .to_return(body: json_body)
         
     | 
| 
       161 
152 
     | 
    
         | 
| 
       162 
     | 
    
         
            -
                    expect { subject.post } 
     | 
| 
       163 
     | 
    
         
            -
             
     | 
| 
       164 
     | 
    
         
            -
                end
         
     | 
| 
       165 
     | 
    
         
            -
             
     | 
| 
       166 
     | 
    
         
            -
                context 'without connections' do
         
     | 
| 
       167 
     | 
    
         
            -
                  around do |example|
         
     | 
| 
       168 
     | 
    
         
            -
                    WebMock.allow_net_connect!
         
     | 
| 
       169 
     | 
    
         
            -
                    example.run
         
     | 
| 
       170 
     | 
    
         
            -
                    WebMock.disable_net_connect!
         
     | 
| 
       171 
     | 
    
         
            -
                  end
         
     | 
| 
       172 
     | 
    
         
            -
             
     | 
| 
       173 
     | 
    
         
            -
                  it 'default to 10 seconds of timeout and retries 4 times' do
         
     | 
| 
       174 
     | 
    
         
            -
                    # This really relies on how exactly MRI implements Net::HTTP open
         
     | 
| 
       175 
     | 
    
         
            -
                    # timeouts
         
     | 
| 
       176 
     | 
    
         
            -
                    skip 'MRI implementation-specific' if RUBY_PLATFORM == 'java'
         
     | 
| 
       177 
     | 
    
         
            -
             
     | 
| 
       178 
     | 
    
         
            -
                    expect(Timeout).to receive(:timeout)
         
     | 
| 
       179 
     | 
    
         
            -
                      .with(10, Net::OpenTimeout)
         
     | 
| 
       180 
     | 
    
         
            -
                      .exactly(4).times
         
     | 
| 
       181 
     | 
    
         
            -
                      .and_raise(Net::OpenTimeout)
         
     | 
| 
       182 
     | 
    
         
            -
             
     | 
| 
       183 
     | 
    
         
            -
                    expect { subject.get }.to raise_error(Faraday::ConnectionFailed)
         
     | 
| 
       184 
     | 
    
         
            -
                    expect(sleeps.size).to eq(3)
         
     | 
| 
      
 153 
     | 
    
         
            +
                    expect { subject.post }
         
     | 
| 
      
 154 
     | 
    
         
            +
                      .to raise_error(HTTPClient::TimeoutError)
         
     | 
| 
       185 
155 
     | 
    
         
             
                  end
         
     | 
| 
       186 
156 
     | 
    
         
             
                end
         
     | 
| 
       187 
157 
     | 
    
         
             
              end
         
     | 
    
        data/spec/fixtures/api.rb
    CHANGED
    
    
    
        data/spec/spec_helper.rb
    CHANGED
    
    | 
         @@ -18,6 +18,8 @@ require 'webmock/rspec' 
     | 
|
| 
       18 
18 
     | 
    
         
             
            WebMock.disable_net_connect!
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
            RSpec.configure do |config|
         
     | 
| 
      
 21 
     | 
    
         
            +
              config.before(:suite) { HyperResource::Modules::HTTP.initialize_http_client! }
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
       21 
23 
     | 
    
         
             
              config.before { Aptible::Resource.configuration.reset }
         
     | 
| 
       22 
24 
     | 
    
         
             
              config.before { WebMock.reset! }
         
     | 
| 
       23 
25 
     | 
    
         | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: aptible-resource
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 1.0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.1.0.pre.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Frank Macreery
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date:  
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2018-01-16 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: uri_template
         
     | 
| 
         @@ -25,39 +25,33 @@ dependencies: 
     | 
|
| 
       25 
25 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       26 
26 
     | 
    
         
             
                    version: 0.5.2
         
     | 
| 
       27 
27 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       28 
     | 
    
         
            -
              name:  
     | 
| 
      
 28 
     | 
    
         
            +
              name: json
         
     | 
| 
       29 
29 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
       30 
30 
     | 
    
         
             
                requirements:
         
     | 
| 
       31 
31 
     | 
    
         
             
                - - ">="
         
     | 
| 
       32 
32 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       33 
     | 
    
         
            -
                    version: 0 
     | 
| 
       34 
     | 
    
         
            -
                - - "<"
         
     | 
| 
       35 
     | 
    
         
            -
                  - !ruby/object:Gem::Version
         
     | 
| 
       36 
     | 
    
         
            -
                    version: '0.14'
         
     | 
| 
      
 33 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
       37 
34 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       38 
35 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       39 
36 
     | 
    
         
             
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       40 
37 
     | 
    
         
             
                requirements:
         
     | 
| 
       41 
38 
     | 
    
         
             
                - - ">="
         
     | 
| 
       42 
39 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       43 
     | 
    
         
            -
                    version: 0 
     | 
| 
       44 
     | 
    
         
            -
                - - "<"
         
     | 
| 
       45 
     | 
    
         
            -
                  - !ruby/object:Gem::Version
         
     | 
| 
       46 
     | 
    
         
            -
                    version: '0.14'
         
     | 
| 
      
 40 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
       47 
41 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       48 
     | 
    
         
            -
              name:  
     | 
| 
      
 42 
     | 
    
         
            +
              name: httpclient
         
     | 
| 
       49 
43 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
       50 
44 
     | 
    
         
             
                requirements:
         
     | 
| 
       51 
     | 
    
         
            -
                - - " 
     | 
| 
      
 45 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
       52 
46 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       53 
     | 
    
         
            -
                    version: ' 
     | 
| 
      
 47 
     | 
    
         
            +
                    version: '2.8'
         
     | 
| 
       54 
48 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       55 
49 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       56 
50 
     | 
    
         
             
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       57 
51 
     | 
    
         
             
                requirements:
         
     | 
| 
       58 
     | 
    
         
            -
                - - " 
     | 
| 
      
 52 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
       59 
53 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       60 
     | 
    
         
            -
                    version: ' 
     | 
| 
      
 54 
     | 
    
         
            +
                    version: '2.8'
         
     | 
| 
       61 
55 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       62 
56 
     | 
    
         
             
              name: fridge
         
     | 
| 
       63 
57 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -249,12 +243,12 @@ required_ruby_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       249 
243 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       250 
244 
     | 
    
         
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         
     | 
| 
       251 
245 
     | 
    
         
             
              requirements:
         
     | 
| 
       252 
     | 
    
         
            -
              - - " 
     | 
| 
      
 246 
     | 
    
         
            +
              - - ">"
         
     | 
| 
       253 
247 
     | 
    
         
             
                - !ruby/object:Gem::Version
         
     | 
| 
       254 
     | 
    
         
            -
                  version:  
     | 
| 
      
 248 
     | 
    
         
            +
                  version: 1.3.1
         
     | 
| 
       255 
249 
     | 
    
         
             
            requirements: []
         
     | 
| 
       256 
250 
     | 
    
         
             
            rubyforge_project: 
         
     | 
| 
       257 
     | 
    
         
            -
            rubygems_version: 2. 
     | 
| 
      
 251 
     | 
    
         
            +
            rubygems_version: 2.4.5.3
         
     | 
| 
       258 
252 
     | 
    
         
             
            signing_key: 
         
     | 
| 
       259 
253 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       260 
254 
     | 
    
         
             
            summary: Foundation classes for Aptible resource server gems
         
     |