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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c8a34771eec831f76ebbcab4057adab19e3ed2f0f18a7ea1fefdf4f6cb9c75c8
4
- data.tar.gz: d9c17c3d66fe7250dcb06c267530feda1255841509724b6ac061137c1584ff47
3
+ metadata.gz: fa4a409cda310073f64e30372fd23444e3785ac0a84bf75ec9a328c4ede4ea27
4
+ data.tar.gz: 5c7eecae6437924564604c56128a7a41a4a152a649cb51b94905becdeec00f20
5
5
  SHA512:
6
- metadata.gz: e2215188c41fa8820c2b6372cb94e29efabbdd98717b7806b107d8e0e28820daa137118a9de1dfab8e0ee2bb0e5fe4a2a848a9a2a3e56f98cab34a001ce0fff2
7
- data.tar.gz: 957fafb9f78368eeff8e0a42e9b01b8e1f5c0cfc8d7112bbcfb2247a62f4fad70632536c290d0f5af147740cccc48dba5165a64a00d430dc06d430cd3d9f7f71
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
- A typical `refresh_oauth_token`, using the [`oauth2`](https://github.com/oauth-xx/oauth2) gem (a separate dependency of your app, not of this gem) and an ActiveRecord model with `token`, `refresh_token`, and `token_expires_at` columns:
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
- def refresh_oauth_token
51
- with_lock do
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
- The `with_lock` (a `SELECT ... FOR UPDATE` row lock) and `updated_at` re-check guard against multiple concurrent requests for the same nation refreshing the token at once.
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NbApiClient
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
  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.0.0
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