Skip to content

feat(rest): add GET /notifications/{id} - #640

Open
faisalahammad wants to merge 2 commits into
WordPress:developfrom
faisalahammad:feature/362-get-notification
Open

faisalahammad wants to merge 2 commits into
WordPress:developfrom
faisalahammad:feature/362-get-notification

Conversation

@faisalahammad

Copy link
Copy Markdown

What?

Adds GET /wp-notifications/v1/notifications/{id}. It returns the current user's notification for a message ID.

Fixes #362

Depends on #639. This branch is built on top of it (repository injection and the /notifications/{id} route), so the DELETE commit shows here too. Please review only the last commit, or merge #639 first.

Why?

#362 asks for an endpoint to get a single notification by ID. Same as in #639 and #612, the {id} is a message ID and a notification is that message for one user. So the response is the message joined with the user's row in notifications_queue.

How?

  • Notification_Repository::find_by_id() now has the signature find_by_id( int $message_id, int $user_id ): ?array, the same as in fix: make notification repository user-scoped #626. It returns array( 'notification' => Model\Notification, 'message' => Model\Message ) or null.

  • Wpdb_Notification_Repository::find_by_id() runs one prepared query that joins notifications_queue and notifications_messages on message_id and user_id. It decodes the meta JSON and builds both models with the existing factories. Table columns win over the same keys in meta.

  • Factory\Message now turns MySQL date strings into DateTime, like Factory\Notification already does. Without this a DB row throws a TypeError on created_at.

  • Notification_Controller gets a READABLE endpoint on notifications/(?P<id>[\d]+):

    • logged out: 401 rest_notifications_login_required
    • missing ID or another user's notification: 404 rest_notification_invalid_id. Same error for both, so it does not tell if another user has that message.
    • success: the notification object. status is dismissed if dismissed_at is set, else displayed if displayed_at is set, else undisplayed.
  • New prepare_item_for_response() supports context and _fields. Fields not in meta come back as null.

  • Schema fixes so the response matches its own schema:

    • user_id is now integer (was string).
    • icon is now string (was integer).
    • accept_label, accept_link, dismiss_label, expires_at, icon, message and title now allow null.

    This overlaps a bit with REST schema and response cleanup for the notifications controller #618. Happy to drop it here if REST schema and response cleanup for the notifications controller #618 lands first.

Testing Instructions

There is no UI yet to send a notification, so add rows by hand. Change the wp_ prefix if needed.

  1. Add two messages and queue rows:
    npx wp-env run cli wp db query "INSERT INTO wp_notifications_messages (id, channel_name, channel_title, created_at, severity, title, message, meta) VALUES (201, 'core/test', 'Test channel', '2026-01-02 03:04:05', 'info', 'Hello', 'A test message', '{\"accept_label\":\"Open\",\"accept_link\":\"https://example.org/\",\"icon\":\"admin-site\"}'), (202, 'core/test', 'Test channel', '2026-01-02 03:04:05', 'warning', 'Second', 'Another message', NULL);"
    npx wp-env run cli wp db query "INSERT INTO wp_notifications_queue (message_id, user_id, displayed_at, dismissed_at) VALUES (201, 1, NULL, NULL), (202, 1, '2026-01-03 00:00:00', '2026-01-04 00:00:00'), (202, 2, NULL, NULL);"
  2. Log in as user 1, open any wp-admin page and the browser console.
  3. Run await wp.apiFetch( { path: '/wp-notifications/v1/notifications/201' } );
    You get id: 201, user_id: 1, title: "Hello", accept_label: "Open", icon: "admin-site", created_at: "2026-01-02T03:04:05+00:00" and status: "undisplayed".
  4. Run it with /notifications/202. You get status: "dismissed" and accept_label: null (no meta).
  5. Run it with /notifications/201?_fields=id,title,status. You get only those 3 fields.
  6. Run it with /notifications/999999. You get rest_notification_invalid_id with status 404.
  7. Log in as user 2 and run it with /notifications/201. You get 404. With /notifications/202 you get user 2's copy with status: "undisplayed".
  8. Log out and run curl -i http://localhost:8888/wp-json/wp-notifications/v1/notifications/201. You get 401.

Automated tests: 6 new tests in test-notification-controller.php cover these cases, and two of them check the response against the schema. PHPUnit passes on single site and multisite, and composer lint is clean.

npx wp-env run tests-cli --env-cwd=wp-content/plugins/wp-feature-notifications vendor/bin/phpunit

Screenshots or screencast

REST only, no UI changes.

Remove the current user's copy of a notification. The message
itself is kept for other users. Returns 401 when logged out and
404 when the user has no such notification.

Fixes WordPress#363
- Add a readable endpoint on /notifications/{id} that returns the
  current user's notification for a message ID. Logged out users get
  401, missing or other users' notifications get 404.
- Implement Wpdb_Notification_Repository::find_by_id() with a JOIN of
  the queue and messages tables, scoped to the user.
- Deserialize MySQL date strings in the Message factory.
- Fix user_id and icon schema types and allow null on optional
  message fields so responses match the schema.
- Add tests for the new endpoint.

Fixes WordPress#362
@jonathanbossenger jonathanbossenger mentioned this pull request Oct 22, 2026
2 of 22 tasks

This branch has not been deployed

No deployments
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.

MVP: Notification Controller: GET /notifications/{id}

1 participant