droplet_kit 3.19.0 → 3.20.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7aa72baa7a9ea4de7fccdc3953c36aec28083aef5339779376e12306b93b09e
4
- data.tar.gz: d539ac1f1fab8846796194ca7f125343ce4749638a277f2d8f3b39afdffc100d
3
+ metadata.gz: 4fc38c8c6d69e9d358d6a5ce09f48d81f86162b21353864d111362d73ad869d0
4
+ data.tar.gz: 69924df50fd90082ab18b00ff46678625bf199929bf722a43a60bb6bd4d4bcc2
5
5
  SHA512:
6
- metadata.gz: dfb0481753a0c360839768dc31c4f1b4b6214e24f7ef9a7ed2d0882f973ce90288a47c5f61cc0ae36f3df43badb80e69ac5b918de21a408726a070e35cceceae
7
- data.tar.gz: 0a353b970b108ccb455ad87d17f20b63847a44b703fb8e1fa9ea0dabb139c5d5f86c9c4c0e02c312f3344f8fb170541528441480b85fa93781be0d3d6be8efbc
6
+ metadata.gz: a119768dc94088edf60b0e33fa7429496686d9494350204531b019c491fc8413ff8364cca381495646e3d72c36d39827906e06a0eac8e4eae69faaa9c2a47972
7
+ data.tar.gz: abd960f172d61201e42e126cf34a298d4da149f8e0b4cdc2fa19fc96c8b1666b96e81c9a0f612f8aa0cf5ee41ea78d9d304902ab3330084d33a41ecd5c6c91aa
data/README.md CHANGED
@@ -52,6 +52,19 @@ require 'droplet_kit'
52
52
  client = DropletKit::Client.new(access_token: 'YOUR_TOKEN', user_agent: 'custom')
53
53
  ```
54
54
 
55
+ ### Automatically Retry Rate Limited Requests
56
+
57
+ By default, DropletKit will handle requests that are rate limited by the DigitalOcean API's [burst limit](https://docs.digitalocean.com/reference/api/api-reference/#section/Introduction/Rate-Limit). When the burst rate limit is reached, DropletKit will wait according to the value of the API response's `Retry-After` header. Typically the wait time is less than one minute. When the hourly rate limit is hit, an error is raised.
58
+
59
+ By default, DropletKit will retry a rate limited request three times before returning an error. If you would like to disable the retry behavior altogether, and instead raise an error when any rate limit is reached, you can set the `retry_max` config value to zero.
60
+
61
+ DropletKit will also wait zero seconds until retrying a request after the `Retry-After` time has elapsed by default. To change this, set the `retry_wait_min` to a different value.
62
+
63
+ ```ruby
64
+ require 'droplet_kit'
65
+ client = DropletKit::Client.new(access_token: 'YOUR_TOKEN', retry_max: 3, retry_wait_min: 1)
66
+ ```
67
+
55
68
  ## Design
56
69
 
57
70
  DropletKit follows a strict design of resources as methods on your client. For examples, for droplets, you will call your client like this:
@@ -61,12 +74,22 @@ client = DropletKit::Client.new(access_token: 'YOUR_TOKEN')
61
74
  client.droplets #=> DropletsResource
62
75
  ```
63
76
 
64
- DropletKit will return Plain Old Ruby objects(tm) that contain the information provided by the API. For example:
77
+ DropletKit will return Plain Old Ruby objects(tm) that contain the information provided by the API. For actions that return multiple objects, the request won't be executed until the result is accessed. For example:
65
78
 
66
79
  ```ruby
67
80
  client = DropletKit::Client.new(access_token: 'YOUR_TOKEN')
81
+
82
+ # Returns an instance of a Paginated Resource.
68
83
  client.droplets.all
69
- # => [ DropletKit::Droplet(id: 123, name: 'something.com', ...), DropletKit::Droplet(id: 1066, name: 'bunk.com', ...) ]
84
+ # => #<DropletKit::PaginatedResource:0x000000010d556c3a>
85
+
86
+ # Returns the first Droplet.
87
+ client.droplets.all.first
88
+ # => <DropletKit::Droplet {:@id=>1, :@name=>"mydroplet", ...}>
89
+
90
+ # Returns an array of Droplet IDs.
91
+ client.droplets.all.map(&:id)
92
+ # => [1]
70
93
  ```
71
94
 
72
95
  When you'd like to save objects, it's your responsibility to instantiate the objects and persist them using the resource objects. Let's use creating a Droplet as an example:
@@ -153,7 +176,7 @@ Actions supported:
153
176
  * `client.databases.list_backups(id: 'id')`
154
177
  * `client.databases.restore_from_backup(database_backup)`
155
178
  * `client.databases.delete_cluster(id: 'id')`
156
- * `client.database.create_db(database, id: 'id')`
179
+ * `client.databases.create_db(database, id: 'id')`
157
180
  * `client.databases.find_db(id: 'id', name: 'name')`
158
181
  * `client.databases.all_dbs(id: 'id')`
159
182
  * `client.databases.delete_db(id: 'id', name: 'name')`
@@ -1,23 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'faraday'
4
+ require 'faraday/retry'
4
5
  require 'droplet_kit/utils'
5
6
 
6
7
  module DropletKit
7
8
  class Client
8
9
  DEFAULT_OPEN_TIMEOUT = 60
