diff --git a/services/notifications/app.js b/services/notifications/app.js index ea32c83329..82ee1960eb 100644 --- a/services/notifications/app.js +++ b/services/notifications/app.js @@ -7,7 +7,7 @@ import express from 'express' import methodOverride from 'method-override' import { mongoClient } from './app/js/mongodb.js' import NotificationsController from './app/js/NotificationsController.ts' -import HealthCheckController from './app/js/HealthCheckController.js' +import HealthCheckController from './app/js/HealthCheckController.ts' import { isZodErrorLike } from 'zod-validation-error' import { ParamsError } from '@overleaf/validation-tools' diff --git a/services/notifications/app/js/HealthCheckController.js b/services/notifications/app/js/HealthCheckController.ts similarity index 74% rename from services/notifications/app/js/HealthCheckController.js rename to services/notifications/app/js/HealthCheckController.ts index 4eb289cc62..c0fbd9206d 100644 --- a/services/notifications/app/js/HealthCheckController.js +++ b/services/notifications/app/js/HealthCheckController.ts @@ -7,14 +7,19 @@ import { RequestFailedError, } from '@overleaf/fetch-utils' import { expressify } from '@overleaf/promise-utils' +import { z, zz } from '@overleaf/validation-tools' +import type { Request, Response } from 'express' const { port } = settings.internal.notifications -function makeUrl(userId, endPath = '') { - return new URL(`/user/${userId}/${endPath}`, `http://127.0.0.1:${port}`) +function makeUrl(userId: string, endPath?: string) { + return new URL( + `/user/${userId}${endPath ? `/${endPath}` : ''}`, + `http://127.0.0.1:${port}` + ) } -async function makeNotification(notificationKey, userId) { +async function makeNotification(notificationKey: string, userId: string) { const postOpts = { method: 'POST', json: { @@ -29,12 +34,23 @@ async function makeNotification(notificationKey, userId) { await fetchNothing(url, postOpts) } -async function getUsersNotifications(userId) { +const getUserNotificationsResponseSchema = z + .object({ + _id: zz.objectId(), + key: z.string(), + messageOpts: z.string().optional(), + templateKey: z.string().optional(), + user_id: zz.objectId(), + }) + .array() + +async function getUsersNotifications(userId: string) { const url = makeUrl(userId) try { - return await fetchJson(url, { + const body = await fetchJson(url, { signal: AbortSignal.timeout(5000), }) + return getUserNotificationsResponseSchema.parse(body) } catch (err) { if (err instanceof RequestFailedError) { logger.err({ err }, 'Non-2xx status code received') @@ -45,7 +61,7 @@ async function getUsersNotifications(userId) { } } -async function userHasNotification(userId, notificationKey) { +async function userHasNotification(userId: string, notificationKey: string) { const body = await getUsersNotifications(userId) const hasNotification = body.some( notification => @@ -62,11 +78,15 @@ async function userHasNotification(userId, notificationKey) { } } -async function cleanupNotifications(userId) { +async function cleanupNotifications(userId: string) { await db.notifications.deleteOne({ user_id: userId }) } -async function deleteNotification(userId, notificationId, notificationKey) { +async function deleteNotification( + userId: string, + notificationId: string, + notificationKey: string +) { const deleteByIdUrl = makeUrl(userId, `notification/${notificationId}`) try { await fetchNothing(deleteByIdUrl, { @@ -100,7 +120,7 @@ async function deleteNotification(userId, notificationId, notificationKey) { } } -async function check(req, res) { +async function check(req: Request, res: Response) { const userId = new ObjectId().toString() let notificationKey = `smoke-test-notification-${new ObjectId()}`