Skip to content

DHMemberPortal: make signup scaffolding writes best-effort (#283 bandaid)#284

Draft
rubin110 wants to merge 1 commit into
devfrom
fix/signup-best-effort-scaffolding
Draft

DHMemberPortal: make signup scaffolding writes best-effort (#283 bandaid)#284
rubin110 wants to merge 1 commit into
devfrom
fix/signup-best-effort-scaffolding

Conversation

@rubin110
Copy link
Copy Markdown
Contributor

Summary

Bandaid for #283. /signup/submit was non-atomic — 8 sequential HTTP calls inside one try/except — so any transient failure after add_member succeeded would abort the redirect to /signup/payment and strand the user with a half-created row.

  • Critical path (get_access_token, get_member_id, add_member) still bounces the user to /signup on failure.
  • Once add_member returns a member_id, the seven secondary JSONB writes (connections/status/forms/notes/access/authorizations/extras) run in a for-loop with per-step try/except. Each step logs success at INFO and failure at ERROR; the loop never throws.
  • The user always reaches /signup/payment once the row exists.
  • Bumped the seven secondary helpers in dhservices.py from timeout=10timeout=20 as cheap insurance against slow DHService responses.

What this PR does not do

Known trade-offs

  • update_member_access is also used by the dashboard Keys page profile save. The 20s timeout now applies there too. The worst case is a 20s synchronous Flask wait on a real DHService timeout (previously 10s); normal writes are sub-second.
  • The "Sign up successful! Please complete payment." flash fires even if all 7 scaffolding steps failed. By design: the user is unblocked, admin tooling addresses the partial row later. The codebase has no partial-success flash convention.

Verification

Manual on dev (https://ps1-member.mime.starset.net) via Chrome devtools:

  • Happy path: walked the full flow with a fresh email; Stripe pricing-table iframe renders both tiers; DB row has all 7 JSONB columns populated; logs show 8 INFO lines (Created new member with ID: N + 7 × Signup member N: <label> initialized).
  • Fault injection (inline raise on forms + notes): rebuilt with a temporary raise RuntimeError mid-loop. User still reached /signup/payment. DB row has forms=null and notes=null as expected; other 5 columns populated. Logs show 2 ERROR lines + 5 INFO lines. Reverted before commit.
  • Existing-email retry: confirmed the early-return guard fires on a duplicate signup.

Test plan

  • Watch for any new signup-related error log lines in prod once deployed
  • Verify status->>'stripe_product_id' lands as expected via the ST2DH webhook for a real new signup (the scaffolding loop creates a pending status row; ST2DH writes stripe_product_id into the same JSONB on payment)
  • Spot-check update_member_access from the dashboard Keys tab still saves within the 20s ceiling

🤖 Generated with Claude Code

Issue #283. Once add_member succeeds, the seven secondary JSONB writes
(connections/status/forms/notes/access/authorizations/extras) run in a
loop with per-step try/except so a transient failure no longer aborts
the redirect to /signup/payment.

Also bumps the 10s timeout to 20s on the seven secondary helpers in
dhservices.py for cheap insurance against slow DHService responses.

A failed scaffolding step leaves the corresponding JSONB column null;
admin tooling can address that later. The user still reaches Stripe,
which is the bandaid's only goal.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants