[misc] migrate the app to the native mongo driver

health-check to follow in a separate commit
This commit is contained in:
Jakob Ackermann 2020-08-31 09:58:47 +01:00
parent a4f1124215
commit d77834ab55
6 changed files with 82 additions and 65 deletions

View file

@ -15,6 +15,7 @@ const app = express()
const methodOverride = require('method-override') const methodOverride = require('method-override')
const bodyParser = require('body-parser') const bodyParser = require('body-parser')
const errorHandler = require('errorhandler') const errorHandler = require('errorhandler')
const mongodb = require('./app/js/mongodb')
const controller = require('./app/js/NotificationsController') const controller = require('./app/js/NotificationsController')
metrics.memory.monitor(logger) metrics.memory.monitor(logger)
@ -62,9 +63,18 @@ const port =
Settings.internal != null ? Settings.internal.notifications : undefined, Settings.internal != null ? Settings.internal.notifications : undefined,
(x1) => x1.port (x1) => x1.port
) || 3042 ) || 3042
app.listen(port, host, () =>
logger.info(`notifications starting up, listening on ${host}:${port}`) mongodb
) .waitForDb()
.then(() => {
app.listen(port, host, () =>
logger.info(`notifications starting up, listening on ${host}:${port}`)
)
})
.catch((err) => {
logger.fatal({ err }, 'Cannot connect to mongo. Exiting.')
process.exit(1)
})
function __guard__(value, transform) { function __guard__(value, transform) {
return typeof value !== 'undefined' && value !== null return typeof value !== 'undefined' && value !== null

View file

@ -12,13 +12,8 @@
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
let Notifications let Notifications
const Settings = require('settings-sharelatex')
const logger = require('logger-sharelatex') const logger = require('logger-sharelatex')
const mongojs = require('mongojs') const { db, ObjectId } = require('./mongodb')
const db = mongojs(Settings.mongo != null ? Settings.mongo.url : undefined, [
'notifications'
])
const { ObjectId } = require('mongojs')
const metrics = require('metrics-sharelatex') const metrics = require('metrics-sharelatex')
module.exports = Notifications = { module.exports = Notifications = {
@ -30,9 +25,7 @@ module.exports = Notifications = {
user_id: ObjectId(user_id), user_id: ObjectId(user_id),
templateKey: { $exists: true } templateKey: { $exists: true }
} }
return db.notifications.find(query, (err, notifications) => db.notifications.find(query).toArray(callback)
callback(err, notifications)
)
}, },
_countExistingNotifications(user_id, notification, callback) { _countExistingNotifications(user_id, notification, callback) {
@ -85,7 +78,7 @@ module.exports = Notifications = {
return callback(err) return callback(err)
} }
} }
return db.notifications.update( db.notifications.updateOne(
{ user_id: doc.user_id, key: notification.key }, { user_id: doc.user_id, key: notification.key },
{ $set: doc }, { $set: doc },
{ upsert: true }, { upsert: true },
@ -100,7 +93,7 @@ module.exports = Notifications = {
_id: ObjectId(notification_id) _id: ObjectId(notification_id)
} }
const updateOperation = { $unset: { templateKey: true, messageOpts: true } } const updateOperation = { $unset: { templateKey: true, messageOpts: true } }
return db.notifications.update(searchOps, updateOperation, callback) db.notifications.updateOne(searchOps, updateOperation, callback)
}, },
removeNotificationKey(user_id, notification_key, callback) { removeNotificationKey(user_id, notification_key, callback) {
@ -109,19 +102,19 @@ module.exports = Notifications = {
key: notification_key key: notification_key
} }
const updateOperation = { $unset: { templateKey: true } } const updateOperation = { $unset: { templateKey: true } }
return db.notifications.update(searchOps, updateOperation, callback) db.notifications.updateOne(searchOps, updateOperation, callback)
}, },
removeNotificationByKeyOnly(notification_key, callback) { removeNotificationByKeyOnly(notification_key, callback) {
const searchOps = { key: notification_key } const searchOps = { key: notification_key }
const updateOperation = { $unset: { templateKey: true } } const updateOperation = { $unset: { templateKey: true } }
return db.notifications.update(searchOps, updateOperation, callback) db.notifications.updateOne(searchOps, updateOperation, callback)
}, },
// hard delete of doc, rather than removing the templateKey // hard delete of doc, rather than removing the templateKey
deleteNotificationByKeyOnly(notification_key, callback) { deleteNotificationByKeyOnly(notification_key, callback) {
const searchOps = { key: notification_key } const searchOps = { key: notification_key }
return db.notifications.remove(searchOps, { justOne: true }, callback) db.notifications.deleteOne(searchOps, callback)
} }
} }
;['getUserNotifications', 'addNotification'].map((method) => ;['getUserNotifications', 'addNotification'].map((method) =>

View file

@ -0,0 +1,21 @@
const Settings = require('settings-sharelatex')
const { MongoClient, ObjectId } = require('mongodb')
const clientPromise = MongoClient.connect(Settings.mongo.url)
async function waitForDb() {
await clientPromise
}
const db = {}
waitForDb().then(async function () {
const internalDb = (await clientPromise).db()
db.notifications = internalDb.collection('notifications')
})
module.exports = {
db,
ObjectId,
waitForDb
}

View file

@ -1471,9 +1471,9 @@
"integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ="
}, },
"bl": { "bl": {
"version": "2.2.0", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz", "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
"integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==", "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
"requires": { "requires": {
"readable-stream": "^2.3.5", "readable-stream": "^2.3.5",
"safe-buffer": "^5.1.1" "safe-buffer": "^5.1.1"
@ -1542,9 +1542,9 @@
"dev": true "dev": true
}, },
"bson": { "bson": {
"version": "1.1.4", "version": "1.1.5",
"resolved": "https://registry.npmjs.org/bson/-/bson-1.1.4.tgz", "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz",
"integrity": "sha512-S/yKGU1syOMzO86+dGpg2qGoDL0zvzcb262G+gqEy6TgP6rt6z6qxSFX/8X6vLC91P7G7C3nLs0+bvDzmvBA3Q==" "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg=="
}, },
"buffer-equal-constant-time": { "buffer-equal-constant-time": {
"version": "1.0.1", "version": "1.0.1",
@ -4507,12 +4507,12 @@
"optional": true "optional": true
}, },
"mongodb": { "mongodb": {
"version": "3.5.5", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.5.tgz", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.0.tgz",
"integrity": "sha512-GCjDxR3UOltDq00Zcpzql6dQo1sVry60OXJY3TDmFc2SWFY6c8Gn1Ardidc5jDirvJrx2GC3knGOImKphbSL3A==", "integrity": "sha512-/XWWub1mHZVoqEsUppE0GV7u9kanLvHxho6EvBxQbShXTKYF9trhZC2NzbulRGeG7xMJHD8IOWRcdKx5LPjAjQ==",
"requires": { "requires": {
"bl": "^2.2.0", "bl": "^2.2.0",
"bson": "^1.1.1", "bson": "^1.1.4",
"denque": "^1.4.1", "denque": "^1.4.1",
"require_optional": "^1.0.1", "require_optional": "^1.0.1",
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",

View file

@ -25,6 +25,7 @@
"logger-sharelatex": "^2.2.0", "logger-sharelatex": "^2.2.0",
"method-override": "^3.0.0", "method-override": "^3.0.0",
"metrics-sharelatex": "^2.6.2", "metrics-sharelatex": "^2.6.2",
"mongodb": "^3.6.0",
"mongojs": "^3.1.0", "mongojs": "^3.1.0",
"node-statsd": "0.1.1", "node-statsd": "0.1.1",
"request": "^2.88.2", "request": "^2.88.2",

View file

@ -19,7 +19,7 @@ const should = chai.should()
const modulePath = '../../../app/js/Notifications.js' const modulePath = '../../../app/js/Notifications.js'
const SandboxedModule = require('sandboxed-module') const SandboxedModule = require('sandboxed-module')
const assert = require('assert') const assert = require('assert')
const { ObjectId } = require('mongojs') const { ObjectId } = require('mongodb')
const user_id = '51dc93e6fb625a261300003b' const user_id = '51dc93e6fb625a261300003b'
const notification_id = 'fb625a26f09d' const notification_id = 'fb625a26f09d'
@ -27,25 +27,19 @@ const notification_key = 'notification-key'
describe('Notifications Tests', function () { describe('Notifications Tests', function () {
beforeEach(function () { beforeEach(function () {
const self = this this.findToArrayStub = sinon.stub()
this.findStub = sinon.stub() this.findStub = sinon.stub().returns({ toArray: this.findToArrayStub })
this.insertStub = sinon.stub()
this.countStub = sinon.stub() this.countStub = sinon.stub()
this.updateStub = sinon.stub() this.updateOneStub = sinon.stub()
this.removeStub = sinon.stub() this.deleteOneStub = sinon.stub()
this.mongojs = () => { this.db = {
return { notifications: {
notifications: { find: this.findStub,
update: self.mongojsUpdate, count: this.countStub,
find: this.findStub, updateOne: this.updateOneStub,
insert: this.insertStub, deleteOne: this.deleteOneStub
count: this.countStub,
update: this.updateStub,
remove: this.removeStub
}
} }
} }
this.mongojs.ObjectId = ObjectId
this.notifications = SandboxedModule.require(modulePath, { this.notifications = SandboxedModule.require(modulePath, {
requires: { requires: {
@ -54,7 +48,7 @@ describe('Notifications Tests', function () {
error() {} error() {}
}, },
'settings-sharelatex': {}, 'settings-sharelatex': {},
mongojs: this.mongojs, './mongodb': { db: this.db, ObjectId },
'metrics-sharelatex': { timeAsyncMethod: sinon.stub() } 'metrics-sharelatex': { timeAsyncMethod: sinon.stub() }
}, },
globals: { globals: {
@ -73,7 +67,7 @@ describe('Notifications Tests', function () {
describe('getUserNotifications', function () { describe('getUserNotifications', function () {
return it('should find all notifications and return i', function (done) { return it('should find all notifications and return i', function (done) {
this.findStub.callsArgWith(1, null, this.stubbedNotificationArray) this.findToArrayStub.callsArgWith(0, null, this.stubbedNotificationArray)
return this.notifications.getUserNotifications( return this.notifications.getUserNotifications(
user_id, user_id,
(err, notifications) => { (err, notifications) => {
@ -106,7 +100,7 @@ describe('Notifications Tests', function () {
user_id: this.stubbedNotification.user_id, user_id: this.stubbedNotification.user_id,
key: 'notification-key' key: 'notification-key'
} }
this.updateStub.yields() this.updateOneStub.yields()
return this.countStub.yields(null, 0) return this.countStub.yields(null, 0)
}) })
@ -117,7 +111,7 @@ describe('Notifications Tests', function () {
(err) => { (err) => {
expect(err).not.exists expect(err).not.exists
sinon.assert.calledWith( sinon.assert.calledWith(
this.updateStub, this.updateOneStub,
this.expectedQuery, this.expectedQuery,
{ $set: this.expectedDocument }, { $set: this.expectedDocument },
{ upsert: true } { upsert: true }
@ -138,7 +132,7 @@ describe('Notifications Tests', function () {
this.stubbedNotification, this.stubbedNotification,
(err) => { (err) => {
expect(err).not.exists expect(err).not.exists
sinon.assert.notCalled(this.updateStub) sinon.assert.notCalled(this.updateOneStub)
return done() return done()
} }
) )
@ -152,7 +146,7 @@ describe('Notifications Tests', function () {
(err) => { (err) => {
expect(err).not.exists expect(err).not.exists
sinon.assert.calledWith( sinon.assert.calledWith(
this.updateStub, this.updateOneStub,
this.expectedQuery, this.expectedQuery,
{ $set: this.expectedDocument }, { $set: this.expectedDocument },
{ upsert: true } { upsert: true }
@ -192,7 +186,7 @@ describe('Notifications Tests', function () {
(err) => { (err) => {
expect(err).not.exists expect(err).not.exists
sinon.assert.calledWith( sinon.assert.calledWith(
this.updateStub, this.updateOneStub,
this.expectedQuery, this.expectedQuery,
{ $set: this.expectedDocument }, { $set: this.expectedDocument },
{ upsert: true } { upsert: true }
@ -227,7 +221,7 @@ describe('Notifications Tests', function () {
this.stubbedNotification, this.stubbedNotification,
(err) => { (err) => {
;(err instanceof Error).should.equal(true) ;(err instanceof Error).should.equal(true)
sinon.assert.notCalled(this.updateStub) sinon.assert.notCalled(this.updateOneStub)
return done() return done()
} }
) )
@ -237,7 +231,7 @@ describe('Notifications Tests', function () {
describe('removeNotificationId', function () { describe('removeNotificationId', function () {
return it('should mark the notification id as read', function (done) { return it('should mark the notification id as read', function (done) {
this.updateStub.callsArgWith(2, null) this.updateOneStub.callsArgWith(2, null)
return this.notifications.removeNotificationId( return this.notifications.removeNotificationId(
user_id, user_id,
@ -250,8 +244,8 @@ describe('Notifications Tests', function () {
const updateOperation = { const updateOperation = {
$unset: { templateKey: true, messageOpts: true } $unset: { templateKey: true, messageOpts: true }
} }
assert.deepEqual(this.updateStub.args[0][0], searchOps) assert.deepEqual(this.updateOneStub.args[0][0], searchOps)
assert.deepEqual(this.updateStub.args[0][1], updateOperation) assert.deepEqual(this.updateOneStub.args[0][1], updateOperation)
return done() return done()
} }
) )
@ -260,7 +254,7 @@ describe('Notifications Tests', function () {
describe('removeNotificationKey', function () { describe('removeNotificationKey', function () {
return it('should mark the notification key as read', function (done) { return it('should mark the notification key as read', function (done) {
this.updateStub.callsArgWith(2, null) this.updateOneStub.callsArgWith(2, null)
return this.notifications.removeNotificationKey( return this.notifications.removeNotificationKey(
user_id, user_id,
@ -273,8 +267,8 @@ describe('Notifications Tests', function () {
const updateOperation = { const updateOperation = {
$unset: { templateKey: true } $unset: { templateKey: true }
} }
assert.deepEqual(this.updateStub.args[0][0], searchOps) assert.deepEqual(this.updateOneStub.args[0][0], searchOps)
assert.deepEqual(this.updateStub.args[0][1], updateOperation) assert.deepEqual(this.updateOneStub.args[0][1], updateOperation)
return done() return done()
} }
) )
@ -283,15 +277,15 @@ describe('Notifications Tests', function () {
describe('removeNotificationByKeyOnly', function () { describe('removeNotificationByKeyOnly', function () {
return it('should mark the notification key as read', function (done) { return it('should mark the notification key as read', function (done) {
this.updateStub.callsArgWith(2, null) this.updateOneStub.callsArgWith(2, null)
return this.notifications.removeNotificationByKeyOnly( return this.notifications.removeNotificationByKeyOnly(
notification_key, notification_key,
(err) => { (err) => {
const searchOps = { key: notification_key } const searchOps = { key: notification_key }
const updateOperation = { $unset: { templateKey: true } } const updateOperation = { $unset: { templateKey: true } }
assert.deepEqual(this.updateStub.args[0][0], searchOps) assert.deepEqual(this.updateOneStub.args[0][0], searchOps)
assert.deepEqual(this.updateStub.args[0][1], updateOperation) assert.deepEqual(this.updateOneStub.args[0][1], updateOperation)
return done() return done()
} }
) )
@ -300,15 +294,13 @@ describe('Notifications Tests', function () {
return describe('deleteNotificationByKeyOnly', function () { return describe('deleteNotificationByKeyOnly', function () {
return it('should completely remove the notification', function (done) { return it('should completely remove the notification', function (done) {
this.removeStub.callsArgWith(2, null) this.deleteOneStub.callsArgWith(1, null)
return this.notifications.deleteNotificationByKeyOnly( return this.notifications.deleteNotificationByKeyOnly(
notification_key, notification_key,
(err) => { (err) => {
const searchOps = { key: notification_key } const searchOps = { key: notification_key }
const opts = { justOne: true } assert.deepEqual(this.deleteOneStub.args[0][0], searchOps)
assert.deepEqual(this.removeStub.args[0][0], searchOps)
assert.deepEqual(this.removeStub.args[0][1], opts)
return done() return done()
} }
) )