[migrations] add migration for back filling db.users.analyticsId (#33115)

* [migrations] add migration for back filling db.users.analyticsId

Co-authored-by: Davinder Singh <davinder.singh@overleaf.com>

* [web] add acceptance test for backfilling db.users.analyticsId

---------

Co-authored-by: Davinder Singh <davinder.singh@overleaf.com>
GitOrigin-RevId: a0840969ac0c4c84e874c6f00cf0a78857a4bb06
This commit is contained in:
Jakob Ackermann
2026-04-30 08:31:42 +02:00
committed by Copybot
parent 4e138e6f99
commit 50abfe8f0c
2 changed files with 123 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
import { expect } from 'chai'
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.mjs'
import { exec } from 'node:child_process'
describe('BackFillUsersAnalyticsId', function () {
beforeEach('insert data', async function () {
await db.users.insertMany([
{
_id: new ObjectId('50e434d90000000000000000'),
email: 'foo0@bar.com',
},
{
_id: new ObjectId('50e434d90000000000000001'),
email: 'foo1@bar.com',
},
{
_id: new ObjectId('50e434d90000000000000002'),
analyticsId: '32dc5866-1451-4209-ba9d-23e96caafecd',
email: 'foo2@bar.com',
},
{
_id: new ObjectId('50e434d90000000000000003'),
analyticsId: '32dc5866-1451-4209-ba9d-23e96caafecf',
email: 'foo3@bar.com',
},
])
})
beforeEach('run migration', function (done) {
exec(
'cd ../../tools/migrations && east migrate -t saas --force 20260424120000_back_fill_users_analyticsId',
done
)
})
it('should update the users', async function () {
expect(
await db.users
.find({}, { projection: { _id: 1, email: 1, analyticsId: 1 } })
.toArray()
).to.deep.equal([
{
_id: new ObjectId('50e434d90000000000000000'),
analyticsId: '50e434d90000000000000000',
email: 'foo0@bar.com',
},
{
_id: new ObjectId('50e434d90000000000000001'),
analyticsId: '50e434d90000000000000001',
email: 'foo1@bar.com',
},
{
_id: new ObjectId('50e434d90000000000000002'),
analyticsId: '32dc5866-1451-4209-ba9d-23e96caafecd',
email: 'foo2@bar.com',
},
{
_id: new ObjectId('50e434d90000000000000003'),
analyticsId: '32dc5866-1451-4209-ba9d-23e96caafecf',
email: 'foo3@bar.com',
},
])
})
describe('after rolling back', function () {
beforeEach(function (done) {
exec(
'cd ../../tools/migrations && east rollback -t saas --force 20260424120000_back_fill_users_analyticsId',
done
)
})
it('should update the users and roll back', async function () {
expect(
await db.users
.find({}, { projection: { _id: 1, email: 1, analyticsId: 1 } })
.toArray()
).to.deep.equal([
{
_id: new ObjectId('50e434d90000000000000000'),
email: 'foo0@bar.com',
},
{
_id: new ObjectId('50e434d90000000000000001'),
email: 'foo1@bar.com',
},
{
_id: new ObjectId('50e434d90000000000000002'),
analyticsId: '32dc5866-1451-4209-ba9d-23e96caafecd',
email: 'foo2@bar.com',
},
{
_id: new ObjectId('50e434d90000000000000003'),
analyticsId: '32dc5866-1451-4209-ba9d-23e96caafecf',
email: 'foo3@bar.com',
},
])
})
})
})

View File

@@ -0,0 +1,24 @@
import { db } from './lib/mongodb.mjs'
import { batchedUpdate } from '@overleaf/mongo-utils/batchedUpdate.js'
const tags = ['saas', 'server-ce', 'server-pro']
const migrate = async () => {
await batchedUpdate(db.users, { analyticsId: { $exists: false } }, [
{ $set: { analyticsId: { $toString: '$_id' } } },
])
}
const rollback = async () => {
await batchedUpdate(
db.users,
{ $expr: { $eq: [{ $strLenCP: '$analyticsId' }, 24] } },
{ $unset: { analyticsId: 1 } }
)
}
export default {
tags,
migrate,
rollback,
}