Heartbeat refresher for chains nearing refresh-token expiry - #3
Heartbeat refresher for chains nearing refresh-token expiry#3helording wants to merge 19 commits into
Conversation
…ken expiry A dormant shop's access token may sit fresh or untouched while its refresh token approaches the 90-day cliff, after which only the merchant can restore access. The new option forces a locked refresh when the refresh token expires inside the window, with the same lock-and-recheck dedup and stale-fallback semantics as the soft window. Groundwork for the KeepAlive process. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A supervision-tree GenServer that scans for chains whose refresh token expires inside a window and rotates them via the store's lock-serialized refresh_token/2 (refresh_token_window option). Multi-node safe: the row lock plus in-lock re-check collapse concurrent ticks into one Shopify call. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This comment was marked as outdated.
This comment was marked as outdated.
There was a problem hiding this comment.
Pull request overview
Adds proactive “keep-alive” refreshing for Shopify offline token chains that are approaching refresh-token expiry, so dormant shops don’t silently cross the 90‑day cliff. This is implemented by introducing a refresh-token-expiry window into the locked refresh decision path and a periodic heartbeat process that scans for soon-to-expire chains and rotates them.
Changes:
- Add
:refresh_token_windowoption to the locked refresh decision so refresh can be forced when the refresh token is near expiry. - Introduce
ExShopifyApp.AccessToken.HeartbeatGenServer to periodically scan and rotate expiring chains. - Rework tests to use a Mox-backed Tesla adapter and SQL Sandbox (plus new test helpers/case template).
Reviewed changes
Copilot reviewed 15 out of 16 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| test/test_helper.exs | Defines Mox Tesla adapter mock, enables log capture, and sets SQL sandbox mode. |
| test/support/repo_case.ex | Adds a shared-mode sandbox + Mox-global test case template for repo/heartbeat tests. |
| test/support/factory.ex | Introduces ExMachina factories/fixtures for access-token rows and mock responses. |
| test/ex_shopify_app/access_token/token_test.exs | Updates token expiry tests and adds refresh_token_expiring?/3 coverage. |
| test/ex_shopify_app/access_token/repo_test.exs | Refactors repo tests to RepoCase + Mox adapter expectations; adds :refresh_token_window tests. |
| test/ex_shopify_app/access_token/lock_timeout_test.exs | Adds a dedicated real-connection test for the lock_timeout behavior. |
| test/ex_shopify_app/access_token/heartbeat_test.exs | Adds tests for the new Heartbeat tick behavior. |
| test/ex_shopify_app/access_token_test.exs | Switches HTTP mocking from Tesla.Mock to Mox adapter expectations. |
| README.md | Documents adding the heartbeat process to a supervision tree. |
| mix.exs | Adds :mox and :ex_machina as test dependencies. |
| mix.lock | Locks new test deps (mox, ex_machina, nimble_ownership). |
| lib/ex_shopify_app/access_token/token.ex | Adds refresh_token_expiring?/3 helper (used to force refresh near refresh-token expiry). |
| lib/ex_shopify_app/access_token/repo/options.ex | Adds option reader for :refresh_token_window. |
| lib/ex_shopify_app/access_token/repo.ex | Integrates refresh-token-expiry window into the refresh decision (refresh_needed?/3). |
| lib/ex_shopify_app/access_token/heartbeat.ex | New GenServer that periodically scans and refreshes expiring chains. |
| config/test.exs | Routes Tesla through the Mox-backed adapter and enables Sandbox pool for TestRepo. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@NexPB copilot comments resolved |
Resolve conflicts in mix.exs and repo_test.exs, and adapt master's migrate/2 tests and migrations test to the new Mox + ex_machina fixture structure introduced on this branch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve conflicts in mix.exs and test/ex_shopify_app/access_token/repo_test.exs. Adapt master's migrate/2 and migrations tests to this branch's new Mox + ex_machina fixture structure (replacing the Agent counter / Tesla.Mock setup). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…_shopify_app into keepalive-refresher
58b89a6 to
110405d
Compare
NexPB
left a comment
There was a problem hiding this comment.
Reminder: think about global setting of the heartbeat server
| import ExShopifyApp.Factory | ||
| import ExShopifyApp.TestHelpers, only: [stored: 1] | ||
|
|
||
| import ExShopifyApp.HTTPMockHelpers, |
There was a problem hiding this comment.
[nitpick] repo case importing http stubs be default feels a little off
On-demand refreshing only fires when a token is used, so a dormant shop can silently cross the 90-day refresh-token expiry. This adds a supervision-tree GenServer that proactively rotates chains before they reach the cliff.
Changes
refresh_token_windowoption on the store'srefresh_token/2, so a refresh can be triggered for chains whose refresh token expires within a window (not just expired access tokens).ExShopifyApp.AccessToken.KeepAliveGenServer: scans every:interval(default 6h) for chains whose refresh token expires within:window(default 7 days) and refreshes them through the store's lock-serialized path. Multi-node safe — the row lock plus in-lock re-check collapse concurrent ticks into a single Shopify call. Lifetime (non-expiring) rows are never touched.Notes
Split out of #2, which now only contains the
AccessToken.Migrationshelper. Independent of that PR — branches offmaster.🤖 Generated with Claude Code