9
10
  DEFAULT_TIMEOUT = 120
11
+ DEFAULT_RETRY_MAX = 3
12
+ DEFAULT_RETRY_WAIT_MIN = 0
13
+
10
14
  DIGITALOCEAN_API = 'https://api.digitalocean.com'
11
15
 
12
- attr_reader :access_token, :api_url, :open_timeout, :timeout, :user_agent
16
+ attr_reader :access_token, :api_url, :open_timeout, :timeout, :user_agent, :retry_max, :retry_wait_min
13
17
 
14
18
  def initialize(options = {})
15
19
  options = DropletKit::Utils.transform_keys(options, &:to_sym)
16
- @access_token = options[:access_token]
17
- @api_url = options[:api_url] || DIGITALOCEAN_API
18
- @open_timeout = options[:open_timeout] || DEFAULT_OPEN_TIMEOUT
19
- @timeout = options[:timeout] || DEFAULT_TIMEOUT
20
- @user_agent = options[:user_agent]
20
+ @access_token = options[:access_token]
21
+ @api_url = options[:api_url] || DIGITALOCEAN_API
22
+ @open_timeout = options[:open_timeout] || DEFAULT_OPEN_TIMEOUT
23
+ @timeout = options[:timeout] || DEFAULT_TIMEOUT
24
+ @user_agent = options[:user_agent]
25
+ @retry_max = options[:retry_max] || DEFAULT_RETRY_MAX
26
+ @retry_wait_min = options[:retry_wait_min] || DEFAULT_RETRY_WAIT_MIN
21
27
  end
22
28
 
23
29
  def connection
@@ -25,6 +31,19 @@ module DropletKit
25
31
  req.adapter :net_http
26
32
  req.options.open_timeout = open_timeout
27
33
  req.options.timeout = timeout
34
+ unless retry_max.zero?
35
+ req.request :retry, {
36
+ max: @retry_max,
37
+ interval: @retry_wait_min,
38
+ retry_statuses: [429],
39
+ # faraday-retry supports both the Retry-After and RateLimit-Reset
40
+ # headers, however, it favours the RateLimit-Reset one. To force it
41
+ # to use the Retry-After header, we override the header that it
42
+ # expects for the RateLimit-Reset header to something that we know
43
+ # we don't set.
44
+ rate_limit_reset_header: 'undefined'
45
+ }
46
+ end
28
47
  end
29
48
  end
30
49
 
@@ -91,6 +110,11 @@ module DropletKit
91
110
  content_type: 'application/json',
92
111
  authorization: "Bearer #{access_token}",
93
112
  user_agent: "#{user_agent} #{default_user_agent}".strip
113
+ },
114
+ request: {
115
+ context: {
116
+ retry_max: @retry_max
117
+ }
94
118
  }
95
119
  }
96
120
  end
@@ -8,11 +8,13 @@ module ErrorHandlingResourcable
8
8
  when 200...299
9
9
  next
10
10
  when 429
11
- error = DropletKit::RateLimitReached.new("#{response.status}: #{response.body}")
12
- error.limit = response.headers['RateLimit-Limit']
13
- error.remaining = response.headers['RateLimit-Remaining']
14
- error.reset_at = response.headers['RateLimit-Reset']
15
- raise error
11
+ unless response.headers.key?('Retry-After') && !connection.options.context.key?(:retry_max)
12
+ error = DropletKit::RateLimitReached.new("#{response.status}: #{response.body}")
13
+ error.limit = response.headers['RateLimit-Limit']
14
+ error.remaining = response.headers['RateLimit-Remaining']
15
+ error.reset_at = response.headers['RateLimit-Reset']
16
+ raise error
17
+ end
16
18
  else
17
19
  raise DropletKit::Error, "#{response.status}: #{response.body}"
18
20
  end
@@ -11,9 +11,25 @@ module DropletKit
11
11
  scoped :read do
12
12
  property :droplet_limit
13
13
  property :floating_ip_limit
14
+ property :name
14
15
  property :email
15
16
  property :uuid
16
17
  property :email_verified
18
+ property :team
19
+ end
20
+ end
21
+ end
22
+
23
+ class AccountTeamMapping
24
+ include Kartograph::DSL
25
+
26
+ kartograph do
27
+ root_key singular: 'team', scopes: [:read]
28
+ mapping AccountTeam
29
+
30
+ scoped :read do
31
+ property :uuid
32
+ property :name
17
33
  end
18
34
  end
19
35
  end
@@ -4,8 +4,15 @@ module DropletKit
4
4
  class Account < BaseModel
5
5
  attribute :droplet_limit
6
6
  attribute :floating_ip_limit
7
+ attribute :name
7
8
  attribute :email
8
9
  attribute :uuid
9
10
  attribute :email_verified
11
+ attribute :team
12
+ end
13
+
14
+ class AccountTeam < BaseModel
15
+ attribute :uuid
16
+ attribute :name
10
17
  end
11
18
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DropletKit
4
- VERSION = '3.19.0'
4
+ VERSION = '3.20.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: droplet_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.19.0
4
+ version: 3.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - DigitalOcean API Engineering team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-03 00:00:00.000000000 Z
11
+ date: 2023-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday-retry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.2.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: kartograph
29
43
  requirement: !ruby/object:Gem::Requirement