convert_track_changes_to_explicit_format.mjs 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. // @ts-check
  2. import { db } from '../app/src/infrastructure/mongodb.mjs'
  3. import { batchedUpdate } from '@overleaf/mongo-utils/batchedUpdate.js'
  4. import { scriptRunner } from './lib/ScriptRunner.mjs'
  5. import CollaboratorsHandler from '../app/src/Features/Collaborators/CollaboratorsHandler.mjs'
  6. const DRY_RUN = !process.argv.includes('--dry-run=false')
  7. const DEBUG = process.argv.includes('--debug=true')
  8. // Deployment procedure:
  9. // Run it locally (not dry run)
  10. // Run it on staging (dry run then real and then real). Maybe leave it a few days but might not get good feedback
  11. // Run on prod on a small number of projects to start with, then on all projects (using BATCH_RANGE_START and BATCH_RANGE_END env vars)
  12. // Are there race conditions here? If someone is editing in parallel. Is it worth doing atomic queries?
  13. /**
  14. * @typedef {Object} Project
  15. * @property {any} _id
  16. * @property {Object} track_changes
  17. */
  18. /**
  19. * @param {(progress: string) => Promise<void>} trackProgress
  20. * @returns {Promise<void>}
  21. * @async
  22. */
  23. async function main(trackProgress) {
  24. let projectsProcessed = 0
  25. await batchedUpdate(
  26. db.projects,
  27. {},
  28. /**
  29. * @param {Array<Project>} projects
  30. * @return {Promise<void>}
  31. */
  32. async function projects(projects) {
  33. for (const project of projects) {
  34. projectsProcessed += 1
  35. if (projectsProcessed % 100000 === 0) {
  36. console.log(projectsProcessed, 'projects processed')
  37. }
  38. await processProject(project)
  39. }
  40. },
  41. { _id: 1, track_changes: 1 },
  42. undefined,
  43. { trackProgress }
  44. )
  45. }
  46. async function processProject(project) {
  47. if (DEBUG) {
  48. console.log(
  49. `Processing project ${project._id} with track_changes: ${JSON.stringify(
  50. project.track_changes
  51. )}`
  52. )
  53. }
  54. if (typeof project.track_changes === 'object') {
  55. if (DEBUG) {
  56. console.log(
  57. `Skipping project ${project._id} as it is already in the new format`
  58. )
  59. }
  60. return
  61. }
  62. const newTrackChangesState =
  63. await CollaboratorsHandler.promises.convertTrackChangesToExplicitFormat(
  64. project._id,
  65. project.track_changes
  66. )
  67. if (DEBUG) {
  68. console.log(
  69. `Processed project ${project._id} to have new track_changes: ${JSON.stringify(
  70. newTrackChangesState
  71. )}`
  72. )
  73. }
  74. if (!DRY_RUN) {
  75. await db.projects.updateOne(
  76. { _id: project._id },
  77. { $set: { track_changes: newTrackChangesState } }
  78. )
  79. console.log(
  80. `Updated project ${project._id} track_changes from ${JSON.stringify(project.track_changes)} to ${JSON.stringify(newTrackChangesState)}`
  81. )
  82. } else {
  83. console.log(
  84. `Dry run - would have updated project ${project._id} track_changes from ${JSON.stringify(project.track_changes)} to ${JSON.stringify(newTrackChangesState)}`
  85. )
  86. }
  87. }
  88. try {
  89. await scriptRunner(main)
  90. process.exit(0)
  91. } catch (error) {
  92. console.error(error)
  93. process.exit(1)
  94. }