feat(notification): implement GetNotificationUnreadCount API - #2751
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
Implements the remaining notification-center read-side capabilities needed by the console: (1) an unread-count RPC for the bell badge, and (2) the “mark as read” RPC (stacked from #2750) with storage support for both MySQL and Postgres.
Changes:
- Add
GetNotificationUnreadCountRPC response field (count) and implement handler + storage count query usingNOT EXISTSonnotification_readand the earliest-account-created bound. - Implement
MarkNotificationsAsReadend-to-end (proto + API + MySQL/Postgres storage upsert SQL). - Add/extend mocks and unit tests for the new storage and API behavior.
Reviewed changes
Copilot reviewed 14 out of 17 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| proto/proto.lock | Locks updated proto schema for unread-count response and mark-as-read request fields/options. |
| proto/notification/service.proto | Adds count to unread-count response and defines ids for mark-as-read request with OpenAPI constraints. |
| pkg/notification/storage/postgres/sql/insert_notification_read.sql | Postgres upsert for inserting read markers guarded by published+not-deleted. |
| pkg/notification/storage/postgres/sql/count_unread_notifications.sql | Base COUNT query used by query builder for unread-count. |
| pkg/notification/storage/postgres/notification.go | Adds storage methods for marking reads and counting unread notifications. |
| pkg/notification/storage/postgres/notification_test.go | Adds unit tests for new Postgres storage methods. |
| pkg/notification/storage/notification.go | Extends NotificationStorage interface with mark-as-read and unread-count methods. |
| pkg/notification/storage/mysql/sql/insert_notification_read.sql | MySQL insert-ignore upsert for inserting read markers guarded by published+not-deleted. |
| pkg/notification/storage/mysql/sql/count_unread_notifications.sql | Base COUNT query used by query builder for unread-count. |
| pkg/notification/storage/mysql/notification.go | Adds storage methods for marking reads and counting unread notifications. |
| pkg/notification/storage/mysql/notification_test.go | Adds unit tests for new MySQL storage methods. |
| pkg/notification/storage/mock/notification.go | Updates generated mock to include the new storage interface methods. |
| pkg/notification/api/error.go | Adds invalid-arg and max-exceeded statuses for mark-as-read ids validation. |
| pkg/notification/api/api.go | Implements GetNotificationUnreadCount and MarkNotificationsAsRead handlers. |
| pkg/notification/api/api_test.go | Adds handler tests for mark-as-read and unread-count. |
| api-description/web-api.swagger.yaml | Updates OpenAPI description and schemas for unread-count response and mark-as-read request. |
Files not reviewed (1)
- pkg/notification/storage/mock/notification.go: Generated file
Suppressed comments (4)
pkg/notification/storage/mysql/notification_test.go:1111
QueryRowContextis variadic; this expectation does not include enough argument matchers for the constructed COUNT query (filters + NOT EXISTS). It should match the full argument list, otherwise the mock will not recognize the call.
s.qe.(*mock.MockQueryExecer).EXPECT().QueryRowContext(
gomock.Any(), gomock.Any(), gomock.Any(),
).Return(row)
pkg/notification/storage/mysql/notification_test.go:1129
- Same issue as above:
QueryRowContextis variadic, but this expectation only provides a single variadic matcher. The unread-count query passes multiple SQL args, so the mock call won’t match.
s.qe.(*mock.MockQueryExecer).EXPECT().QueryRowContext(
gomock.Any(), gomock.Any(), gomock.Any(),
).Return(row)
pkg/notification/storage/postgres/notification_test.go:1141
QueryRowContextis variadic; this expectation does not include enough argument matchers for the constructed COUNT query (filters + NOT EXISTS). It should match the full argument list, otherwise the mock will not recognize the call.
s.qe.(*mock.MockQueryExecer).EXPECT().QueryRowContext(
gomock.Any(), gomock.Any(), gomock.Any(),
).Return(row)
pkg/notification/storage/postgres/notification_test.go:1159
- Same issue as above:
QueryRowContextis variadic, but this expectation only provides a single variadic matcher. The unread-count query passes multiple SQL args, so the mock call won’t match.
s.qe.(*mock.MockQueryExecer).EXPECT().QueryRowContext(
gomock.Any(), gomock.Any(), gomock.Any(),
).Return(row)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
hvn2k1
force-pushed
the
get-notification-unread-count
branch
from
August 6, 2026 04:00
cf0886d to
a626b83
Compare
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
hvn2k1
marked this pull request as ready for review
August 6, 2026 04:04
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.
Follows #2750
What this PR does
Implements the
GetNotificationUnreadCountRPC: returns the requesting user's unread notification count, backing the bell badge in the console.Background / Why this PR is needed
This is the last read-side API of the notification center besides mark-all-as-read. The badge count must always agree with the Unread tab, so it uses the identical definition: published, not soft-deleted, no read marker for the viewer, and only notifications published after the viewer's account was created.
Points
COUNTquery with a correlatedNOT EXISTSonnotification_read— no fan-out rows, O(1) with respect to user count, per the RFC's read-time derivation design.earliestAccountCreatedAtlookup as ListNotifications' UNREAD filter, guaranteeing badge/tab consistency.notify-success-to-slackrequired-check misconfiguration in the main ruleset; the first commits shown here belong to that PR and will disappear once it merges.MarkAllNotificationsAsReadis the only remaining stub.