feat(users): add endpoint to update users' marketing updates by email#526
Open
dspachos wants to merge 9 commits into
Open
feat(users): add endpoint to update users' marketing updates by email#526dspachos wants to merge 9 commits into
dspachos wants to merge 9 commits into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds marketing-consent tracking to users and introduces admin APIs that update the flag and sync it to HubSpot.
Changes:
- Add
receive_marketing_updatescolumn + schema fields and default behavior (false). - Add
PUT /users/by-email/marketing-updates(system-admin) and HubSpot sync on updates (by-id and by-email). - Minor formatting/refactors in tests and scripts.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_users.py | Adds assertions/tests for the new marketing-updates behavior and HubSpot sync. |
| tests/test_trial_access.py | Updates DBUser mocks to include the new attribute. |
| tests/test_public_models_missing.py | Formatting-only updates to with (...) blocks and long assertions. |
| scripts/check_missing_models.py | Formatting-only updates for line wrapping. |
| app/services/hubspot.py | Introduces HubSpot client/service for upserting contact marketable status. |
| app/schemas/models.py | Adds receive_marketing_updates to user schemas + new request payload schema. |
| app/migrations/versions/20260520_160000_add_receive_marketing_updates_to_users.py | Alembic migration adding the new non-null boolean column with default. |
| app/main.py | Formatting-only change in OpenAPI customization. |
| app/db/models.py | Adds receive_marketing_updates column to DBUser. |
| app/core/config.py | Adds HUBSPOT_TOKEN setting. |
| app/api/users.py | Adds by-email marketing-updates endpoint and triggers HubSpot sync when the field changes. |
| app/api/public.py | Minor formatting-only tweaks. |
Comments suppressed due to low confidence (1)
app/api/users.py:963
update_usercommits and refreshes the DB user before calling HubSpot. If the HubSpot call fails, the client receives an error response even though the user record was already updated in the DB. Align the transactional semantics by either making HubSpot sync best-effort after commit, or performing HubSpot sync before commit and rolling back on failure.
if (
user_update.receive_marketing_updates is not None
and user_update.receive_marketing_updates != previous_marketing_updates
):
hubspot = HubSpotService()
await hubspot.upsert_contact_marketable_status(
email=db_user.email, enabled=db_user.receive_marketing_updates
)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Agent-Logs-Url: https://github.com/amazeeio/amazee.ai/sessions/bd59c38c-40db-4097-ba17-ec52137a3905 Co-authored-by: dspachos <6309422+dspachos@users.noreply.github.com>
pmelab
approved these changes
May 22, 2026
…e-marketing-updates
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Greptile Summary
This PR adds a
receive_marketing_updatesboolean field to the user model (with a matching migration), a newPUT /users/by-email/marketing-updatesendpoint to bulk-update that flag by normalised email, and a newHubSpotServicethat syncs the marketable status to HubSpot's CRM on every change.update_userpath commit the database before calling HubSpot; a HubSpot failure returns 502 while leaving the database already updated — and inupdate_userthe retry-skip guard (!= previous_marketing_updates) prevents a subsequent retry from ever reaching HubSpot again.user+tag@example.com) rather than the normalised canonical address used to look them up, which will create duplicate HubSpot contacts instead of updating the existing one.Confidence Score: 3/5
The core user-update and bulk-by-email paths both commit to the database before calling HubSpot; a transient HubSpot error leaves the two systems permanently out of sync with no recovery path.
Two distinct defects exist in the changed code: the update_user retry-skip guard means a failed HubSpot call is unrecoverable without a manual DB revert, and the bulk endpoint calls HubSpot with aliased addresses that will silently create duplicate contacts. Both affect the core purpose of this feature.
app/api/users.py — specifically the HubSpot call placement in update_user (after db.commit) and the email passed to upsert_contact_marketable_status in update_users_marketing_updates_by_email.
Important Files Changed
Sequence Diagram
sequenceDiagram participant Client participant API as PUT /by-email/marketing-updates participant DB as Database participant HS as HubSpotService Client->>API: "PUT {email, receive_marketing_updates}" API->>DB: Query users by normalised email DB-->>API: "[user1 (foo+a@ex.com), user2 (foo+b@ex.com)]" API->>DB: UPDATE receive_marketing_updates for all API->>DB: db.commit() DB updated API->>DB: db.refresh(users) loop Each user (raw aliased email) API->>HS: "upsert_contact(email=foo+a@ex.com)" Note over HS: Aliased email may create new HubSpot contact HS-->>API: 200 OK or 502 error Note over API: If 502 DB committed but client receives error end API-->>Client: 200 [users] or 502 (DB already committed)Reviews (1): Last reviewed commit: "feat(users): add receive_marketing_updat..." | Re-trigger Greptile