feat(notification): add batch mark-as-read endpoint#1709
Conversation
- Add PUT /api/v1/notifications/read endpoint - Support marking all unread or specific IDs as read - Validate ownership (403 for non-owned/non-existent IDs) - Enforce max 1000 IDs per request - Add integration tests (9 cases) - Add OpenAPI documentation Closes #1559
Paul-AUB
left a comment
There was a problem hiding this comment.
Clean, well-scoped implementation that matches the issue spec closely. The ownership check via a single count() query is an efficient approach, and returning 403 for both non-owned and non-existent IDs neatly prevents ID enumeration. Route registration (PUT vs. the existing POST — no method collision), policy, Swagger, and the 9 integration tests all look solid. A few non-blocking points below.
Suggestions (Should Consider)
-
[mark-as-read-batch.js:24-33] Duplicate IDs in the request can produce a false
403. If the client sends{ ids: [1, 1, 2] },count()returns2(distinct rows) whileids.lengthis3, so the strictownedCount !== ids.lengthcheck rejects a request whose IDs all legitimately belong to the user. Consider de-duplicating up front, e.g.const ids = [...new Set(rawIds)], before the length/ownership comparisons. This also makes theMAX_IDSceiling reflect distinct IDs. -
[mark-as-read-batch.js:24]
mark-as-read.jscasts the token id withNumber(req.token.id)when comparing ownership, whereas herenotified: req.token.idis passed to Waterline raw. It mirrorscount-unread.js, so it's consistent and the tests pass — just flagging the divergence frommark-as-read.jsin case a future change relies on a numeric type. No action needed if Waterline coercion is being relied on intentionally.
Nitpicks (Optional)
- [mark-as-read-batch.js] Unlike
count-unread.js(which wraps its logic intry/catch+ErrorService), this controller has no explicit error handling. It follows themark-as-read.jsstyle and Sails will surface a 500 on rejection, so this is fine — noting only for consistency awareness across the notification controllers.
- Rename raw input to rawIds, de-duplicate with Set before length/ownership comparisons - Prevents false 403 when client sends repeated IDs (e.g. [1, 1, 2]) - MAX_IDS ceiling now reflects distinct IDs
|
Thanks for the review @Paul-AUB! Item 1 (duplicate IDs) — Good catch. Fixed in 0108e72: the raw input is now de-duplicated with Item 2 (token id coercion) — Acknowledged, intentionally consistent with Item 3 (no try/catch) — Acknowledged, follows |
🤔 What
PUT /api/v1/notifications/readendpoint for batch marking notifications as read🤷♂️ Why
Users with many unread notifications (after a vacation or a burst of activity in subscribed areas) have no efficient way to clear them. The front-end (GrottoCenter/grottocenter-front#1427) needs a batch endpoint to support a "mark all as read" action and future checkbox selection.
🔍 How
api/controllers/v1/notification/mark-as-read-batch.js):idsis provided and non-empty: validates they are positive integers, enforces a max of 1000 IDs, verifies ownership via a singlecount()query, then batch-updates only unread onesidsis omitted or empty: marks all unread notifications for the authenticated user as readPUT /api/v1/notifications/read→v1/notification/mark-as-read-batchtokenAuth(JWT required)🧪 Testing
9 integration tests covering:
All 3097 existing tests pass with no regressions.