From f295bbc3beef75276cb6ab86970bf9bbc19e01b9 Mon Sep 17 00:00:00 2001 From: Andrew Rumble Date: Fri, 4 Jul 2025 11:57:00 +0100 Subject: [PATCH] Convert files to ES modules GitOrigin-RevId: 713430521d60f37ee20906bb2d5d0a56849a729d --- services/notifications/app.js | 66 +++++++++---------- .../app/js/HealthCheckController.js | 20 +++--- .../notifications/app/js/Notifications.js | 17 +---- .../app/js/NotificationsController.js | 8 +-- services/notifications/app/js/mongodb.js | 28 ++++---- ...ings.defaults.js => settings.defaults.cjs} | 0 services/notifications/package.json | 1 + .../unit/js/NotificationsControllerTest.js | 30 ++++----- .../test/unit/js/NotificationsTests.js | 50 +++++++------- services/notifications/vitest.config.cjs | 9 +++ 10 files changed, 111 insertions(+), 118 deletions(-) rename services/notifications/config/{settings.defaults.js => settings.defaults.cjs} (100%) create mode 100644 services/notifications/vitest.config.cjs diff --git a/services/notifications/app.js b/services/notifications/app.js index 38292c7c13..a55d61199f 100644 --- a/services/notifications/app.js +++ b/services/notifications/app.js @@ -1,38 +1,40 @@ // Metrics must be initialized before importing anything else -require('@overleaf/metrics/initialize') +import '@overleaf/metrics/initialize.js' +import metrics from '@overleaf/metrics' +import Settings from '@overleaf/settings' +import logger from '@overleaf/logger' +import express from 'express' +import methodOverride from 'method-override' +import { mongoClient } from './app/js/mongodb.js' +import NotificationsController from './app/js/NotificationsController.js' +import HealthCheckController from './app/js/HealthCheckController.js' -const metrics = require('@overleaf/metrics') -const Settings = require('@overleaf/settings') -const logger = require('@overleaf/logger') -logger.initialize('notifications') -const express = require('express') const app = express() -const methodOverride = require('method-override') -const bodyParser = require('body-parser') -const { mongoClient } = require('./app/js/mongodb') -const controller = require('./app/js/NotificationsController') + +logger.initialize('notifications') metrics.memory.monitor(logger) metrics.open_sockets.monitor() -const HealthCheckController = require('./app/js/HealthCheckController') - app.use(methodOverride()) -app.use(bodyParser()) +app.use(express.json()) app.use(metrics.http.monitor(logger)) metrics.injectMetricsRoute(app) -app.post('/user/:user_id', controller.addNotification) -app.get('/user/:user_id', controller.getUserNotifications) +app.post('/user/:user_id', NotificationsController.addNotification) +app.get('/user/:user_id', NotificationsController.getUserNotifications) app.delete( '/user/:user_id/notification/:notification_id', - controller.removeNotificationId + NotificationsController.removeNotificationId +) +app.delete('/user/:user_id', NotificationsController.removeNotificationKey) +app.delete('/key/:key', NotificationsController.removeNotificationByKeyOnly) +app.get('/key/:key/count', NotificationsController.countNotificationsByKeyOnly) +app.delete( + '/key/:key/bulk', + NotificationsController.deleteUnreadNotificationsByKeyOnlyBulk ) -app.delete('/user/:user_id', controller.removeNotificationKey) -app.delete('/key/:key', controller.removeNotificationByKeyOnly) -app.get('/key/:key/count', controller.countNotificationsByKeyOnly) -app.delete('/key/:key/bulk', controller.deleteUnreadNotificationsByKeyOnlyBulk) app.get('/status', (req, res) => res.send('notifications is up')) @@ -49,17 +51,15 @@ app.get('/health_check', (req, res) => app.get('*', (req, res) => res.sendStatus(404)) -const host = Settings.internal?.notifications?.host || '127.0.0.1' -const port = Settings.internal?.notifications?.port || 3042 +const host = Settings.internal.notifications?.host || '127.0.0.1' +const port = Settings.internal.notifications?.port || 3042 +try { + await mongoClient.connect() +} catch (err) { + logger.fatal({ err }, 'Cannot connect to mongo. Exiting.') + process.exit(1) +} -mongoClient - .connect() - .then(() => { - app.listen(port, host, () => - logger.debug(`notifications starting up, listening on ${host}:${port}`) - ) - }) - .catch(err => { - logger.fatal({ err }, 'Cannot connect to mongo. Exiting.') - process.exit(1) - }) +app.listen(port, host, () => + logger.debug({}, `notifications starting up, listening on ${host}:${port}`) +) diff --git a/services/notifications/app/js/HealthCheckController.js b/services/notifications/app/js/HealthCheckController.js index b08be32cf8..96b189bf73 100644 --- a/services/notifications/app/js/HealthCheckController.js +++ b/services/notifications/app/js/HealthCheckController.js @@ -1,6 +1,3 @@ -/* eslint-disable - no-dupe-keys, -*/ // TODO: This file was created by bulk-decaffeinate. // Fix any style issues and re-enable lint. /* @@ -9,14 +6,15 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const { db, ObjectId } = require('./mongodb') -const request = require('request') -const async = require('async') -const settings = require('@overleaf/settings') -const { port } = settings.internal.notifications -const logger = require('@overleaf/logger') +import { db, ObjectId } from './mongodb.js' +import request from 'request' +import async from 'async' +import settings from '@overleaf/settings' +import logger from '@overleaf/logger' -module.exports = { +const { port } = settings.internal.notifications + +export default { check(callback) { const userId = new ObjectId() const cleanupNotifications = callback => @@ -28,7 +26,7 @@ module.exports = { timeout: 5000, }) logger.debug( - { userId, opts: getOpts(), key: notificationKey, userId }, + { opts: getOpts(), key: notificationKey, userId }, 'Health Check: running' ) const jobs = [ diff --git a/services/notifications/app/js/Notifications.js b/services/notifications/app/js/Notifications.js index 9c4df59bd2..7c7fb8f237 100644 --- a/services/notifications/app/js/Notifications.js +++ b/services/notifications/app/js/Notifications.js @@ -1,18 +1,7 @@ -/* eslint-disable - no-unused-vars, -*/ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ -const logger = require('@overleaf/logger') -const { db, ObjectId } = require('./mongodb') +import logger from '@overleaf/logger' +import { db, ObjectId } from './mongodb.js' -module.exports = { +export default { getUserNotifications(userId, callback) { if (callback == null) { callback = function () {} diff --git a/services/notifications/app/js/NotificationsController.js b/services/notifications/app/js/NotificationsController.js index 89b7fa97d6..31d15ba581 100644 --- a/services/notifications/app/js/NotificationsController.js +++ b/services/notifications/app/js/NotificationsController.js @@ -6,11 +6,11 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const Notifications = require('./Notifications') -const logger = require('@overleaf/logger') -const metrics = require('@overleaf/metrics') +import logger from '@overleaf/logger' +import metrics from '@overleaf/metrics' +import Notifications from './Notifications.js' -module.exports = { +export default { getUserNotifications(req, res, next) { logger.debug( { userId: req.params.user_id }, diff --git a/services/notifications/app/js/mongodb.js b/services/notifications/app/js/mongodb.js index e0dd89fa08..9fc9b78d4b 100644 --- a/services/notifications/app/js/mongodb.js +++ b/services/notifications/app/js/mongodb.js @@ -1,26 +1,22 @@ -// @ts-check +import Metrics from '@overleaf/metrics' +import MongoUtils from '@overleaf/mongo-utils' +import Settings from '@overleaf/settings' +import mongodb from 'mongodb-legacy' -const Metrics = require('@overleaf/metrics') -const MongoUtils = require('@overleaf/mongo-utils') -const Settings = require('@overleaf/settings') -const { MongoClient, ObjectId } = require('mongodb-legacy') - -const mongoClient = new MongoClient(Settings.mongo.url, Settings.mongo.options) +export const mongoClient = new mongodb.MongoClient( + Settings.mongo.url, + Settings.mongo.options +) const mongoDb = mongoClient.db() -const db = { +export const db = { notifications: mongoDb.collection('notifications'), } +export const ObjectId = mongodb.ObjectId + Metrics.mongodb.monitor(mongoClient) -async function cleanupTestDatabase() { +export async function cleanupTestDatabase() { await MongoUtils.cleanupTestDatabase(mongoClient) } - -module.exports = { - db, - mongoClient, - ObjectId, - cleanupTestDatabase, -} diff --git a/services/notifications/config/settings.defaults.js b/services/notifications/config/settings.defaults.cjs similarity index 100% rename from services/notifications/config/settings.defaults.js rename to services/notifications/config/settings.defaults.cjs diff --git a/services/notifications/package.json b/services/notifications/package.json index 5e2cbc48f0..50911e9d5d 100644 --- a/services/notifications/package.json +++ b/services/notifications/package.json @@ -3,6 +3,7 @@ "description": "An API to handle user notifications", "private": true, "main": "app.js", + "type": "module", "scripts": { "start": "node app.js", "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", diff --git a/services/notifications/test/unit/js/NotificationsControllerTest.js b/services/notifications/test/unit/js/NotificationsControllerTest.js index 4c0626d0b9..6afeda56a1 100644 --- a/services/notifications/test/unit/js/NotificationsControllerTest.js +++ b/services/notifications/test/unit/js/NotificationsControllerTest.js @@ -9,11 +9,11 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require('sinon') -const modulePath = '../../../app/js/NotificationsController.js' -const SandboxedModule = require('sandboxed-module') -const assert = require('node:assert') +import { stub } from 'sinon' +import { require as _require } from 'sandboxed-module' +import assert from 'node:assert' +const modulePath = '../../../app/js/NotificationsController.js' const userId = '51dc93e6fb625a261300003b' const notificationId = 'fb625a26f09d' const notificationKey = 'my-notification-key' @@ -22,11 +22,11 @@ describe('Notifications Controller', function () { beforeEach(function () { const self = this this.notifications = {} - this.controller = SandboxedModule.require(modulePath, { + this.controller = _require(modulePath, { requires: { './Notifications': this.notifications, '@overleaf/metrics': { - inc: sinon.stub(), + inc: stub(), }, }, }) @@ -42,9 +42,11 @@ describe('Notifications Controller', function () { describe('getUserNotifications', function () { return it('should ask the notifications for the users notifications', function (done) { - this.notifications.getUserNotifications = sinon - .stub() - .callsArgWith(1, null, this.stubbedNotification) + this.notifications.getUserNotifications = stub().callsArgWith( + 1, + null, + this.stubbedNotification + ) const req = { params: { user_id: userId, @@ -64,7 +66,7 @@ describe('Notifications Controller', function () { describe('addNotification', function () { return it('should tell the notifications to add the notification for the user', function (done) { - this.notifications.addNotification = sinon.stub().callsArgWith(2) + this.notifications.addNotification = stub().callsArgWith(2) const req = { params: { user_id: userId, @@ -85,7 +87,7 @@ describe('Notifications Controller', function () { describe('removeNotificationId', function () { return it('should tell the notifications to mark the notification Id as read', function (done) { - this.notifications.removeNotificationId = sinon.stub().callsArgWith(2) + this.notifications.removeNotificationId = stub().callsArgWith(2) const req = { params: { user_id: userId, @@ -106,7 +108,7 @@ describe('Notifications Controller', function () { describe('removeNotificationKey', function () { return it('should tell the notifications to mark the notification Key as read', function (done) { - this.notifications.removeNotificationKey = sinon.stub().callsArgWith(2) + this.notifications.removeNotificationKey = stub().callsArgWith(2) const req = { params: { user_id: userId, @@ -127,9 +129,7 @@ describe('Notifications Controller', function () { return describe('removeNotificationByKeyOnly', function () { return it('should tell the notifications to mark the notification Key as read', function (done) { - this.notifications.removeNotificationByKeyOnly = sinon - .stub() - .callsArgWith(1) + this.notifications.removeNotificationByKeyOnly = stub().callsArgWith(1) const req = { params: { key: notificationKey, diff --git a/services/notifications/test/unit/js/NotificationsTests.js b/services/notifications/test/unit/js/NotificationsTests.js index fb485bb7a4..ca1745f02b 100644 --- a/services/notifications/test/unit/js/NotificationsTests.js +++ b/services/notifications/test/unit/js/NotificationsTests.js @@ -10,24 +10,24 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require('sinon') -const { expect } = require('chai') -const modulePath = '../../../app/js/Notifications.js' -const SandboxedModule = require('sandboxed-module') -const assert = require('node:assert') -const { ObjectId } = require('mongodb-legacy') +import { stub, assert as _assert } from 'sinon' +import { expect } from 'chai' +import { require as _require } from 'sandboxed-module' +import { deepEqual } from 'node:assert' +import { ObjectId } from 'mongodb-legacy' +const modulePath = '../../../app/js/Notifications.js' const userId = '51dc93e6fb625a261300003b' const notificationId = '574ee8d6f40c3a244e704249' const notificationKey = 'notification-key' describe('Notifications Tests', function () { beforeEach(function () { - this.findToArrayStub = sinon.stub() - this.findStub = sinon.stub().returns({ toArray: this.findToArrayStub }) - this.countStub = sinon.stub() - this.updateOneStub = sinon.stub() - this.deleteOneStub = sinon.stub() + this.findToArrayStub = stub() + this.findStub = stub().returns({ toArray: this.findToArrayStub }) + this.countStub = stub() + this.updateOneStub = stub() + this.deleteOneStub = stub() this.db = { notifications: { find: this.findStub, @@ -37,7 +37,7 @@ describe('Notifications Tests', function () { }, } - this.notifications = SandboxedModule.require(modulePath, { + this.notifications = _require(modulePath, { requires: { '@overleaf/settings': {}, './mongodb': { db: this.db, ObjectId }, @@ -61,7 +61,7 @@ describe('Notifications Tests', function () { (err, notifications) => { if (err) return done(err) notifications.should.equal(this.stubbedNotificationArray) - assert.deepEqual(this.findStub.args[0][0], { + deepEqual(this.findStub.args[0][0], { user_id: new ObjectId(userId), templateKey: { $exists: true }, }) @@ -99,7 +99,7 @@ describe('Notifications Tests', function () { this.stubbedNotification, err => { expect(err).not.to.exist - sinon.assert.calledWith( + _assert.calledWith( this.updateOneStub, this.expectedQuery, { $set: this.expectedDocument }, @@ -121,7 +121,7 @@ describe('Notifications Tests', function () { this.stubbedNotification, err => { expect(err).not.to.exist - sinon.assert.notCalled(this.updateOneStub) + _assert.notCalled(this.updateOneStub) return done() } ) @@ -134,7 +134,7 @@ describe('Notifications Tests', function () { this.stubbedNotification, err => { expect(err).not.to.exist - sinon.assert.calledWith( + _assert.calledWith( this.updateOneStub, this.expectedQuery, { $set: this.expectedDocument }, @@ -174,7 +174,7 @@ describe('Notifications Tests', function () { this.stubbedNotification, err => { expect(err).not.to.exist - sinon.assert.calledWith( + _assert.calledWith( this.updateOneStub, this.expectedQuery, { $set: this.expectedDocument }, @@ -210,7 +210,7 @@ describe('Notifications Tests', function () { this.stubbedNotification, err => { ;(err instanceof Error).should.equal(true) - sinon.assert.notCalled(this.updateOneStub) + _assert.notCalled(this.updateOneStub) return done() } ) @@ -234,8 +234,8 @@ describe('Notifications Tests', function () { const updateOperation = { $unset: { templateKey: true, messageOpts: true }, } - assert.deepEqual(this.updateOneStub.args[0][0], searchOps) - assert.deepEqual(this.updateOneStub.args[0][1], updateOperation) + deepEqual(this.updateOneStub.args[0][0], searchOps) + deepEqual(this.updateOneStub.args[0][1], updateOperation) return done() } ) @@ -258,8 +258,8 @@ describe('Notifications Tests', function () { const updateOperation = { $unset: { templateKey: true }, } - assert.deepEqual(this.updateOneStub.args[0][0], searchOps) - assert.deepEqual(this.updateOneStub.args[0][1], updateOperation) + deepEqual(this.updateOneStub.args[0][0], searchOps) + deepEqual(this.updateOneStub.args[0][1], updateOperation) return done() } ) @@ -276,8 +276,8 @@ describe('Notifications Tests', function () { if (err) return done(err) const searchOps = { key: notificationKey } const updateOperation = { $unset: { templateKey: true } } - assert.deepEqual(this.updateOneStub.args[0][0], searchOps) - assert.deepEqual(this.updateOneStub.args[0][1], updateOperation) + deepEqual(this.updateOneStub.args[0][0], searchOps) + deepEqual(this.updateOneStub.args[0][1], updateOperation) return done() } ) @@ -293,7 +293,7 @@ describe('Notifications Tests', function () { err => { if (err) return done(err) const searchOps = { key: notificationKey } - assert.deepEqual(this.deleteOneStub.args[0][0], searchOps) + deepEqual(this.deleteOneStub.args[0][0], searchOps) return done() } ) diff --git a/services/notifications/vitest.config.cjs b/services/notifications/vitest.config.cjs new file mode 100644 index 0000000000..0edcbead49 --- /dev/null +++ b/services/notifications/vitest.config.cjs @@ -0,0 +1,9 @@ +const { defineConfig } = require('vitest/config') + +module.exports = defineConfig({ + test: { + include: ['test/unit/js/**/*.test.js'], + setupFiles: ['./test/setup.js'], + isolate: false, + }, +})