internetdata 2.8.4 → 2.8.6
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/README.md +1 -1
- data/lib/internetdata/client.rb +8 -4
- data/lib/internetdata/retries.rb +9 -0
- data/lib/internetdata/transport.rb +20 -4
- data/lib/internetdata/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 60cb6997f887f4ac598e856721bfc16b49d84059390ce32d1c1a13721cf3079a
|
|
4
|
+
data.tar.gz: 129161d2e8eede6fc3aa00ee2daf764f3177dab897b7af0a79d7188241430cce
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 63b12de438f16533af87d36997d2a66a53b7e5b98330b6a4175c42c6ab481f27ed4bf742646674b51a5eae3567db6fcce25b267cf237bd019369577ebac9f476
|
|
7
|
+
data.tar.gz: c9c6f3cef56834177ea1f5806ebadb0c31aa7ce40817c7e19b3e579f309b64fb68ffaa474f64c503ce8a11c59792e0bb4acf6e678cd4d3fa5f23e044907957d9
|
data/README.md
CHANGED
|
@@ -118,7 +118,7 @@ end
|
|
|
118
118
|
|
|
119
119
|
`kind` is one of `:bad_request`, `:unauthorized`, `:forbidden`, `:rate_limited`, `:quota_exceeded`, `:server_error` or `:network`. `rc` is the API's own result code, such as `NOT_LICENSED` or `LICENSE_EXPIRED`, which is usually the specific thing you want to read.
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
`:rate_limited` is a 429 that carries `Retry-After`, and the library retries it for you, waiting as long as it says, or its own backoff from 250 ms when that's past about 24.8 days. `:quota_exceeded` is a 429 without one, which it never retries. InternetData's database API has no quota, so you won't see `:quota_exceeded` from it.
|
|
122
122
|
|
|
123
123
|
### Sign in with OAuth (device flow)
|
|
124
124
|
|
data/lib/internetdata/client.rb
CHANGED
|
@@ -21,16 +21,20 @@ module InternetData
|
|
|
21
21
|
|
|
22
22
|
# @param api_key [String, nil] a key carrying the `db.download` scope. Omit
|
|
23
23
|
# it and no credential is sent; every database published today answers 401.
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
24
|
+
# The blanks around it are dropped, and blanks alone are no key.
|
|
25
|
+
# @param retries [Integer] extra attempts for a transient failure, from 0. Each
|
|
26
|
+
# waits the server's `Retry-After` when it sent one of at most about 24.8
|
|
27
|
+
# days, and otherwise a backoff of 250 ms that doubles per retry.
|
|
27
28
|
# @param timeout [Numeric] seconds allowed for one API call, 0 for no bound. It
|
|
28
29
|
# also bounds the CONNECT phase of a transfer, which is otherwise unlimited.
|
|
29
30
|
# @raise [ArgumentError] for a `timeout` that is negative, not a finite number,
|
|
30
|
-
# or past 2147483.647 seconds (2**31 - 1 ms), the longest curl holds
|
|
31
|
+
# or past 2147483.647 seconds (2**31 - 1 ms), the longest curl holds; for a
|
|
32
|
+
# `retries` that is not an Integer from 0; and for an `api_key` holding a
|
|
33
|
+
# control character, without the key in the message.
|
|
31
34
|
# @param transport [Transport, nil] override the HTTP layer, mostly for tests.
|
|
32
35
|
def initialize(api_key: nil, base_url: DEFAULT_BASE_URL, retries: DEFAULT_RETRIES,
|
|
33
36
|
timeout: DEFAULT_TIMEOUT, transport: nil)
|
|
37
|
+
retries = Retries.checked_count(retries)
|
|
34
38
|
@transport = transport || Transport.new(
|
|
35
39
|
Transport::Config.new(api_key: api_key, base_url: base_url, timeout: timeout),
|
|
36
40
|
)
|
data/lib/internetdata/retries.rb
CHANGED
|
@@ -15,6 +15,15 @@ module InternetData
|
|
|
15
15
|
|
|
16
16
|
module_function
|
|
17
17
|
|
|
18
|
+
# `retries`, once it is a count. Refused where it is set: unchecked, -1 and
|
|
19
|
+
# NaN read as none, 1.5 as two, Infinity retried a failing call for good, and
|
|
20
|
+
# a String, nil or true raised a raw ArgumentError out of every failing call.
|
|
21
|
+
def checked_count(retries)
|
|
22
|
+
return retries if retries.is_a?(Integer) && retries >= 0
|
|
23
|
+
|
|
24
|
+
raise ArgumentError, "retries must be an Integer from 0, not #{retries.inspect}"
|
|
25
|
+
end
|
|
26
|
+
|
|
18
27
|
# `retry_if`, when given, is asked after each retryable failure, and a false
|
|
19
28
|
# answer ends the attempts there.
|
|
20
29
|
def with_retries(retries, retry_if: nil)
|
|
@@ -24,6 +24,19 @@ module InternetData
|
|
|
24
24
|
"timeout must be a number of seconds from 0 (no bound) to #{LONGEST_TIMEOUT}, not #{timeout.inspect}"
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
# `api_key` without the blanks around it, nil for blanks alone. A control
|
|
28
|
+
# character is refused where it is set, and the key is left out of the
|
|
29
|
+
# message: a CR or LF failed every call with Ethon's raw InvalidValue, whose
|
|
30
|
+
# message quoted the key, and a tab, NUL or DEL went out inside it (2.8.5).
|
|
31
|
+
def self.checked_key(api_key)
|
|
32
|
+
return nil if api_key.nil?
|
|
33
|
+
|
|
34
|
+
key = api_key.to_s.strip
|
|
35
|
+
raise ArgumentError, 'api_key must not contain a control character' if key.match?(/[[:cntrl:]]/)
|
|
36
|
+
|
|
37
|
+
key.empty? ? nil : key
|
|
38
|
+
end
|
|
39
|
+
|
|
27
40
|
# The generated Configuration applies a security scheme whatever it holds, so
|
|
28
41
|
# a keyless client would send `Authorization: Bearer ` - an empty credential,
|
|
29
42
|
# which the API answers 401 to rather than treating as no credential at all.
|
|
@@ -35,7 +48,7 @@ module InternetData
|
|
|
35
48
|
self.scheme = uri.scheme
|
|
36
49
|
self.host = uri.port == uri.default_port ? uri.host : "#{uri.host}:#{uri.port}"
|
|
37
50
|
self.base_path = uri.path
|
|
38
|
-
self.access_token = api_key
|
|
51
|
+
self.access_token = Transport.checked_key(api_key)
|
|
39
52
|
self.timeout = timeout.nil? ? nil : Transport.checked_timeout(timeout)
|
|
40
53
|
end
|
|
41
54
|
|
|
@@ -216,12 +229,13 @@ module InternetData
|
|
|
216
229
|
}.freeze
|
|
217
230
|
|
|
218
231
|
# RFC 3339's full-date, the spec's `date`, and its date-time, the spec's
|
|
219
|
-
# `date-time`,
|
|
232
|
+
# `date-time`, its `T` and `Z` in either case, as RFC 3339 allows: through
|
|
233
|
+
# 2.8.4 a lowercase one failed the call. Anything else reached the
|
|
220
234
|
# generated reader's Date.parse or Time.parse, which read "monday" as this
|
|
221
235
|
# week's, "10:00" as today in the process's zone and 30 February as 2 March
|
|
222
236
|
# (2.8.2, measured 2026-10-20).
|
|
223
237
|
RFC3339_DATE = /\A(\d{4})-(\d{2})-(\d{2})\z/
|
|
224
|
-
RFC3339_DATE_TIME = /\A(\d{4})-(\d{2})-(\d{2})
|
|
238
|
+
RFC3339_DATE_TIME = /\A(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:[Zz]|[+-](\d{2}):(\d{2}))\z/
|
|
225
239
|
|
|
226
240
|
private
|
|
227
241
|
|
|
@@ -239,7 +253,9 @@ module InternetData
|
|
|
239
253
|
when 'Integer' then value.is_a?(Integer)
|
|
240
254
|
when 'Float' then value.is_a?(Numeric)
|
|
241
255
|
when 'Boolean' then [true, false].include?(value)
|
|
242
|
-
|
|
256
|
+
# The spec's `type: object`, a sample row: through 2.8.5 a row of 1, "x"
|
|
257
|
+
# or [] came back as the answer.
|
|
258
|
+
when 'Object' then value.is_a?(Hash)
|
|
243
259
|
when /\AArray<(?<inner>.+)>\z/
|
|
244
260
|
inner = Regexp.last_match(:inner)
|
|
245
261
|
value.is_a?(Array) && value.each_with_index.all? { |v, i| check_type!(v, inner, "#{where}[#{i}]") }
|
data/lib/internetdata/version.rb
CHANGED