From d4551dc7cedf935ff08bbc566fac04e47264064b Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Mon, 14 Nov 2022 17:27:10 +0000 Subject: [PATCH] Merge pull request #10442 from overleaf/jpa-convert-archived-trashed [web] add migration for convert_archived_state script GitOrigin-RevId: aeea3601a0c5f96e978c3f2a85458687d6d6678e --- ...1111111111_ce_sp_convert_archived_state.js | 9 ++ .../web/scripts/convert_archived_state.js | 99 +++++++++++-------- .../acceptance/src/ConvertArchivedState.js | 89 ++++++++++++++++- 3 files changed, 153 insertions(+), 44 deletions(-) create mode 100644 services/web/migrations/20221111111111_ce_sp_convert_archived_state.js diff --git a/services/web/migrations/20221111111111_ce_sp_convert_archived_state.js b/services/web/migrations/20221111111111_ce_sp_convert_archived_state.js new file mode 100644 index 0000000000..d48d96e29c --- /dev/null +++ b/services/web/migrations/20221111111111_ce_sp_convert_archived_state.js @@ -0,0 +1,9 @@ +const runScript = require('../scripts/convert_archived_state') + +exports.tags = ['server-ce', 'server-pro'] + +exports.migrate = async () => { + await runScript('FIRST,SECOND') +} + +exports.rollback = async () => {} diff --git a/services/web/scripts/convert_archived_state.js b/services/web/scripts/convert_archived_state.js index c92e0ad015..d83ee767b6 100644 --- a/services/web/scripts/convert_archived_state.js +++ b/services/web/scripts/convert_archived_state.js @@ -6,62 +6,77 @@ const { batchedUpdate } = require('./helpers/batchedUpdate') const { promiseMapWithLimit } = require('../app/src/util/promises') // $ node scripts/convert_archived_state.js FIRST,SECOND -const STAGE = process.argv.pop() -async function main() { - if (STAGE.includes('FIRST')) { - await batchedUpdate( - 'projects', - { archived: false }, - { - $set: { archived: [] }, - } - ) +async function main(STAGE) { + for (const FIELD of ['archived', 'trashed']) { + if (STAGE.includes('FIRST')) { + await batchedUpdate( + 'projects', + { [FIELD]: false }, + { + $set: { [FIELD]: [] }, + } + ) - console.error('Done, with first part') + console.error('Done, with first part for field:', FIELD) + } + + if (STAGE.includes('SECOND')) { + await batchedUpdate( + 'projects', + { [FIELD]: true }, + async function performUpdate(collection, nextBatch) { + await promiseMapWithLimit( + WRITE_CONCURRENCY, + nextBatch, + async project => { + try { + await upgradeFieldToArray({ collection, project, FIELD }) + } catch (err) { + console.error(project._id, err) + throw err + } + } + ) + }, + { + _id: 1, + owner_ref: 1, + collaberator_refs: 1, + readOnly_refs: 1, + tokenAccessReadAndWrite_refs: 1, + tokenAccessReadOnly_refs: 1, + } + ) + + console.error('Done, with second part for field:', FIELD) + } } +} - if (STAGE.includes('SECOND')) { - await batchedUpdate('projects', { archived: true }, performUpdate, { - _id: 1, - owner_ref: 1, - collaberator_refs: 1, - readOnly_refs: 1, - tokenAccessReadAndWrite_refs: 1, - tokenAccessReadOnly_refs: 1, +module.exports = main + +if (require.main === module) { + main(process.argv.pop()) + .then(() => { + process.exit(0) + }) + .catch(error => { + console.error({ error }) + process.exit(1) }) - - console.error('Done, with second part') - } } -main() - .then(() => { - process.exit(0) - }) - .catch(error => { - console.error({ error }) - process.exit(1) - }) - -async function performUpdate(collection, nextBatch) { - await promiseMapWithLimit(WRITE_CONCURRENCY, nextBatch, project => - setArchived(collection, project) - ) -} - -async function setArchived(collection, project) { - const archived = calculateArchivedArray(project) - +async function upgradeFieldToArray({ collection, project, FIELD }) { return collection.updateOne( { _id: project._id }, { - $set: { archived }, + $set: { [FIELD]: getAllUserIds(project) }, } ) } -function calculateArchivedArray(project) { +function getAllUserIds(project) { return _.unionWith( [project.owner_ref], project.collaberator_refs, diff --git a/services/web/test/acceptance/src/ConvertArchivedState.js b/services/web/test/acceptance/src/ConvertArchivedState.js index 11b5e60353..c2926f093d 100644 --- a/services/web/test/acceptance/src/ConvertArchivedState.js +++ b/services/web/test/acceptance/src/ConvertArchivedState.js @@ -10,6 +10,10 @@ describe('ConvertArchivedState', function () { let projectTwo, projectTwoId let projectThree, projectThreeId let projectFour, projectFourId + let projectIdTrashed + let projectIdNotTrashed + let projectIdArchivedAndTrashed + let projectIdNotArchivedNotTrashed beforeEach(async function () { userOne = new User() @@ -64,15 +68,60 @@ describe('ConvertArchivedState', function () { projectFour.archived = false await userOne.saveProject(projectFour) + + projectIdTrashed = await userOne.createProject('trashed', { + template: 'blank', + }) + { + const p = await userOne.getProject(projectIdTrashed) + p.trashed = true + p.collaberator_refs.push(userTwo._id) + await userOne.saveProject(p) + } + + projectIdNotTrashed = await userOne.createProject('not-trashed', { + template: 'blank', + }) + { + const p = await userOne.getProject(projectIdNotTrashed) + p.trashed = false + p.collaberator_refs.push(userTwo._id) + await userOne.saveProject(p) + } + + projectIdArchivedAndTrashed = await userOne.createProject('not-trashed', { + template: 'blank', + }) + { + const p = await userOne.getProject(projectIdArchivedAndTrashed) + p.archived = true + p.trashed = true + p.collaberator_refs.push(userTwo._id) + await userOne.saveProject(p) + } + + projectIdNotArchivedNotTrashed = await userOne.createProject( + 'not-archived,not-trashed', + { + template: 'blank', + } + ) + { + const p = await userOne.getProject(projectIdNotArchivedNotTrashed) + p.archived = false + p.trashed = false + p.collaberator_refs.push(userTwo._id) + await userOne.saveProject(p) + } }) beforeEach(function (done) { exec( 'CONNECT_DELAY=1 node scripts/convert_archived_state.js FIRST,SECOND', (error, stdout, stderr) => { - console.log(stdout) - console.error(stderr) if (error) { + console.log(stdout) + console.error(stderr) return done(error) } done() @@ -95,6 +144,7 @@ describe('ConvertArchivedState', function () { userThree._id, userFour._id, ]) + expect(projectTwo.trashed).to.deep.equal([]) }) it('should not change the value of a project already archived with an array', async function () { @@ -104,11 +154,46 @@ describe('ConvertArchivedState', function () { userTwo._id, userFour._id, ]) + expect(projectThree.trashed).to.deep.equal([]) }) it('should change a none-archived project with a boolean value to an array', async function () { projectFour = await userOne.getProject(projectFourId) expect(convertObjectIdsToStrings(projectFour.archived)).to.deep.equal([]) + expect(projectFour.trashed).to.deep.equal([]) + }) + + it('should change a archived and trashed project with a boolean value to an array', async function () { + const p = await userOne.getProject(projectIdArchivedAndTrashed) + expect(convertObjectIdsToStrings(p.archived)).to.deep.equal([ + userOne._id, + userTwo._id, + ]) + expect(convertObjectIdsToStrings(p.trashed)).to.deep.equal([ + userOne._id, + userTwo._id, + ]) + }) + + it('should change a trashed project with a boolean value to an array', async function () { + const p = await userOne.getProject(projectIdTrashed) + expect(p.archived).to.not.exist + expect(convertObjectIdsToStrings(p.trashed)).to.deep.equal([ + userOne._id, + userTwo._id, + ]) + }) + + it('should change a not-trashed project with a boolean value to an array', async function () { + const p = await userOne.getProject(projectIdNotTrashed) + expect(p.archived).to.not.exist + expect(convertObjectIdsToStrings(p.trashed)).to.deep.equal([]) + }) + + it('should change a not-archived/not-trashed project with a boolean value to an array', async function () { + const p = await userOne.getProject(projectIdNotArchivedNotTrashed) + expect(p.archived).to.deep.equal([]) + expect(p.trashed).to.deep.equal([]) }) })