feat(notifications): handle_comment_remix_contest_update trigger#848
Merged
Conversation
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>
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.
Summary
Adds the missing fourth contest notification —
remix_contest_update. Sibling of the three already in this repo:fan_remix_contest_startedhandle_event.sqlartist_remix_contest_submissionshandle_track.sqlfan_remix_contest_submissionhandle_track.sqlremix_contest_updatehandle_comment_remix_contest_update.sql(this PR)Triggers when the contest host posts a TOP-LEVEL comment on their own
remix_contestevent. Fans out one notification per active event subscriber (excluding the host). Mirrors AudiusProject/apps#14159's pythoncreate_commentblock.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.
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 DEFERREDThe "is_reply" check looks at
comment_threads. The indexer inserts thecomment_threadsrow AFTER thecommentsrow in the same transaction. A plainAFTER INSERTtrigger oncommentsfires beforecomment_threadsis visible and would misclassify every reply as top-level.Using a
CONSTRAINT TRIGGER ... DEFERRABLE INITIALLY DEFERREDfires the trigger at commit time, by which point both rows are visible. The trigger function checksEXISTS(SELECT 1 FROM comment_threads WHERE comment_id = NEW.comment_id)to detect replies.Standard
exception when otherswrapper 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 viaapp.writePool:TestRemixContestUpdate_NotifiesEventSubscribers— host top-level comment on own event: one notification per non-host subscriber, with the expectedgroup_id/specifier/data/user_idsshape. Host's own (self-)subscription is excluded.TestRemixContestUpdate_SkipsReplies— host posts a reply (bothcommentsandcomment_threadsINSERTs 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_submissionhalf of apps#14159 to the Go ETL — without thosesubscriptionsrows landing, the recipient set this trigger fans out to (and the two inhandle_track.sql) would be empty.🤖 Generated with Claude Code