nb_api_client 1.0.0 → 1.1.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 +10 -0
- data/README.md +11 -19
- data/lib/nb_api_client/configuration.rb +7 -0
- data/lib/nb_api_client/oauth_token_refresh.rb +39 -0
- data/lib/nb_api_client/version.rb +1 -1
- data/lib/nb_api_client.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fa4a409cda310073f64e30372fd23444e3785ac0a84bf75ec9a328c4ede4ea27
|
|
4
|
+
data.tar.gz: 5c7eecae6437924564604c56128a7a41a4a152a649cb51b94905becdeec00f20
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0732e459c9eddf51483e4718ea6b117a993db84b960bac2a2d2213fa30776fc1f576265e464c5cf9df5d44e7d1eb10ba6d2fef14a99d4edf99bfe274bdee28d9
|
|
7
|
+
data.tar.gz: 5958277d5e508c9b63f3c126e9ab5305a5f85a8100bc693d07b26f83f09f7639e62071a44e2cc6a2b9a7a2ff3bf29f1ff61e18b804494b8ee01f95ddf66795f5
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.1.0] - 2026-07-16
|
|
8
|
+
|
|
9
|
+
- Added `NbApiClient::OAuthTokenRefresh`, an optional mixin implementing
|
|
10
|
+
`refresh_oauth_token` for nations backed by an ActiveRecord-style model
|
|
11
|
+
(`token`/`refresh_token`/`token_expires_at` columns, `#with_lock`/
|
|
12
|
+
`#update!`). `include` it instead of writing your own; it POSTs to
|
|
13
|
+
NationBuilder's `/oauth/token` endpoint via HTTParty. Configured via new
|
|
14
|
+
`config.oauth_client_id`/`config.oauth_client_secret` (default from
|
|
15
|
+
`NB_CLIENT_ID`/`NB_CLIENT_SECRET` env vars).
|
|
16
|
+
|
|
7
17
|
## [1.0.0] - 2026-07-16
|
|
8
18
|
|
|
9
19
|
- **Breaking:** Removed the `oauth2` gem dependency. `NbApiClient::Request` now
|
data/README.md
CHANGED
|
@@ -44,30 +44,17 @@ NbApiClient::Request.call(nation, :post, "/api/v2/signups", {
|
|
|
44
44
|
| `refresh_oauth_token` | Refreshes the OAuth token in place; returns truthy on success, falsy on failure. |
|
|
45
45
|
| `deauthorize` | Called after too many consecutive "unauthorized" responses (see `unauthorized_error_threshold` below). |
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
If your model is an ActiveRecord model with `token`, `refresh_token`, and `token_expires_at` columns and supports `#with_lock`/`#update!`, just include the gem's mixin instead of writing `refresh_oauth_token` yourself:
|
|
48
48
|
|
|
49
49
|
```ruby
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
# Re-check after acquiring the lock in case another process already
|
|
53
|
-
# refreshed the token while this one was waiting.
|
|
54
|
-
return true if updated_at > 5.seconds.ago
|
|
55
|
-
|
|
56
|
-
client = OAuth2::Client.new(ENV["NB_CLIENT_ID"], ENV["NB_CLIENT_SECRET"], site: "https://#{slug}.nationbuilder.com")
|
|
57
|
-
old_token = OAuth2::AccessToken.new(client, token, refresh_token: refresh_token)
|
|
58
|
-
new_token = old_token.refresh!
|
|
59
|
-
|
|
60
|
-
update!(token: new_token.token,
|
|
61
|
-
refresh_token: new_token.refresh_token,
|
|
62
|
-
token_expires_at: new_token.expires_at)
|
|
63
|
-
end
|
|
64
|
-
rescue OAuth2::Error => e
|
|
65
|
-
Rails.logger.error("Failed to refresh OAuth token for #{slug}: #{e.message}")
|
|
66
|
-
false
|
|
50
|
+
class Nation < ApplicationRecord
|
|
51
|
+
include NbApiClient::OAuthTokenRefresh
|
|
67
52
|
end
|
|
68
53
|
```
|
|
69
54
|
|
|
70
|
-
|
|
55
|
+
It also needs `config.oauth_client_id`/`config.oauth_client_secret` set (below; default from `NB_CLIENT_ID`/`NB_CLIENT_SECRET` env vars). Internally it POSTs to NationBuilder's `/oauth/token` endpoint via HTTParty (no `oauth2` gem dependency), and uses `with_lock` plus an `updated_at` re-check to guard against multiple concurrent requests for the same nation refreshing the token at once.
|
|
56
|
+
|
|
57
|
+
If your model doesn't fit that shape (different column names, no `with_lock`, non-ActiveRecord storage), write your own `refresh_oauth_token` instead — it just needs to return truthy on success, falsy on failure.
|
|
71
58
|
|
|
72
59
|
### Configuration
|
|
73
60
|
|
|
@@ -91,6 +78,11 @@ NbApiClient.configure do |config|
|
|
|
91
78
|
config.unauthorized_error_threshold = 25
|
|
92
79
|
config.unauthorized_error_window = 5 * 60
|
|
93
80
|
|
|
81
|
+
# Only needed if you include NbApiClient::OAuthTokenRefresh. Default from
|
|
82
|
+
# NB_CLIENT_ID/NB_CLIENT_SECRET env vars.
|
|
83
|
+
config.oauth_client_id = ENV["NB_CLIENT_ID"]
|
|
84
|
+
config.oauth_client_secret = ENV["NB_CLIENT_SECRET"]
|
|
85
|
+
|
|
94
86
|
# Cache store used to count those unauthorized responses, and (below) to
|
|
95
87
|
# back the sliding-window rate limiter. Defaults to Rails.cache; must
|
|
96
88
|
# respond to #increment(key, amount, options), #decrement, and #read.
|
|
@@ -30,6 +30,11 @@ module NbApiClient
|
|
|
30
30
|
attr_accessor :max_rate_limit_retries
|
|
31
31
|
attr_accessor :max_token_retries
|
|
32
32
|
|
|
33
|
+
# Client id/secret used by NbApiClient::OAuthTokenRefresh (only needed if
|
|
34
|
+
# you include that mixin instead of writing your own refresh_oauth_token).
|
|
35
|
+
attr_accessor :oauth_client_id
|
|
36
|
+
attr_accessor :oauth_client_secret
|
|
37
|
+
|
|
33
38
|
# Sliding-window rate limit: at most `rate_limit` requests per `rate_limit_window_seconds`,
|
|
34
39
|
# enforced across all processes via `cache_store` (above). If `cache_store` is nil,
|
|
35
40
|
# rate limiting is disabled entirely.
|
|
@@ -48,6 +53,8 @@ module NbApiClient
|
|
|
48
53
|
@short_circuit_in_test = true
|
|
49
54
|
@max_rate_limit_retries = 3
|
|
50
55
|
@max_token_retries = 2
|
|
56
|
+
@oauth_client_id = ENV["NB_CLIENT_ID"]
|
|
57
|
+
@oauth_client_secret = ENV["NB_CLIENT_SECRET"]
|
|
51
58
|
|
|
52
59
|
@rate_limit = ENV.fetch("API_RATE_LIMIT", 200).to_i
|
|
53
60
|
@rate_limit_window_seconds = ENV.fetch("API_RATE_WINDOW", 10).to_i
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NbApiClient
|
|
4
|
+
# Optional mixin implementing `refresh_oauth_token` (see README's "nation
|
|
5
|
+
# interface") for nations backed by an ActiveRecord-style model with
|
|
6
|
+
# `token`, `refresh_token`, `token_expires_at` columns, and `#with_lock`/
|
|
7
|
+
# `#update!`. `include NbApiClient::OAuthTokenRefresh` instead of writing
|
|
8
|
+
# your own refresh_oauth_token; write your own if your model differs
|
|
9
|
+
# (no `with_lock`, different column names, etc).
|
|
10
|
+
module OAuthTokenRefresh
|
|
11
|
+
def refresh_oauth_token
|
|
12
|
+
with_lock do
|
|
13
|
+
# Re-check after acquiring the lock in case another process already
|
|
14
|
+
# refreshed the token while this one was waiting.
|
|
15
|
+
return true if Time.now - updated_at < 5
|
|
16
|
+
|
|
17
|
+
response = HTTParty.post(
|
|
18
|
+
"https://#{slug}.nationbuilder.com/oauth/token",
|
|
19
|
+
body: {
|
|
20
|
+
grant_type: "refresh_token",
|
|
21
|
+
refresh_token: refresh_token,
|
|
22
|
+
client_id: NbApiClient.configuration.oauth_client_id,
|
|
23
|
+
client_secret: NbApiClient.configuration.oauth_client_secret
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
return false unless response.success?
|
|
27
|
+
|
|
28
|
+
update!(
|
|
29
|
+
token: response["access_token"],
|
|
30
|
+
refresh_token: response["refresh_token"],
|
|
31
|
+
token_expires_at: response["expires_in"] ? Time.now + response["expires_in"].to_i : nil
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
rescue => e
|
|
35
|
+
NbApiClient.configuration.logger&.error("[NbApiClient::OAuthTokenRefresh] Failed to refresh OAuth token for #{slug}: #{e.message}")
|
|
36
|
+
false
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
data/lib/nb_api_client.rb
CHANGED
|
@@ -15,6 +15,7 @@ require_relative "nb_api_client/configuration"
|
|
|
15
15
|
require_relative "nb_api_client/url_builder"
|
|
16
16
|
require_relative "nb_api_client/rate_limiter"
|
|
17
17
|
require_relative "nb_api_client/request"
|
|
18
|
+
require_relative "nb_api_client/oauth_token_refresh"
|
|
18
19
|
|
|
19
20
|
module NbApiClient
|
|
20
21
|
class << self
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: nb_api_client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex Flint
|
|
@@ -106,6 +106,7 @@ files:
|
|
|
106
106
|
- README.md
|
|
107
107
|
- lib/nb_api_client.rb
|
|
108
108
|
- lib/nb_api_client/configuration.rb
|
|
109
|
+
- lib/nb_api_client/oauth_token_refresh.rb
|
|
109
110
|
- lib/nb_api_client/rate_limiter.rb
|
|
110
111
|
- lib/nb_api_client/request.rb
|
|
111
112
|
- lib/nb_api_client/url_builder.rb
|