Skip to content

feat(notifications): handle_comment_remix_contest_update trigger#848

Merged
raymondjacobson merged 3 commits into
mainfrom
api/handle-comment-remix-contest-update
May 22, 2026
Merged

feat(notifications): handle_comment_remix_contest_update trigger#848
raymondjacobson merged 3 commits into
mainfrom
api/handle-comment-remix-contest-update

Conversation

@raymondjacobson
Copy link
Copy Markdown
Member

Summary

Adds the missing fourth contest notification — remix_contest_update. Sibling of the three already in this repo:

Notification File
fan_remix_contest_started handle_event.sql
artist_remix_contest_submissions handle_track.sql
fan_remix_contest_submission handle_track.sql
remix_contest_update handle_comment_remix_contest_update.sql (this PR)

Triggers when the contest host posts a TOP-LEVEL comment on their own remix_contest event. Fans out one notification per active event subscriber (excluding the host). Mirrors AudiusProject/apps#14159's python create_comment block.

Recipient set + data shape

Matches the python indexer 1:1 so existing frontend renderers (the web + mobile components shipped in apps#14159) consume it without changes.

  • Recipient set: subscriptions WHERE entity_type='Event' AND user_id=event_id AND is_current AND NOT is_delete, excluding the host.
  • group_id: remix_contest_update:{comment_id}:event:{event_id}
  • specifier: {recipient_id} (per-recipient row; (group_id, specifier) is the unique constraint)
  • data: {event_id, entity_id (= contest's parent track), entity_user_id (= host), comment_id}

Why DEFERRABLE INITIALLY DEFERRED

The "is_reply" check looks at comment_threads. The indexer inserts the comment_threads row AFTER the comments row in the same transaction. A plain AFTER INSERT trigger on comments fires before comment_threads is visible and would misclassify every reply as top-level.

Using a CONSTRAINT TRIGGER ... DEFERRABLE INITIALLY DEFERRED fires the trigger at commit time, by which point both rows are visible. The trigger function checks EXISTS(SELECT 1 FROM comment_threads WHERE comment_id = NEW.comment_id) to detect replies.

Standard exception when others wrapper so a bug in the notification path never aborts the user's comment insert.

Tests

Three tests in api/v1_event_comments_notification_test.go, all exercising the trigger via app.writePool:

  • TestRemixContestUpdate_NotifiesEventSubscribers — host top-level comment on own event: one notification per non-host subscriber, with the expected group_id/specifier/data/user_ids shape. Host's own (self-)subscription is excluded.
  • TestRemixContestUpdate_SkipsReplies — host posts a reply (both comments and comment_threads INSERTs in the same transaction): zero notifications. Exercises the deferred-trigger contract.
  • TestRemixContestUpdate_SkipsNonHostComments — random non-host commenter on the event: zero notifications.

Companion change

OpenAudio/go-openaudio#311 ports the auto_subscribe_to_contest_on_submission half of apps#14159 to the Go ETL — without those subscriptions rows landing, the recipient set this trigger fans out to (and the two in handle_track.sql) would be empty.

🤖 Generated with Claude Code

raymondjacobson and others added 3 commits May 22, 2026 12:03
Adds the missing fourth contest notification trigger. Sibling of
handle_event.sql (fan_remix_contest_started) and the two emitted from
handle_track.sql (artist_remix_contest_submissions,
fan_remix_contest_submission). When the contest host posts a top-level
(non-reply) comment on their own remix_contest event, fan out one
`remix_contest_update` notification to every active event subscriber
except the host.

Recipient set mirrors AudiusProject/apps#14159's python create_comment:
`subscriptions WHERE entity_type='Event' AND user_id=event_id AND
is_current AND NOT is_delete`, excluding the host.

The `is_reply` check looks at `comment_threads` — but that row is
inserted AFTER the comments row in the same indexer transaction. A
plain AFTER INSERT trigger fires before comment_threads is visible and
would misclassify every reply as top-level. Solved with
DEFERRABLE INITIALLY DEFERRED so the trigger fires at commit time,
when both rows are visible.

Tests (api/v1_event_comments_notification_test.go):
- TestRemixContestUpdate_NotifiesEventSubscribers — host's top-level
  comment fans out to every non-host event subscriber, with the
  expected group_id / specifier / data shape
- TestRemixContestUpdate_SkipsReplies — host's reply (comment +
  comment_threads in the same tx) produces zero notifications
- TestRemixContestUpdate_SkipsNonHostComments — non-host commenter
  produces zero notifications

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… tests

comments.blocknumber has an FK to blocks.number. The three new tests
inserted comments without first seeding the matching blocks rows, so CI
failed with comments_blocknumber_fkey (SQLSTATE 23503). Add the missing
blocks fixtures (and align the parent-comment's blockhash with the
seeded blocks row in the SkipsReplies test).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The test DB is bootstrapped from sql/01_schema.sql (mounted into
postgres' /docker-entrypoint-initdb.d). New ddl/functions files don't
take effect in tests until the schema dump is regenerated. Adding the
function + constraint trigger here so the new tests find them on a
fresh template.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@raymondjacobson raymondjacobson merged commit 081664b into main May 22, 2026
5 checks passed
@raymondjacobson raymondjacobson deleted the api/handle-comment-remix-contest-update branch May 22, 2026 19:52
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.

1 participant