jbr 3.7.2 → 3.8.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/CHANGELOG.md +9 -0
- data/README.md +10 -0
- data/lib/graphql/client.rb +12 -1
- data/lib/graphql/throttled.rb +16 -0
- data/lib/jbr/oauth.rb +2 -0
- data/lib/jbr/retriable.rb +20 -0
- data/lib/jbr/version.rb +1 -1
- data/lib/jbr.rb +2 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: de01eb560b5d6366142d35901817eae421a30ccebf31aeede4cacb2e97720cd3
|
|
4
|
+
data.tar.gz: 1a613711f19080da2368d9b2d38d2e9fff372dbd4e37126b2bc06e9d4c9a9493
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9b8fc6971e3cbe48eecf5025cb7f5b9beb9e7490a0b7a6830b0a22eb9b4c98fbe3c066686866dff576de01bfc0e10f16050c6bd1002b929bd18940dfeeccd137
|
|
7
|
+
data.tar.gz: 2cbdcb7fd7bd995bf7450784afdb08d59acce2c0ea788f3192a1c62aabaf00251497bfb8ff6efa57a932d96a15735dbaa9bdc4e846824679521056e7a3129e33
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## [3.8.0] - 2026-08-18
|
|
2
|
+
|
|
3
|
+
- [New] `Jbr::Retriable`, a `Jbr::Error` for a query Jobber refused over what it costs rather
|
|
4
|
+
than over anything about the query. Every refusal read alike before, so an app had nothing to
|
|
5
|
+
branch on: the one worth asking again a moment later looked exactly like the one that will
|
|
6
|
+
say the same thing forever. It carries what Jobber reported it with — `cost`, `available`,
|
|
7
|
+
`maximum` and `restore_rate` — so a caller can tell a bucket that needed a second from a
|
|
8
|
+
query too big to ever fit in it
|
|
9
|
+
|
|
1
10
|
## [3.7.2] - 2026-08-17
|
|
2
11
|
|
|
3
12
|
- [Change] Jobber's token endpoint answers in prose, so that is all this reads. 3.7.1 kept a
|
data/README.md
CHANGED
|
@@ -228,6 +228,16 @@ what happened, so the caller can decide:
|
|
|
228
228
|
Throttled (cost 1885, 1254 of 10000 available, restoring 500/s)
|
|
229
229
|
```
|
|
230
230
|
|
|
231
|
+
That one is a `Jbr::Retriable`, a `Jbr::Error` for a refusal worth asking again, carrying the
|
|
232
|
+
numbers to decide with:
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
error.cost # => 1885, what the query was priced at
|
|
236
|
+
error.available # => 1254, what the bucket held when it was asked
|
|
237
|
+
error.maximum # => 10000, what the bucket holds when full — a cost above it never fits
|
|
238
|
+
error.restore_rate # => 500, points a second
|
|
239
|
+
```
|
|
240
|
+
|
|
231
241
|
That arrives as a `Jbr::Error`. 631 points short of a query the bucket holds five times over,
|
|
232
242
|
which a second would have refilled — worth asking again. A cost above `maximumAvailable` is
|
|
233
243
|
worth nothing but a smaller query. Either way the decision belongs to whoever called: from a
|
data/lib/graphql/client.rb
CHANGED
|
@@ -22,12 +22,23 @@ module GraphQL
|
|
|
22
22
|
raise Unauthorized, response.body if response.code == '401'
|
|
23
23
|
raise Error, response.body unless response.is_a? Net::HTTPSuccess
|
|
24
24
|
body = JSON.parse(response.body)
|
|
25
|
-
raise
|
|
25
|
+
raise refusal_for(body) if body['errors'].present?
|
|
26
26
|
body.fetch('data')
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
private
|
|
30
30
|
|
|
31
|
+
# Refused over cost where the endpoint names the code for it, or prices the query above
|
|
32
|
+
# what it says was left. Anything else is a refusal of the query itself.
|
|
33
|
+
def refusal_for(body)
|
|
34
|
+
cost = body['extensions'].to_h['cost'].to_h
|
|
35
|
+
available = cost['throttleStatus'].to_h['currentlyAvailable']
|
|
36
|
+
coded = body['errors'].any? { |error| error.to_h.dig('extensions', 'code') == 'THROTTLED' }
|
|
37
|
+
priced = available && cost['requestedQueryCost'].to_f > available.to_f
|
|
38
|
+
|
|
39
|
+
coded || priced ? Throttled.new(refusal(body), cost) : Error.new(refusal(body))
|
|
40
|
+
end
|
|
41
|
+
|
|
31
42
|
# What the endpoint refused, and — where it priced the refusal — what the query would have
|
|
32
43
|
# cost against what was available. `Throttled` on its own leaves a caller unable to tell a
|
|
33
44
|
# query too big to ever run from a bucket that a moment would have refilled.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module GraphQL
|
|
2
|
+
# An endpoint refusing a query for what it costs rather than for anything about the query
|
|
3
|
+
# itself. Worth telling apart: the same query is answered once the budget it is priced
|
|
4
|
+
# against has refilled.
|
|
5
|
+
class Throttled < Error
|
|
6
|
+
# @param message [String] what the endpoint said, with the numbers it said it with.
|
|
7
|
+
# @param cost [Hash] what it priced the query at, and the budget it priced it against.
|
|
8
|
+
def initialize(message, cost = {})
|
|
9
|
+
super message
|
|
10
|
+
@cost = cost
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @return [Hash] those numbers, in the endpoint's own words.
|
|
14
|
+
attr_reader :cost
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/jbr/oauth.rb
CHANGED
|
@@ -47,6 +47,8 @@ module Jbr
|
|
|
47
47
|
client.query statement, variables: variables
|
|
48
48
|
rescue GraphQL::Unauthorized
|
|
49
49
|
refresh ? retry : {}
|
|
50
|
+
rescue GraphQL::Throttled => error
|
|
51
|
+
raise Retriable.new(error.message, error.cost)
|
|
50
52
|
rescue GraphQL::Error => error
|
|
51
53
|
# The transport's own class never leaves the gem: a caller told to rescue `Jbr::Error`
|
|
52
54
|
# was not catching a throttle, a 500 or an unreadable answer, and had its own job blow
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Jbr
|
|
2
|
+
# Jobber refusing a query over what it costs rather than over anything about the query. The
|
|
3
|
+
# bucket it is priced against refills, so the same question asked later is answered.
|
|
4
|
+
class Retriable < Error
|
|
5
|
+
# @param message [String] what Jobber said, with the numbers it said it with.
|
|
6
|
+
# @param cost [Hash] the `requestedQueryCost` and the `throttleStatus` beside it.
|
|
7
|
+
def initialize(message, cost = {})
|
|
8
|
+
super message
|
|
9
|
+
status = cost['throttleStatus'].to_h
|
|
10
|
+
@cost = cost['requestedQueryCost']
|
|
11
|
+
@available = status['currentlyAvailable']
|
|
12
|
+
@maximum = status['maximumAvailable']
|
|
13
|
+
@restore_rate = status['restoreRate']
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# What the query would have cost, the budget it was priced against, and how fast that
|
|
17
|
+
# budget refills. A cost above the maximum is one no waiting will pay for.
|
|
18
|
+
attr_reader :cost, :available, :maximum, :restore_rate
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/jbr/version.rb
CHANGED
data/lib/jbr.rb
CHANGED
|
@@ -11,6 +11,7 @@ require 'active_support/core_ext/array/conversions'
|
|
|
11
11
|
|
|
12
12
|
require 'graphql/error'
|
|
13
13
|
require 'graphql/unauthorized'
|
|
14
|
+
require 'graphql/throttled'
|
|
14
15
|
require 'graphql/client'
|
|
15
16
|
|
|
16
17
|
require 'jbr/mock'
|
|
@@ -18,6 +19,7 @@ require 'jbr/mock'
|
|
|
18
19
|
require 'jbr/url'
|
|
19
20
|
require 'jbr/error'
|
|
20
21
|
require 'jbr/refused'
|
|
22
|
+
require 'jbr/retriable'
|
|
21
23
|
require 'jbr/token'
|
|
22
24
|
require 'jbr/refreshing'
|
|
23
25
|
# Phone before Cliental, and Cliental before the records that include it: what each asks
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jbr
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Claudio Baccigalupo
|
|
@@ -106,6 +106,7 @@ files:
|
|
|
106
106
|
- README.md
|
|
107
107
|
- lib/graphql/client.rb
|
|
108
108
|
- lib/graphql/error.rb
|
|
109
|
+
- lib/graphql/throttled.rb
|
|
109
110
|
- lib/graphql/unauthorized.rb
|
|
110
111
|
- lib/jbr.rb
|
|
111
112
|
- lib/jbr/account.rb
|
|
@@ -144,6 +145,7 @@ files:
|
|
|
144
145
|
- lib/jbr/refused.rb
|
|
145
146
|
- lib/jbr/request.rb
|
|
146
147
|
- lib/jbr/resource.rb
|
|
148
|
+
- lib/jbr/retriable.rb
|
|
147
149
|
- lib/jbr/token.rb
|
|
148
150
|
- lib/jbr/url.rb
|
|
149
151
|
- lib/jbr/version.rb
|