booqable 2.0.0 → 2.2.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 +23 -0
- data/lib/booqable/json_api_serializer.rb +5 -0
- data/lib/booqable/middleware/auth/oauth.rb +30 -8
- data/lib/booqable/resources.json +1 -0
- data/lib/booqable/version.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ea7652c0e4f9625ad965a20a0d9ec5aaf6265cbaf7b464c958b393d1d11da6e5
|
|
4
|
+
data.tar.gz: 95f3928c775d8c74bf1a89f86de03b46bd05df79a0d6e4371049936b69306081
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 99cef1b7b21ae72fa8df8516ad6791b2e2568cca68463270d8e620b71a36b333f321cd5850a637c8fd22d384321887c1ac27afd26929b571f989620d025501f2
|
|
7
|
+
data.tar.gz: 021b52cd5054f2033c83a6234dce59f64fb9a30cf8884ee7ded7d35bb48e865b9467c33c84329ca006a407ab36716ff3bcee95f116a668821ccb9c7411a94a45
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
## [2.2.0] - 2026-11-03
|
|
2
|
+
|
|
3
|
+
- OAuth middleware: refresh the access token a short buffer
|
|
4
|
+
(`REFRESH_BUFFER_SECONDS`, 60s) before it expires, instead of only once it
|
|
5
|
+
has already expired. This fixes `Token is invalid` failures for requests
|
|
6
|
+
landing right at the token's expiry boundary, where network latency or clock
|
|
7
|
+
skew pushes an otherwise-valid token past expiry server-side.
|
|
8
|
+
- Fix: a token refresh failing without an HTTP response (e.g. the stored token
|
|
9
|
+
has no refresh token) now raises the original `OAuth2::Error` instead of
|
|
10
|
+
crashing with `NoMethodError` while trying to map a response that isn't
|
|
11
|
+
there.
|
|
12
|
+
- Require `multi_json` >= 1.21.2, the first release compatible with the
|
|
13
|
+
`json` 3.x gem. Older versions raise `ArgumentError` on every parse under
|
|
14
|
+
json 3.
|
|
15
|
+
|
|
16
|
+
## [2.1.0] - 2026-07-16
|
|
17
|
+
|
|
18
|
+
- Add the `app_issues` resource (CRUD for `App::Issue`).
|
|
19
|
+
- Fix: a to-one relationship serialized with `"data": null` (e.g. a charge
|
|
20
|
+
line's `item`, or an included `barcode` on a product without one) now reads
|
|
21
|
+
as nil, matching the present-with-null attribute semantics — instead of the
|
|
22
|
+
parser dropping the key and the read raising `Booqable::MissingAttribute`.
|
|
23
|
+
|
|
1
24
|
## [2.0.0] - 2026-07-14
|
|
2
25
|
|
|
3
26
|
- **Breaking:** reading an attribute that is absent from an API payload now
|
|
@@ -184,6 +184,11 @@ module Booqable
|
|
|
184
184
|
# Handle multiple relationships (to-many)
|
|
185
185
|
elsif relationship_data.is_a?(Array)
|
|
186
186
|
hash[key] = relationship_data
|
|
187
|
+
# An explicitly null to-one relationship ("data": null) is present-with-null, not
|
|
188
|
+
# absent: keep the key so strict reads answer nil instead of raising
|
|
189
|
+
# Booqable::MissingAttribute (see Booqable::StrictAttributes).
|
|
190
|
+
elsif relationship_data.nil?
|
|
191
|
+
hash[key] = nil
|
|
187
192
|
end
|
|
188
193
|
end
|
|
189
194
|
|
|
@@ -15,6 +15,12 @@ module Booqable
|
|
|
15
15
|
# read_token: -> { stored_token },
|
|
16
16
|
# write_token: ->(token) { store_token(token) }
|
|
17
17
|
class OAuth < Base
|
|
18
|
+
# Refresh the token this many seconds before it actually expires. A
|
|
19
|
+
# request sent in the last moment of a token's life can still be
|
|
20
|
+
# rejected once network latency or clock skew pushes it past the
|
|
21
|
+
# expiry boundary server-side.
|
|
22
|
+
REFRESH_BUFFER_SECONDS = 60
|
|
23
|
+
|
|
18
24
|
# Initialize the OAuth authentication middleware
|
|
19
25
|
#
|
|
20
26
|
# @param app [#call] The next middleware in the Faraday stack
|
|
@@ -48,9 +54,9 @@ module Booqable
|
|
|
48
54
|
|
|
49
55
|
# Process the HTTP request and add OAuth authentication
|
|
50
56
|
#
|
|
51
|
-
# Retrieves the stored access token, refreshes it if expired
|
|
52
|
-
# it to the Authorization header. Then
|
|
53
|
-
# middleware in the stack.
|
|
57
|
+
# Retrieves the stored access token, refreshes it if it is expired or
|
|
58
|
+
# about to expire, and adds it to the Authorization header. Then
|
|
59
|
+
# passes the request to the next middleware in the stack.
|
|
54
60
|
#
|
|
55
61
|
# @param env [Faraday::Env] The request environment
|
|
56
62
|
# @return [Faraday::Response] The response from the next middleware
|
|
@@ -58,7 +64,7 @@ module Booqable
|
|
|
58
64
|
around_refresh_token do
|
|
59
65
|
@token = @client.get_access_token_from_hash(@read_token.call)
|
|
60
66
|
|
|
61
|
-
if
|
|
67
|
+
if expires_soon?(@token)
|
|
62
68
|
@token = refresh_token!
|
|
63
69
|
end
|
|
64
70
|
end
|
|
@@ -70,6 +76,14 @@ module Booqable
|
|
|
70
76
|
|
|
71
77
|
private
|
|
72
78
|
|
|
79
|
+
# Whether the token expires within the buffer, or has no usable
|
|
80
|
+
# expiry. expires_at may be an Integer epoch (OAuth2) or a Time
|
|
81
|
+
# (e.g. an ActiveRecord datetime handed back by read_token), so
|
|
82
|
+
# coerce it.
|
|
83
|
+
def expires_soon?(token)
|
|
84
|
+
token.expires_at.nil? || Time.now.to_i >= token.expires_at.to_i - REFRESH_BUFFER_SECONDS
|
|
85
|
+
end
|
|
86
|
+
|
|
73
87
|
# Yield to the configured around-callback, if any
|
|
74
88
|
#
|
|
75
89
|
# When a host application provides one (e.g. an advisory lock), the
|
|
@@ -84,10 +98,16 @@ module Booqable
|
|
|
84
98
|
#
|
|
85
99
|
# Uses the refresh token to obtain a new access token and stores it
|
|
86
100
|
# using the configured write_token proc. Converts OAuth2 errors to
|
|
87
|
-
# Booqable errors for consistent error handling.
|
|
101
|
+
# Booqable errors for consistent error handling. The OAuth2 error
|
|
102
|
+
# propagates as-is when there is no HTTP response to map (e.g. the
|
|
103
|
+
# stored token has no refresh token, so the error carries a plain
|
|
104
|
+
# Hash) or when the response maps to no Booqable error class (e.g. a
|
|
105
|
+
# 200 from the token endpoint without an access token — only 4xx/5xx
|
|
106
|
+
# map). A refresh failure must never return: the caller would carry
|
|
107
|
+
# on with a nil token.
|
|
88
108
|
#
|
|
89
109
|
# @return [OAuth2::AccessToken] The new access token
|
|
90
|
-
# @raise [Booqable::Error]
|
|
110
|
+
# @raise [Booqable::Error, OAuth2::Error] When the refresh fails
|
|
91
111
|
def refresh_token!
|
|
92
112
|
new_token = @token.refresh!
|
|
93
113
|
|
|
@@ -95,9 +115,11 @@ module Booqable
|
|
|
95
115
|
|
|
96
116
|
new_token
|
|
97
117
|
rescue OAuth2::Error => e
|
|
98
|
-
|
|
118
|
+
http_response = e.response.response if e.response.respond_to?(:response)
|
|
119
|
+
raise unless http_response&.env
|
|
99
120
|
|
|
100
|
-
Booqable::Error.from_response(
|
|
121
|
+
Booqable::Error.from_response(http_response.env)
|
|
122
|
+
raise
|
|
101
123
|
end
|
|
102
124
|
end
|
|
103
125
|
end
|
data/lib/booqable/resources.json
CHANGED
data/lib/booqable/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: booqable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Hrvoje Šimić
|
|
@@ -58,6 +58,9 @@ dependencies:
|
|
|
58
58
|
- - "~>"
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
60
|
version: '1.15'
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: 1.21.2
|
|
61
64
|
type: :runtime
|
|
62
65
|
prerelease: false
|
|
63
66
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -65,6 +68,9 @@ dependencies:
|
|
|
65
68
|
- - "~>"
|
|
66
69
|
- !ruby/object:Gem::Version
|
|
67
70
|
version: '1.15'
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: 1.21.2
|
|
68
74
|
- !ruby/object:Gem::Dependency
|
|
69
75
|
name: addressable
|
|
70
76
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -187,7 +193,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
187
193
|
- !ruby/object:Gem::Version
|
|
188
194
|
version: '0'
|
|
189
195
|
requirements: []
|
|
190
|
-
rubygems_version:
|
|
196
|
+
rubygems_version: 4.0.10
|
|
191
197
|
specification_version: 4
|
|
192
198
|
summary: Official Booqable API client for Ruby.
|
|
193
199
|
test_files: []
|