Skip to content

feat(notification): implement GetNotificationUnreadCount API - #2751

Merged
hvn2k1 merged 1 commit into
mainfrom
get-notification-unread-count
Aug 6, 2026
Merged

feat(notification): implement GetNotificationUnreadCount API#2751
hvn2k1 merged 1 commit into
mainfrom
get-notification-unread-count

Conversation

@hvn2k1

@hvn2k1 hvn2k1 commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Follows #2750

What this PR does

Implements the GetNotificationUnreadCount RPC: 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

  • The count is a single COUNT query with a correlated NOT EXISTS on notification_read — no fan-out rows, O(1) with respect to user count, per the RFC's read-time derivation design.
  • The account-created bound reuses the same earliestAccountCreatedAt lookup as ListNotifications' UNREAD filter, guaranteeing badge/tab consistency.
  • This branch is stacked on feat(notification): implement MarkNotificationsAsRead API #2750 (mark-as-read), which is approved but blocked from merging by the notify-success-to-slack required-check misconfiguration in the main ruleset; the first commits shown here belong to that PR and will disappear once it merges.
  • MarkAllNotificationsAsRead is the only remaining stub.

Copilot AI review requested due to automatic review settings August 6, 2026 03:54
@hvn2k1
hvn2k1 requested a review from Ubisoft-potato August 6, 2026 03:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 GetNotificationUnreadCount RPC response field (count) and implement handler + storage count query using NOT EXISTS on notification_read and the earliest-account-created bound.
  • Implement MarkNotificationsAsRead end-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

  • QueryRowContext is 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: QueryRowContext is 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

  • QueryRowContext is 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: QueryRowContext is 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.

Comment thread pkg/notification/storage/mysql/notification_test.go
Comment thread pkg/notification/storage/postgres/notification_test.go
@hvn2k1
hvn2k1 force-pushed the get-notification-unread-count branch from cf0886d to a626b83 Compare August 6, 2026 04:00
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@hvn2k1
hvn2k1 marked this pull request as ready for review August 6, 2026 04:04

@Ubisoft-potato Ubisoft-potato left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! LGTM!

@hvn2k1
hvn2k1 merged commit 8f4d576 into main Aug 6, 2026
11 checks passed
@hvn2k1
hvn2k1 deleted the get-notification-unread-count branch August 6, 2026 09:04
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.

3 participants