From 8b2f8ce2431568ee4554d158ef8051093d93e755 Mon Sep 17 00:00:00 2001 From: Eric Mc Sween Date: Thu, 25 Aug 2022 08:01:39 -0400 Subject: [PATCH] Merge pull request #9383 from overleaf/em-file-tree-histories Track source for all file tree operations GitOrigin-RevId: ff95ea8e99bfa30203a2a42968519bbaba65e708 --- .../document-updater/app/js/HttpController.js | 3 +- .../app/js/ProjectHistoryRedisManager.js | 98 +-- .../document-updater/app/js/ProjectManager.js | 6 + .../js/HttpController/HttpControllerTests.js | 5 +- .../ProjectHistoryRedisManagerTests.js | 85 ++- .../js/ProjectManager/updateProjectTests.js | 25 +- .../DocumentUpdater/DocumentUpdaterHandler.js | 2 + .../src/Features/Editor/EditorController.js | 568 ++++++++---------- .../Features/Editor/EditorHttpController.js | 9 +- .../Project/ProjectCreationHandler.js | 9 +- .../src/Features/Project/ProjectDuplicator.js | 3 +- .../Project/ProjectEntityUpdateHandler.js | 91 ++- .../Features/Uploads/ProjectUploadManager.js | 3 +- services/web/scripts/convert_doc_to_file.js | 3 +- services/web/scripts/create_project.js | 9 +- .../scripts/history/HistoryUpgradeHelper.js | 3 +- .../web/scripts/recover_docs_from_redis.js | 3 +- .../DocumentUpdaterHandlerTests.js | 17 +- .../unit/src/Editor/EditorControllerTests.js | 32 +- .../src/Editor/EditorHttpControllerTests.js | 15 +- .../src/Project/ProjectDuplicatorTests.js | 3 +- .../ProjectEntityRestoreHandlerTests.js | 1 + .../ProjectEntityUpdateHandlerTests.js | 194 ++++-- .../src/Uploads/ProjectUploadManagerTests.js | 6 +- 24 files changed, 666 insertions(+), 527 deletions(-) diff --git a/services/document-updater/app/js/HttpController.js b/services/document-updater/app/js/HttpController.js index 78b728dfe5..dbe0e8ba67 100644 --- a/services/document-updater/app/js/HttpController.js +++ b/services/document-updater/app/js/HttpController.js @@ -322,7 +322,7 @@ function deleteComment(req, res, next) { function updateProject(req, res, next) { const timer = new Metrics.Timer('http.updateProject') const projectId = req.params.project_id - const { projectHistoryId, userId, updates = [], version } = req.body + const { projectHistoryId, userId, updates = [], version, source } = req.body logger.debug({ projectId, updates, version }, 'updating project via http') ProjectManager.updateProjectWithLocks( projectId, @@ -330,6 +330,7 @@ function updateProject(req, res, next) { userId, updates, version, + source, error => { timer.done() if (error) { diff --git a/services/document-updater/app/js/ProjectHistoryRedisManager.js b/services/document-updater/app/js/ProjectHistoryRedisManager.js index 1d1ed3eb50..3b160079e8 100644 --- a/services/document-updater/app/js/ProjectHistoryRedisManager.js +++ b/services/document-updater/app/js/ProjectHistoryRedisManager.js @@ -1,16 +1,3 @@ -/* eslint-disable - camelcase, -*/ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS201: Simplify complex destructure assignments - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ let ProjectHistoryRedisManager const Settings = require('@overleaf/settings') const projectHistoryKeys = Settings.redis?.project_history?.key_schema @@ -22,106 +9,119 @@ const metrics = require('./Metrics') const { docIsTooLarge } = require('./Limits') module.exports = ProjectHistoryRedisManager = { - queueOps(project_id, ...rest) { + queueOps(projectId, ...rest) { // Record metric for ops pushed onto queue const callback = rest.pop() const ops = rest - for (const op of Array.from(ops)) { + for (const op of ops) { metrics.summary('redis.projectHistoryOps', op.length, { status: 'push' }) } const multi = rclient.multi() // Push the ops onto the project history queue multi.rpush( - projectHistoryKeys.projectHistoryOps({ project_id }), - ...Array.from(ops) + projectHistoryKeys.projectHistoryOps({ project_id: projectId }), + ...ops ) // To record the age of the oldest op on the queue set a timestamp if not // already present (SETNX). multi.setnx( - projectHistoryKeys.projectHistoryFirstOpTimestamp({ project_id }), + projectHistoryKeys.projectHistoryFirstOpTimestamp({ + project_id: projectId, + }), Date.now() ) - return multi.exec(function (error, result) { - if (error != null) { + multi.exec(function (error, result) { + if (error) { return callback(error) } // return the number of entries pushed onto the project history queue - return callback(null, result[0]) + callback(null, result[0]) }) }, queueRenameEntity( - project_id, + projectId, projectHistoryId, - entity_type, - entity_id, - user_id, + entityType, + entityId, + userId, projectUpdate, + source, callback ) { projectUpdate = { pathname: projectUpdate.pathname, new_pathname: projectUpdate.newPathname, meta: { - user_id, + user_id: userId, ts: new Date(), }, version: projectUpdate.version, projectHistoryId, } - projectUpdate[entity_type] = entity_id + projectUpdate[entityType] = entityId + if (source != null) { + projectUpdate.meta.source = source + if (source !== 'editor') { + projectUpdate.meta.type = 'external' + } + } logger.debug( - { project_id, projectUpdate }, + { projectId, projectUpdate }, 'queue rename operation to project-history' ) const jsonUpdate = JSON.stringify(projectUpdate) - return ProjectHistoryRedisManager.queueOps(project_id, jsonUpdate, callback) + ProjectHistoryRedisManager.queueOps(projectId, jsonUpdate, callback) }, queueAddEntity( - project_id, + projectId, projectHistoryId, - entity_type, - entitiy_id, - user_id, + entityType, + entityId, + userId, projectUpdate, + source, callback ) { - if (callback == null) { - callback = function () {} - } projectUpdate = { pathname: projectUpdate.pathname, docLines: projectUpdate.docLines, url: projectUpdate.url, meta: { - user_id, + user_id: userId, ts: new Date(), }, version: projectUpdate.version, projectHistoryId, } - projectUpdate[entity_type] = entitiy_id + projectUpdate[entityType] = entityId + if (source != null) { + projectUpdate.meta.source = source + if (source !== 'editor') { + projectUpdate.meta.type = 'external' + } + } logger.debug( - { project_id, projectUpdate }, + { projectId, projectUpdate }, 'queue add operation to project-history' ) const jsonUpdate = JSON.stringify(projectUpdate) - return ProjectHistoryRedisManager.queueOps(project_id, jsonUpdate, callback) + ProjectHistoryRedisManager.queueOps(projectId, jsonUpdate, callback) }, queueResyncProjectStructure( - project_id, + projectId, projectHistoryId, docs, files, callback ) { - logger.debug({ project_id, docs, files }, 'queue project structure resync') + logger.debug({ projectId, docs, files }, 'queue project structure resync') const projectUpdate = { resyncProjectStructure: { docs, files }, projectHistoryId, @@ -130,20 +130,20 @@ module.exports = ProjectHistoryRedisManager = { }, } const jsonUpdate = JSON.stringify(projectUpdate) - return ProjectHistoryRedisManager.queueOps(project_id, jsonUpdate, callback) + ProjectHistoryRedisManager.queueOps(projectId, jsonUpdate, callback) }, queueResyncDocContent( - project_id, + projectId, projectHistoryId, - doc_id, + docId, lines, version, pathname, callback ) { logger.debug( - { project_id, doc_id, lines, version, pathname }, + { projectId, docId, lines, version, pathname }, 'queue doc content resync' ) const projectUpdate = { @@ -153,7 +153,7 @@ module.exports = ProjectHistoryRedisManager = { }, projectHistoryId, path: pathname, - doc: doc_id, + doc: docId, meta: { ts: new Date(), }, @@ -166,9 +166,9 @@ module.exports = ProjectHistoryRedisManager = { const err = new Error( 'blocking resync doc content insert into project history queue: doc is too large' ) - logger.error({ project_id, doc_id, err, docSize: sizeBound }, err.message) + logger.error({ projectId, docId, err, docSize: sizeBound }, err.message) return callback(err) } - return ProjectHistoryRedisManager.queueOps(project_id, jsonUpdate, callback) + ProjectHistoryRedisManager.queueOps(projectId, jsonUpdate, callback) }, } diff --git a/services/document-updater/app/js/ProjectManager.js b/services/document-updater/app/js/ProjectManager.js index aad6077371..31e28f5943 100644 --- a/services/document-updater/app/js/ProjectManager.js +++ b/services/document-updater/app/js/ProjectManager.js @@ -209,6 +209,7 @@ function updateProjectWithLocks( userId, updates, projectVersion, + source, _callback ) { const timer = new Metrics.Timer('projectManager.updateProject') @@ -231,6 +232,7 @@ function updateProjectWithLocks( update.id, userId, update, + source, (error, count) => { projectOpsLength = count cb(error) @@ -248,6 +250,7 @@ function updateProjectWithLocks( update.id, userId, update, + source, (error, count) => { projectOpsLength = count cb(error) @@ -272,6 +275,7 @@ function updateProjectWithLocks( update.id, userId, update, + source, (error, count) => { projectOpsLength = count cb(error) @@ -289,6 +293,7 @@ function updateProjectWithLocks( update.id, userId, update, + source, (error, count) => { projectOpsLength = count cb(error) @@ -303,6 +308,7 @@ function updateProjectWithLocks( update.id, userId, update, + source, (error, count) => { projectOpsLength = count cb(error) diff --git a/services/document-updater/test/unit/js/HttpController/HttpControllerTests.js b/services/document-updater/test/unit/js/HttpController/HttpControllerTests.js index d8a2905984..80c3826c8e 100644 --- a/services/document-updater/test/unit/js/HttpController/HttpControllerTests.js +++ b/services/document-updater/test/unit/js/HttpController/HttpControllerTests.js @@ -24,6 +24,7 @@ describe('HttpController', function () { this.project_id = 'project-id-123' this.doc_id = 'doc-id-123' + this.source = 'editor' this.next = sinon.stub() this.res = { send: sinon.stub(), @@ -837,6 +838,7 @@ describe('HttpController', function () { userId: this.userId, updates: this.updates, version: this.version, + source: this.source, }, params: { project_id: this.project_id, @@ -857,7 +859,8 @@ describe('HttpController', function () { this.projectHistoryId, this.userId, this.updates, - this.version + this.version, + this.source ) .should.equal(true) }) diff --git a/services/document-updater/test/unit/js/ProjectHistoryRedisManager/ProjectHistoryRedisManagerTests.js b/services/document-updater/test/unit/js/ProjectHistoryRedisManager/ProjectHistoryRedisManagerTests.js index db27cf4d4e..0cb4fd9a79 100644 --- a/services/document-updater/test/unit/js/ProjectHistoryRedisManager/ProjectHistoryRedisManagerTests.js +++ b/services/document-updater/test/unit/js/ProjectHistoryRedisManager/ProjectHistoryRedisManagerTests.js @@ -1,16 +1,3 @@ -/* eslint-disable - camelcase, - no-return-assign, - no-unused-vars, -*/ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * 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/ProjectHistoryRedisManager.js' const SandboxedModule = require('sandboxed-module') @@ -23,43 +10,41 @@ describe('ProjectHistoryRedisManager', function () { this.user_id = 'user-id-123' this.callback = sinon.stub() this.rclient = {} + this.source = 'editor' tk.freeze(new Date()) this.Limits = { docIsTooLarge: sinon.stub().returns(false), } - return (this.ProjectHistoryRedisManager = SandboxedModule.require( - modulePath, - { - requires: { - '@overleaf/settings': (this.settings = { - max_doc_length: 123, - redis: { - project_history: { - key_schema: { - projectHistoryOps({ project_id }) { - return `ProjectHistory:Ops:${project_id}` - }, - projectHistoryFirstOpTimestamp({ project_id }) { - return `ProjectHistory:FirstOpTimestamp:${project_id}` - }, + this.ProjectHistoryRedisManager = SandboxedModule.require(modulePath, { + requires: { + '@overleaf/settings': (this.settings = { + max_doc_length: 123, + redis: { + project_history: { + key_schema: { + projectHistoryOps({ project_id: projectId }) { + return `ProjectHistory:Ops:${projectId}` + }, + projectHistoryFirstOpTimestamp({ project_id: projectId }) { + return `ProjectHistory:FirstOpTimestamp:${projectId}` }, }, }, - }), - '@overleaf/redis-wrapper': { - createClient: () => this.rclient, }, - './Metrics': (this.metrics = { summary: sinon.stub() }), - './Limits': this.Limits, + }), + '@overleaf/redis-wrapper': { + createClient: () => this.rclient, }, - } - )) + './Metrics': (this.metrics = { summary: sinon.stub() }), + './Limits': this.Limits, + }, + }) }) afterEach(function () { - return tk.reset() + tk.reset() }) describe('queueOps', function () { @@ -70,15 +55,15 @@ describe('ProjectHistoryRedisManager', function () { this.multi.setnx = sinon.stub() this.rclient.multi = () => this.multi // @rclient = multi: () => @multi - return this.ProjectHistoryRedisManager.queueOps( + this.ProjectHistoryRedisManager.queueOps( this.project_id, - ...Array.from(this.ops), + ...this.ops, this.callback ) }) it('should queue an update', function () { - return this.multi.rpush + this.multi.rpush .calledWithExactly( `ProjectHistory:Ops:${this.project_id}`, this.ops[0], @@ -87,8 +72,8 @@ describe('ProjectHistoryRedisManager', function () { .should.equal(true) }) - return it('should set the queue timestamp if not present', function () { - return this.multi.setnx + it('should set the queue timestamp if not present', function () { + this.multi.setnx .calledWithExactly( `ProjectHistory:FirstOpTimestamp:${this.project_id}`, Date.now() @@ -108,31 +93,33 @@ describe('ProjectHistoryRedisManager', function () { } this.ProjectHistoryRedisManager.queueOps = sinon.stub() - return this.ProjectHistoryRedisManager.queueRenameEntity( + this.ProjectHistoryRedisManager.queueRenameEntity( this.project_id, this.projectHistoryId, 'file', this.file_id, this.user_id, this.rawUpdate, + this.source, this.callback ) }) - return it('should queue an update', function () { + it('should queue an update', function () { const update = { pathname: this.pathname, new_pathname: this.newPathname, meta: { user_id: this.user_id, ts: new Date(), + source: this.source, }, version: this.version, projectHistoryId: this.projectHistoryId, file: this.file_id, } - return this.ProjectHistoryRedisManager.queueOps + this.ProjectHistoryRedisManager.queueOps .calledWithExactly( this.project_id, JSON.stringify(update), @@ -142,7 +129,7 @@ describe('ProjectHistoryRedisManager', function () { }) }) - return describe('queueAddEntity', function () { + describe('queueAddEntity', function () { beforeEach(function () { this.rclient.rpush = sinon.stub().yields() this.doc_id = 1234 @@ -155,13 +142,14 @@ describe('ProjectHistoryRedisManager', function () { } this.ProjectHistoryRedisManager.queueOps = sinon.stub() - return this.ProjectHistoryRedisManager.queueAddEntity( + this.ProjectHistoryRedisManager.queueAddEntity( this.project_id, this.projectHistoryId, 'doc', this.doc_id, this.user_id, this.rawUpdate, + this.source, this.callback ) }) @@ -174,13 +162,14 @@ describe('ProjectHistoryRedisManager', function () { meta: { user_id: this.user_id, ts: new Date(), + source: this.source, }, version: this.version, projectHistoryId: this.projectHistoryId, doc: this.doc_id, } - return this.ProjectHistoryRedisManager.queueOps + this.ProjectHistoryRedisManager.queueOps .calledWithExactly( this.project_id, JSON.stringify(update), @@ -190,7 +179,7 @@ describe('ProjectHistoryRedisManager', function () { }) describe('queueResyncProjectStructure', function () { - return it('should queue an update', function () {}) + it('should queue an update', function () {}) }) describe('queueResyncDocContent', function () { diff --git a/services/document-updater/test/unit/js/ProjectManager/updateProjectTests.js b/services/document-updater/test/unit/js/ProjectManager/updateProjectTests.js index ffc1257fe0..c2da2774ca 100644 --- a/services/document-updater/test/unit/js/ProjectManager/updateProjectTests.js +++ b/services/document-updater/test/unit/js/ProjectManager/updateProjectTests.js @@ -36,6 +36,7 @@ describe('ProjectManager', function () { this.projectHistoryId = 'history-id-123' this.user_id = 'user-id-123' this.version = 1234567 + this.source = 'editor' this.callback = sinon.stub() }) @@ -75,6 +76,7 @@ describe('ProjectManager', function () { this.user_id, this.updates, this.version, + this.source, this.callback ) }) @@ -121,7 +123,8 @@ describe('ProjectManager', function () { 'file', this.firstFileUpdate.id, this.user_id, - firstFileUpdateWithVersion + firstFileUpdateWithVersion, + this.source ) .should.equal(true) }) @@ -147,6 +150,7 @@ describe('ProjectManager', function () { this.user_id, this.updates, this.version, + this.source, this.callback ) }) @@ -166,6 +170,7 @@ describe('ProjectManager', function () { this.user_id, this.updates, this.version, + this.source, this.callback ) }) @@ -184,6 +189,7 @@ describe('ProjectManager', function () { this.user_id, this.updates, this.version, + this.source, this.callback ) }) @@ -234,6 +240,7 @@ describe('ProjectManager', function () { this.user_id, this.updates, this.version, + this.source, this.callback ) }) @@ -255,7 +262,8 @@ describe('ProjectManager', function () { 'doc', this.firstDocUpdate.id, this.user_id, - firstDocUpdateWithVersion + firstDocUpdateWithVersion, + this.source ) .should.equal(true) this.ProjectHistoryRedisManager.queueAddEntity @@ -266,7 +274,8 @@ describe('ProjectManager', function () { 'doc', this.secondDocUpdate.id, this.user_id, - secondDocUpdateWithVersion + secondDocUpdateWithVersion, + this.source ) .should.equal(true) }) @@ -290,7 +299,8 @@ describe('ProjectManager', function () { 'file', this.firstFileUpdate.id, this.user_id, - firstFileUpdateWithVersion + firstFileUpdateWithVersion, + this.source ) .should.equal(true) this.ProjectHistoryRedisManager.queueAddEntity @@ -301,7 +311,8 @@ describe('ProjectManager', function () { 'file', this.secondFileUpdate.id, this.user_id, - secondFileUpdateWithVersion + secondFileUpdateWithVersion, + this.source ) .should.equal(true) }) @@ -327,6 +338,7 @@ describe('ProjectManager', function () { this.user_id, this.updates, this.version, + this.source, this.callback ) }) @@ -346,6 +358,7 @@ describe('ProjectManager', function () { this.user_id, this.updates, this.version, + this.source, this.callback ) }) @@ -364,6 +377,7 @@ describe('ProjectManager', function () { this.user_id, this.updates, this.version, + this.source, this.callback ) }) @@ -385,6 +399,7 @@ describe('ProjectManager', function () { this.user_id, this.updates, this.version, + this.source, this.callback ) }) diff --git a/services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js b/services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js index 07edc68bf1..5ec5a7f266 100644 --- a/services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js +++ b/services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js @@ -243,6 +243,7 @@ function updateProjectStructure( projectHistoryId, userId, changes, + source, callback ) { if ( @@ -293,6 +294,7 @@ function updateProjectStructure( userId, version: projectVersion, projectHistoryId, + source, }, method: 'POST', }, diff --git a/services/web/app/src/Features/Editor/EditorController.js b/services/web/app/src/Features/Editor/EditorController.js index 6e9f3b18f5..9952f60c3e 100644 --- a/services/web/app/src/Features/Editor/EditorController.js +++ b/services/web/app/src/Features/Editor/EditorController.js @@ -1,18 +1,3 @@ -/* eslint-disable - camelcase, - n/handle-callback-err, - max-len, - no-dupe-keys, - 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 OError = require('@overleaf/o-error') const Metrics = require('@overleaf/metrics') @@ -20,181 +5,159 @@ const ProjectEntityUpdateHandler = require('../Project/ProjectEntityUpdateHandle const ProjectOptionsHandler = require('../Project/ProjectOptionsHandler') const ProjectDetailsHandler = require('../Project/ProjectDetailsHandler') const ProjectDeleter = require('../Project/ProjectDeleter') -const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler') const EditorRealTimeController = require('./EditorRealTimeController') const async = require('async') const PublicAccessLevels = require('../Authorization/PublicAccessLevels') -const _ = require('underscore') const { promisifyAll } = require('../../util/promises') const EditorController = { - addDoc(project_id, folder_id, docName, docLines, source, user_id, callback) { - if (callback == null) { - callback = function () {} - } - return EditorController.addDocWithRanges( - project_id, - folder_id, + addDoc(projectId, folderId, docName, docLines, source, userId, callback) { + EditorController.addDocWithRanges( + projectId, + folderId, docName, docLines, {}, source, - user_id, + userId, callback ) }, addDocWithRanges( - project_id, - folder_id, + projectId, + folderId, docName, docLines, docRanges, source, - user_id, + userId, callback ) { - if (callback == null) { - callback = function () {} - } docName = docName.trim() Metrics.inc('editor.add-doc') - return ProjectEntityUpdateHandler.addDocWithRanges( - project_id, - folder_id, + ProjectEntityUpdateHandler.addDocWithRanges( + projectId, + folderId, docName, docLines, docRanges, - user_id, - (err, doc, folder_id) => { - if (err != null) { + userId, + source, + (err, doc, folderId) => { + if (err) { OError.tag(err, 'error adding doc without lock', { - project_id, + projectId, docName, }) return callback(err) } EditorRealTimeController.emitToRoom( - project_id, + projectId, 'reciveNewDoc', - folder_id, + folderId, doc, source, - user_id + userId ) - return callback(err, doc) + callback(err, doc) } ) }, addFile( - project_id, - folder_id, + projectId, + folderId, fileName, fsPath, linkedFileData, source, - user_id, + userId, callback ) { - if (callback == null) { - callback = function () {} - } fileName = fileName.trim() Metrics.inc('editor.add-file') - return ProjectEntityUpdateHandler.addFile( - project_id, - folder_id, + ProjectEntityUpdateHandler.addFile( + projectId, + folderId, fileName, fsPath, linkedFileData, - user_id, - (err, fileRef, folder_id) => { - if (err != null) { + userId, + source, + (err, fileRef, folderId) => { + if (err) { OError.tag(err, 'error adding file without lock', { - project_id, - folder_id, + projectId, + folderId, fileName, }) return callback(err) } EditorRealTimeController.emitToRoom( - project_id, + projectId, 'reciveNewFile', - folder_id, + folderId, fileRef, source, linkedFileData, - user_id + userId ) - return callback(err, fileRef) + callback(err, fileRef) } ) }, - upsertDoc( - project_id, - folder_id, - docName, - docLines, - source, - user_id, - callback - ) { - if (callback == null) { - callback = function () {} - } - return ProjectEntityUpdateHandler.upsertDoc( - project_id, - folder_id, + upsertDoc(projectId, folderId, docName, docLines, source, userId, callback) { + ProjectEntityUpdateHandler.upsertDoc( + projectId, + folderId, docName, docLines, source, - user_id, + userId, function (err, doc, didAddNewDoc) { if (didAddNewDoc) { EditorRealTimeController.emitToRoom( - project_id, + projectId, 'reciveNewDoc', - folder_id, + folderId, doc, source, - user_id + userId ) } - return callback(err, doc) + callback(err, doc) } ) }, upsertFile( - project_id, - folder_id, + projectId, + folderId, fileName, fsPath, linkedFileData, source, - user_id, + userId, callback ) { - if (callback == null) { - callback = function () {} - } - return ProjectEntityUpdateHandler.upsertFile( - project_id, - folder_id, + ProjectEntityUpdateHandler.upsertFile( + projectId, + folderId, fileName, fsPath, linkedFileData, - user_id, + userId, + source, function (err, newFile, didAddFile, existingFile) { - if (err != null) { + if (err) { return callback(err) } if (!didAddFile) { // replacement, so remove the existing file from the client EditorRealTimeController.emitToRoom( - project_id, + projectId, 'removeEntity', existingFile._id, source @@ -202,55 +165,55 @@ const EditorController = { } // now add the new file on the client EditorRealTimeController.emitToRoom( - project_id, + projectId, 'reciveNewFile', - folder_id, + folderId, newFile, source, linkedFileData, - user_id + userId ) - return callback(null, newFile) + callback(null, newFile) } ) }, upsertDocWithPath( - project_id, + projectId, elementPath, docLines, source, - user_id, + userId, callback ) { - return ProjectEntityUpdateHandler.upsertDocWithPath( - project_id, + ProjectEntityUpdateHandler.upsertDocWithPath( + projectId, elementPath, docLines, source, - user_id, + userId, function (err, doc, didAddNewDoc, newFolders, lastFolder) { - if (err != null) { + if (err) { return callback(err) } - return EditorController._notifyProjectUsersOfNewFolders( - project_id, + EditorController._notifyProjectUsersOfNewFolders( + projectId, newFolders, function (err) { - if (err != null) { + if (err) { return callback(err) } if (didAddNewDoc) { EditorRealTimeController.emitToRoom( - project_id, + projectId, 'reciveNewDoc', lastFolder._id, doc, source, - user_id + userId ) } - return callback() + callback() } ) } @@ -258,20 +221,21 @@ const EditorController = { }, upsertFileWithPath( - project_id, + projectId, elementPath, fsPath, linkedFileData, source, - user_id, + userId, callback ) { - return ProjectEntityUpdateHandler.upsertFileWithPath( - project_id, + ProjectEntityUpdateHandler.upsertFileWithPath( + projectId, elementPath, fsPath, linkedFileData, - user_id, + userId, + source, function ( err, newFile, @@ -280,20 +244,20 @@ const EditorController = { newFolders, lastFolder ) { - if (err != null) { + if (err) { return callback(err) } - return EditorController._notifyProjectUsersOfNewFolders( - project_id, + EditorController._notifyProjectUsersOfNewFolders( + projectId, newFolders, function (err) { - if (err != null) { + if (err) { return callback(err) } if (!didAddFile) { // replacement, so remove the existing file from the client EditorRealTimeController.emitToRoom( - project_id, + projectId, 'removeEntity', existingFile._id, source @@ -301,193 +265,189 @@ const EditorController = { } // now add the new file on the client EditorRealTimeController.emitToRoom( - project_id, + projectId, 'reciveNewFile', lastFolder._id, newFile, source, linkedFileData, - user_id + userId ) - return callback() + callback() } ) } ) }, - addFolder(project_id, folder_id, folderName, source, userId, callback) { - if (callback == null) { - callback = function () {} - } + addFolder(projectId, folderId, folderName, source, userId, callback) { folderName = folderName.trim() Metrics.inc('editor.add-folder') - return ProjectEntityUpdateHandler.addFolder( - project_id, - folder_id, + ProjectEntityUpdateHandler.addFolder( + projectId, + folderId, folderName, - (err, folder, folder_id) => { - if (err != null) { + (err, folder, folderId) => { + if (err) { OError.tag(err, 'could not add folder', { - project_id, - folder_id, + projectId, + folderId, folderName, source, }) return callback(err) } - return EditorController._notifyProjectUsersOfNewFolder( - project_id, - folder_id, + EditorController._notifyProjectUsersOfNewFolder( + projectId, + folderId, folder, userId, function (err) { - if (err != null) { + if (err) { return callback(err) } - return callback(null, folder) + callback(null, folder) } ) } ) }, - mkdirp(project_id, path, callback) { - if (callback == null) { - callback = function () {} - } - logger.debug({ project_id, path }, "making directories if they don't exist") - return ProjectEntityUpdateHandler.mkdirp( - project_id, + mkdirp(projectId, path, callback) { + logger.debug({ projectId, path }, "making directories if they don't exist") + ProjectEntityUpdateHandler.mkdirp( + projectId, path, (err, newFolders, lastFolder) => { - if (err != null) { + if (err) { OError.tag(err, 'could not mkdirp', { - project_id, + projectId, path, }) return callback(err) } - return EditorController._notifyProjectUsersOfNewFolders( - project_id, + EditorController._notifyProjectUsersOfNewFolders( + projectId, newFolders, function (err) { - if (err != null) { + if (err) { return callback(err) } - return callback(null, newFolders, lastFolder) + callback(null, newFolders, lastFolder) } ) } ) }, - deleteEntity(project_id, entity_id, entityType, source, userId, callback) { - if (callback == null) { - callback = function () {} - } + deleteEntity(projectId, entityId, entityType, source, userId, callback) { Metrics.inc('editor.delete-entity') - return ProjectEntityUpdateHandler.deleteEntity( - project_id, - entity_id, + ProjectEntityUpdateHandler.deleteEntity( + projectId, + entityId, entityType, userId, + source, function (err) { - if (err != null) { + if (err) { OError.tag(err, 'could not delete entity', { - project_id, - entity_id, + projectId, + entityId, entityType, }) return callback(err) } logger.debug( - { project_id, entity_id, entityType }, + { projectId, entityId, entityType }, 'telling users entity has been deleted' ) EditorRealTimeController.emitToRoom( - project_id, + projectId, 'removeEntity', - entity_id, + entityId, source ) - return callback() + callback() } ) }, - deleteEntityWithPath(project_id, path, source, user_id, callback) { - return ProjectEntityUpdateHandler.deleteEntityWithPath( - project_id, + deleteEntityWithPath(projectId, path, source, userId, callback) { + ProjectEntityUpdateHandler.deleteEntityWithPath( + projectId, path, - user_id, - function (err, entity_id) { - if (err != null) { + userId, + source, + function (err, entityId) { + if (err) { return callback(err) } EditorRealTimeController.emitToRoom( - project_id, + projectId, 'removeEntity', - entity_id, + entityId, source ) - return callback(null, entity_id) + callback(null, entityId) } ) }, - updateProjectDescription(project_id, description, callback) { - if (callback == null) { - callback = function () {} - } - logger.debug({ project_id, description }, 'updating project description') - return ProjectDetailsHandler.setProjectDescription( - project_id, + updateProjectDescription(projectId, description, callback) { + logger.debug({ projectId, description }, 'updating project description') + ProjectDetailsHandler.setProjectDescription( + projectId, description, function (err) { - if (err != null) { + if (err) { OError.tag( err, 'something went wrong setting the project description', { - project_id, + projectId, description, } ) return callback(err) } EditorRealTimeController.emitToRoom( - project_id, + projectId, 'projectDescriptionUpdated', description ) - return callback() + callback() } ) }, - deleteProject(project_id, callback) { + deleteProject(projectId, callback) { Metrics.inc('editor.delete-project') - return ProjectDeleter.deleteProject(project_id, callback) + ProjectDeleter.deleteProject(projectId, callback) }, - renameEntity(project_id, entity_id, entityType, newName, userId, callback) { - if (callback == null) { - callback = function () {} - } + renameEntity( + projectId, + entityId, + entityType, + newName, + userId, + source, + callback + ) { Metrics.inc('editor.rename-entity') - return ProjectEntityUpdateHandler.renameEntity( - project_id, - entity_id, + ProjectEntityUpdateHandler.renameEntity( + projectId, + entityId, entityType, newName, userId, + source, function (err) { - if (err != null) { + if (err) { OError.tag(err, 'error renaming entity', { - project_id, - entity_id, + projectId, + entityId, entityType, newName, }) @@ -495,204 +455,177 @@ const EditorController = { } if (newName.length > 0) { EditorRealTimeController.emitToRoom( - project_id, + projectId, 'reciveEntityRename', - entity_id, + entityId, newName ) } - return callback() + callback() } ) }, - moveEntity(project_id, entity_id, folder_id, entityType, userId, callback) { - if (callback == null) { - callback = function () {} - } + moveEntity( + projectId, + entityId, + folderId, + entityType, + userId, + source, + callback + ) { Metrics.inc('editor.move-entity') - return ProjectEntityUpdateHandler.moveEntity( - project_id, - entity_id, - folder_id, + ProjectEntityUpdateHandler.moveEntity( + projectId, + entityId, + folderId, entityType, userId, + source, function (err) { - if (err != null) { + if (err) { OError.tag(err, 'error moving entity', { - project_id, - entity_id, - folder_id, + projectId, + entityId, + folderId, }) return callback(err) } EditorRealTimeController.emitToRoom( - project_id, + projectId, 'reciveEntityMove', - entity_id, - folder_id + entityId, + folderId ) - return callback() + callback() } ) }, - renameProject(project_id, newName, callback) { - if (callback == null) { - callback = function () {} - } - return ProjectDetailsHandler.renameProject( - project_id, - newName, - function (err) { - if (err != null) { - OError.tag(err, 'error renaming project', { - project_id, - newName, - }) - return callback(err) - } - EditorRealTimeController.emitToRoom( - project_id, - 'projectNameUpdated', - newName - ) - return callback() + renameProject(projectId, newName, callback) { + ProjectDetailsHandler.renameProject(projectId, newName, function (err) { + if (err) { + OError.tag(err, 'error renaming project', { + projectId, + newName, + }) + return callback(err) } - ) + EditorRealTimeController.emitToRoom( + projectId, + 'projectNameUpdated', + newName + ) + callback() + }) }, - setCompiler(project_id, compiler, callback) { - if (callback == null) { - callback = function () {} - } - return ProjectOptionsHandler.setCompiler( - project_id, - compiler, - function (err) { - if (err != null) { - return callback(err) - } - EditorRealTimeController.emitToRoom( - project_id, - 'compilerUpdated', - compiler - ) - return callback() + setCompiler(projectId, compiler, callback) { + ProjectOptionsHandler.setCompiler(projectId, compiler, function (err) { + if (err) { + return callback(err) } - ) + EditorRealTimeController.emitToRoom( + projectId, + 'compilerUpdated', + compiler + ) + callback() + }) }, - setImageName(project_id, imageName, callback) { - if (callback == null) { - callback = function () {} - } - return ProjectOptionsHandler.setImageName( - project_id, - imageName, - function (err) { - if (err != null) { - return callback(err) - } - EditorRealTimeController.emitToRoom( - project_id, - 'imageNameUpdated', - imageName - ) - return callback() + setImageName(projectId, imageName, callback) { + ProjectOptionsHandler.setImageName(projectId, imageName, function (err) { + if (err) { + return callback(err) } - ) + EditorRealTimeController.emitToRoom( + projectId, + 'imageNameUpdated', + imageName + ) + callback() + }) }, - setSpellCheckLanguage(project_id, languageCode, callback) { - if (callback == null) { - callback = function () {} - } - return ProjectOptionsHandler.setSpellCheckLanguage( - project_id, + setSpellCheckLanguage(projectId, languageCode, callback) { + ProjectOptionsHandler.setSpellCheckLanguage( + projectId, languageCode, function (err) { - if (err != null) { + if (err) { return callback(err) } EditorRealTimeController.emitToRoom( - project_id, + projectId, 'spellCheckLanguageUpdated', languageCode ) - return callback() + callback() } ) }, - setPublicAccessLevel(project_id, newAccessLevel, callback) { - if (callback == null) { - callback = function () {} - } - return ProjectDetailsHandler.setPublicAccessLevel( - project_id, + setPublicAccessLevel(projectId, newAccessLevel, callback) { + ProjectDetailsHandler.setPublicAccessLevel( + projectId, newAccessLevel, function (err) { - if (err != null) { + if (err) { return callback(err) } EditorRealTimeController.emitToRoom( - project_id, + projectId, 'project:publicAccessLevel:changed', { newAccessLevel } ) if (newAccessLevel === PublicAccessLevels.TOKEN_BASED) { - return ProjectDetailsHandler.ensureTokensArePresent( - project_id, + ProjectDetailsHandler.ensureTokensArePresent( + projectId, function (err, tokens) { - if (err != null) { + if (err) { return callback(err) } EditorRealTimeController.emitToRoom( - project_id, + projectId, 'project:tokens:changed', { tokens } ) - return callback() + callback() } ) } else { - return callback() + callback() } } ) }, - setRootDoc(project_id, newRootDocID, callback) { - if (callback == null) { - callback = function () {} - } - return ProjectEntityUpdateHandler.setRootDoc( - project_id, + setRootDoc(projectId, newRootDocID, callback) { + ProjectEntityUpdateHandler.setRootDoc( + projectId, newRootDocID, function (err) { - if (err != null) { + if (err) { return callback(err) } EditorRealTimeController.emitToRoom( - project_id, + projectId, 'rootDocUpdated', newRootDocID ) - return callback() + callback() } ) }, - _notifyProjectUsersOfNewFolders(project_id, folders, callback) { - if (callback == null) { - callback = function () {} - } - return async.eachSeries( + _notifyProjectUsersOfNewFolders(projectId, folders, callback) { + async.eachSeries( folders, (folder, cb) => EditorController._notifyProjectUsersOfNewFolder( - project_id, + projectId, folder.parentFolder_id, folder, null, @@ -703,23 +636,20 @@ const EditorController = { }, _notifyProjectUsersOfNewFolder( - project_id, - folder_id, + projectId, + folderId, folder, userId, callback ) { - if (callback == null) { - callback = function () {} - } EditorRealTimeController.emitToRoom( - project_id, + projectId, 'reciveNewFolder', - folder_id, + folderId, folder, userId ) - return callback() + callback() }, } diff --git a/services/web/app/src/Features/Editor/EditorHttpController.js b/services/web/app/src/Features/Editor/EditorHttpController.js index 415ab72f1d..316f9c6e52 100644 --- a/services/web/app/src/Features/Editor/EditorHttpController.js +++ b/services/web/app/src/Features/Editor/EditorHttpController.js @@ -212,7 +212,7 @@ async function renameEntity(req, res, next) { const projectId = req.params.Project_id const entityId = req.params.entity_id const entityType = req.params.entity_type - const { name } = req.body + const { name, source = 'editor' } = req.body if (!_nameIsAcceptableLength(name)) { return res.sendStatus(400) } @@ -222,7 +222,8 @@ async function renameEntity(req, res, next) { entityId, entityType, name, - userId + userId, + source ) res.sendStatus(204) } @@ -232,13 +233,15 @@ async function moveEntity(req, res, next) { const entityId = req.params.entity_id const entityType = req.params.entity_type const folderId = req.body.folder_id + const source = req.body.source ?? 'editor' const userId = SessionManager.getLoggedInUserId(req.session) await EditorController.promises.moveEntity( projectId, entityId, folderId, entityType, - userId + userId, + source ) res.sendStatus(204) } diff --git a/services/web/app/src/Features/Project/ProjectCreationHandler.js b/services/web/app/src/Features/Project/ProjectCreationHandler.js index 2e245d1997..e480c95e70 100644 --- a/services/web/app/src/Features/Project/ProjectCreationHandler.js +++ b/services/web/app/src/Features/Project/ProjectCreationHandler.js @@ -112,7 +112,8 @@ async function _addExampleProjectFiles(ownerId, projectName, project) { project.rootFolder[0]._id, 'sample.bib', bibDocLines, - ownerId + ownerId, + null ) const frogPath = path.join( @@ -125,7 +126,8 @@ async function _addExampleProjectFiles(ownerId, projectName, project) { 'frog.jpg', frogPath, null, - ownerId + ownerId, + null ) } @@ -177,7 +179,8 @@ async function _createRootDoc(project, ownerId, docLines) { project.rootFolder[0]._id, 'main.tex', docLines, - ownerId + ownerId, + null ) await ProjectEntityUpdateHandler.promises.setRootDoc(project._id, doc._id) } catch (error) { diff --git a/services/web/app/src/Features/Project/ProjectDuplicator.js b/services/web/app/src/Features/Project/ProjectDuplicator.js index c10779f581..c7c8e16cc8 100644 --- a/services/web/app/src/Features/Project/ProjectDuplicator.js +++ b/services/web/app/src/Features/Project/ProjectDuplicator.js @@ -205,6 +205,7 @@ async function _notifyDocumentUpdater(project, userId, changes) { project._id, projectHistoryId, userId, - changes + changes, + null ) } diff --git a/services/web/app/src/Features/Project/ProjectEntityUpdateHandler.js b/services/web/app/src/Features/Project/ProjectEntityUpdateHandler.js index b9476fa92d..ae5d2e1776 100644 --- a/services/web/app/src/Features/Project/ProjectEntityUpdateHandler.js +++ b/services/web/app/src/Features/Project/ProjectEntityUpdateHandler.js @@ -298,7 +298,7 @@ const ProjectEntityUpdateHandler = { ) }, - addDoc(projectId, folderId, docName, docLines, userId, callback) { + addDoc(projectId, folderId, docName, docLines, userId, source, callback) { ProjectEntityUpdateHandler.addDocWithRanges( projectId, folderId, @@ -306,6 +306,7 @@ const ProjectEntityUpdateHandler = { docLines, {}, userId, + source, callback ) }, @@ -319,6 +320,7 @@ const ProjectEntityUpdateHandler = { docLines, ranges, userId, + source, callback ) { if (!SafePath.isCleanFilename(docName)) { @@ -345,6 +347,7 @@ const ProjectEntityUpdateHandler = { docLines, ranges, userId, + source, callback ) } @@ -359,6 +362,7 @@ const ProjectEntityUpdateHandler = { docLines, ranges, userId, + source, callback ) { ProjectEntityUpdateHandler._addDocAndSendToTpds( @@ -386,6 +390,7 @@ const ProjectEntityUpdateHandler = { projectHistoryId, userId, { newDocs, newProject: project }, + source, error => { if (error != null) { return callback(error) @@ -468,6 +473,7 @@ const ProjectEntityUpdateHandler = { fsPath, linkedFileData, userId, + source, callback ) { if (!SafePath.isCleanFilename(fileName)) { @@ -492,6 +498,7 @@ const ProjectEntityUpdateHandler = { userId, fileRef, fileStoreUrl, + source, callback ) } @@ -507,6 +514,7 @@ const ProjectEntityUpdateHandler = { userId, fileRef, fileStoreUrl, + source, callback ) { ProjectEntityUpdateHandler._addFileAndSendToTpds( @@ -533,6 +541,7 @@ const ProjectEntityUpdateHandler = { projectHistoryId, userId, { newFiles, newProject: project }, + source, error => { if (error != null) { return callback(error) @@ -554,6 +563,7 @@ const ProjectEntityUpdateHandler = { fsPath, linkedFileData, userId, + source, callback ) { // create a new file @@ -577,6 +587,7 @@ const ProjectEntityUpdateHandler = { userId, fileRef, fileStoreUrl, + source, callback ) } @@ -591,6 +602,7 @@ const ProjectEntityUpdateHandler = { userId, newFileRef, fileStoreUrl, + source, callback ) { ProjectEntityMongoUpdateHandler.replaceFileWithNew( @@ -643,6 +655,7 @@ const ProjectEntityUpdateHandler = { projectHistoryId, userId, { oldFiles, newFiles, newProject }, + source, callback ) } @@ -732,6 +745,7 @@ const ProjectEntityUpdateHandler = { projectHistoryId, userId, { oldFiles, newDocs, newProject: project }, + source, error => { if (error != null) { return callback(error) @@ -786,6 +800,7 @@ const ProjectEntityUpdateHandler = { docLines, {}, userId, + source, (err, doc) => { if (err != null) { return callback(err) @@ -807,6 +822,7 @@ const ProjectEntityUpdateHandler = { fsPath, linkedFileData, userId, + source, callback ) { if (!SafePath.isCleanFilename(fileName)) { @@ -834,6 +850,7 @@ const ProjectEntityUpdateHandler = { userId, fileRef, fileStoreUrl, + source, callback ) } @@ -849,6 +866,7 @@ const ProjectEntityUpdateHandler = { userId, newFileRef, fileStoreUrl, + source, callback ) { ProjectLocator.findElement( @@ -918,6 +936,7 @@ const ProjectEntityUpdateHandler = { ], newProject: project, }, + source, err => { if (err) { return callback(err) @@ -947,6 +966,7 @@ const ProjectEntityUpdateHandler = { userId, newFileRef, fileStoreUrl, + source, err => { if (err != null) { return callback(err) @@ -965,6 +985,7 @@ const ProjectEntityUpdateHandler = { userId, newFileRef, fileStoreUrl, + source, err => { if (err != null) { return callback(err) @@ -1024,6 +1045,7 @@ const ProjectEntityUpdateHandler = { fsPath, linkedFileData, userId, + source, callback ) { if (!SafePath.isCleanPath(elementPath)) { @@ -1053,6 +1075,7 @@ const ProjectEntityUpdateHandler = { userId, fileRef, fileStoreUrl, + source, callback ) } @@ -1068,6 +1091,7 @@ const ProjectEntityUpdateHandler = { userId, fileRef, fileStoreUrl, + source, callback ) { ProjectEntityUpdateHandler.mkdirp.withoutLock( @@ -1087,6 +1111,7 @@ const ProjectEntityUpdateHandler = { userId, fileRef, fileStoreUrl, + source, (err, newFile, isNewFile, existingFile) => { if (err != null) { return callback(err) @@ -1111,6 +1136,7 @@ const ProjectEntityUpdateHandler = { entityId, entityType, userId, + source, callback ) { logger.debug({ entityId, entityType, projectId }, 'deleting project entity') @@ -1134,6 +1160,7 @@ const ProjectEntityUpdateHandler = { entityType, path.fileSystem, userId, + source, error => { if (error != null) { return callback(error) @@ -1157,25 +1184,27 @@ const ProjectEntityUpdateHandler = { ) }), - deleteEntityWithPath: wrapWithLock((projectId, path, userId, callback) => - ProjectLocator.findElementByPath( - { project_id: projectId, path }, - (err, element, type) => { - if (err != null) { - return callback(err) + deleteEntityWithPath: wrapWithLock( + (projectId, path, userId, source, callback) => + ProjectLocator.findElementByPath( + { project_id: projectId, path }, + (err, element, type) => { + if (err != null) { + return callback(err) + } + if (element == null) { + return callback(new Errors.NotFoundError('project not found')) + } + ProjectEntityUpdateHandler.deleteEntity.withoutLock( + projectId, + element._id, + type, + userId, + source, + callback + ) } - if (element == null) { - return callback(new Errors.NotFoundError('project not found')) - } - ProjectEntityUpdateHandler.deleteEntity.withoutLock( - projectId, - element._id, - type, - userId, - callback - ) - } - ) + ) ), mkdirp: wrapWithLock(function (projectId, path, callback) { @@ -1229,6 +1258,7 @@ const ProjectEntityUpdateHandler = { destFolderId, entityType, userId, + source, callback ) { logger.debug( @@ -1270,6 +1300,7 @@ const ProjectEntityUpdateHandler = { projectHistoryId, userId, changes, + source, callback ) } @@ -1282,6 +1313,7 @@ const ProjectEntityUpdateHandler = { entityType, newName, userId, + source, callback ) { if (!SafePath.isCleanFilename(newName)) { @@ -1324,6 +1356,7 @@ const ProjectEntityUpdateHandler = { projectHistoryId, userId, changes, + source, callback ) } @@ -1501,6 +1534,7 @@ const ProjectEntityUpdateHandler = { projectHistoryId, null, changes, + 'automatic-fix', cb ) } @@ -1543,6 +1577,7 @@ const ProjectEntityUpdateHandler = { entityType, path, userId, + source, callback ) { ProjectEntityUpdateHandler._updateProjectStructureWithDeletedEntity( @@ -1552,6 +1587,7 @@ const ProjectEntityUpdateHandler = { entityType, path, userId, + source, error => { if (error != null) { return callback(error) @@ -1598,6 +1634,7 @@ const ProjectEntityUpdateHandler = { entityType, entityPath, userId, + source, callback ) { // compute the changes to the project structure @@ -1636,6 +1673,7 @@ const ProjectEntityUpdateHandler = { projectHistoryId, userId, changes, + source, callback ) }, @@ -1723,7 +1761,7 @@ const ProjectEntityUpdateHandler = { convertDocToFile: wrapWithLock({ beforeLock(next) { - return function (projectId, docId, userId, callback) { + return function (projectId, docId, userId, source, callback) { DocumentUpdaterHandler.flushDocToMongo(projectId, docId, err => { if (err) { return callback(err) @@ -1778,6 +1816,7 @@ const ProjectEntityUpdateHandler = { fileRef, fileStoreUrl, userId, + source, callback ) }) @@ -1793,7 +1832,16 @@ const ProjectEntityUpdateHandler = { }) } }, - withLock(projectId, doc, path, fileRef, fileStoreUrl, userId, callback) { + withLock( + projectId, + doc, + path, + fileRef, + fileStoreUrl, + userId, + source, + callback + ) { ProjectEntityMongoUpdateHandler.replaceDocWithFile( projectId, doc._id, @@ -1815,6 +1863,7 @@ const ProjectEntityUpdateHandler = { newFiles: [{ file: fileRef, path, url: fileStoreUrl }], newProject: project, }, + source, err => { if (err) { return callback(err) diff --git a/services/web/app/src/Features/Uploads/ProjectUploadManager.js b/services/web/app/src/Features/Uploads/ProjectUploadManager.js index dd70e6a9ee..98e1d38bb9 100644 --- a/services/web/app/src/Features/Uploads/ProjectUploadManager.js +++ b/services/web/app/src/Features/Uploads/ProjectUploadManager.js @@ -204,6 +204,7 @@ async function _notifyDocumentUpdater(project, userId, changes) { project._id, projectHistoryId, userId, - changes + changes, + null ) } diff --git a/services/web/scripts/convert_doc_to_file.js b/services/web/scripts/convert_doc_to_file.js index 6f5d648d3d..3902e4fe3e 100644 --- a/services/web/scripts/convert_doc_to_file.js +++ b/services/web/scripts/convert_doc_to_file.js @@ -21,7 +21,8 @@ async function main() { await ProjectEntityUpdateHandler.promises.convertDocToFile( projectId, docId, - userId + userId, + null ) } catch (err) { if (err instanceof Errors.NotFoundError) { diff --git a/services/web/scripts/create_project.js b/services/web/scripts/create_project.js index f03396f8a2..52555d11e9 100644 --- a/services/web/scripts/create_project.js +++ b/services/web/scripts/create_project.js @@ -36,7 +36,8 @@ async function _createRootDoc(project, ownerId, docLines) { project.rootFolder[0]._id, 'main.tex', docLines, - ownerId + ownerId, + null ) await ProjectEntityUpdateHandler.promises.setRootDoc(project._id, doc._id) } catch (error) { @@ -62,7 +63,8 @@ async function _addDefaultExampleProjectFiles(ownerId, projectName, project) { project.rootFolder[0]._id, 'sample.bib', bibDocLines, - ownerId + ownerId, + null ) const frogPath = path.join( @@ -75,7 +77,8 @@ async function _addDefaultExampleProjectFiles(ownerId, projectName, project) { 'frog.jpg', frogPath, null, - ownerId + ownerId, + null ) } diff --git a/services/web/scripts/history/HistoryUpgradeHelper.js b/services/web/scripts/history/HistoryUpgradeHelper.js index e3249ab8fa..84baa900a2 100644 --- a/services/web/scripts/history/HistoryUpgradeHelper.js +++ b/services/web/scripts/history/HistoryUpgradeHelper.js @@ -304,7 +304,8 @@ async function convertLargeDocsToFile(projectId, userId) { await ProjectEntityUpdateHandler.promises.convertDocToFile( projectId, doc._id, - userId + userId, + null ) convertedDocCount++ } diff --git a/services/web/scripts/recover_docs_from_redis.js b/services/web/scripts/recover_docs_from_redis.js index 0a536fb031..e18a3e7a0e 100644 --- a/services/web/scripts/recover_docs_from_redis.js +++ b/services/web/scripts/recover_docs_from_redis.js @@ -123,7 +123,8 @@ async function processDoc(docId) { restoredName, doc.lines, doc.ranges, - null + null, + 'recovery-script' ) await deleteDocFromRedis(projectId, docId) } catch (err) { diff --git a/services/web/test/unit/src/DocumentUpdater/DocumentUpdaterHandlerTests.js b/services/web/test/unit/src/DocumentUpdater/DocumentUpdaterHandlerTests.js index c54c8bc24a..820642d124 100644 --- a/services/web/test/unit/src/DocumentUpdater/DocumentUpdaterHandlerTests.js +++ b/services/web/test/unit/src/DocumentUpdater/DocumentUpdaterHandlerTests.js @@ -29,6 +29,7 @@ describe('DocumentUpdaterHandler', function () { }, }, } + this.source = 'dropbox' this.callback = sinon.stub() this.handler = SandboxedModule.require(modulePath, { @@ -359,10 +360,6 @@ describe('DocumentUpdaterHandler', function () { }) describe('setDocument', function () { - beforeEach(function () { - this.source = 'dropbox' - }) - describe('successfully', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 204 }, '') @@ -852,6 +849,7 @@ describe('DocumentUpdaterHandler', function () { this.projectHistoryId, this.user_id, {}, + this.source, this.callback ) }) @@ -909,6 +907,7 @@ describe('DocumentUpdaterHandler', function () { this.projectHistoryId, this.user_id, this.changes, + this.source, () => { this.request .calledWith({ @@ -919,6 +918,7 @@ describe('DocumentUpdaterHandler', function () { userId: this.user_id, version: this.version, projectHistoryId: this.projectHistoryId, + source: this.source, }, timeout: 30 * 1000, }) @@ -955,6 +955,7 @@ describe('DocumentUpdaterHandler', function () { this.projectHistoryId, this.user_id, this.changes, + this.source, () => { this.request .calledWith({ @@ -965,6 +966,7 @@ describe('DocumentUpdaterHandler', function () { userId: this.user_id, version: this.version, projectHistoryId: this.projectHistoryId, + source: this.source, }, timeout: 30 * 1000, }) @@ -1005,6 +1007,7 @@ describe('DocumentUpdaterHandler', function () { this.projectHistoryId, this.user_id, this.changes, + this.source, () => { this.request .calledWith({ @@ -1015,6 +1018,7 @@ describe('DocumentUpdaterHandler', function () { userId: this.user_id, version: this.version, projectHistoryId: this.projectHistoryId, + source: this.source, }, timeout: 30 * 1000, }) @@ -1049,6 +1053,7 @@ describe('DocumentUpdaterHandler', function () { this.projectHistoryId, this.user_id, this.changes, + this.source, () => { this.request .calledWith({ @@ -1059,6 +1064,7 @@ describe('DocumentUpdaterHandler', function () { userId: this.user_id, version: this.version, projectHistoryId: this.projectHistoryId, + source: this.source, }, timeout: 30 * 1000, }) @@ -1113,6 +1119,7 @@ describe('DocumentUpdaterHandler', function () { this.projectHistoryId, this.user_id, this.changes, + this.source, () => { this.request.should.have.been.calledWith({ url: this.url, @@ -1122,6 +1129,7 @@ describe('DocumentUpdaterHandler', function () { userId: this.user_id, version: this.version, projectHistoryId: this.projectHistoryId, + source: this.source, }, timeout: 30 * 1000, }) @@ -1145,6 +1153,7 @@ describe('DocumentUpdaterHandler', function () { this.projectHistoryId, this.user_id, this.changes, + this.source, this.callback ) diff --git a/services/web/test/unit/src/Editor/EditorControllerTests.js b/services/web/test/unit/src/Editor/EditorControllerTests.js index 2c98b29c5f..c52c82cc79 100644 --- a/services/web/test/unit/src/Editor/EditorControllerTests.js +++ b/services/web/test/unit/src/Editor/EditorControllerTests.js @@ -96,7 +96,9 @@ describe('EditorController', function () { this.folder_id, this.docName, this.docLines, - {} + {}, + this.user_id, + this.source ) .should.equal(true) }) @@ -144,7 +146,8 @@ describe('EditorController', function () { this.fileName, this.fsPath, this.linkedFileData, - this.user_id + this.user_id, + this.source ) .should.equal(true) }) @@ -256,7 +259,8 @@ describe('EditorController', function () { this.fileName, this.fsPath, this.linkedFileData, - this.user_id + this.user_id, + this.source ) .should.equal(true) }) @@ -414,7 +418,9 @@ describe('EditorController', function () { this.project_id, this.filePath, this.fsPath, - this.linkedFileData + this.linkedFileData, + this.user_id, + this.source ) .should.equal(true) }) @@ -583,7 +589,13 @@ describe('EditorController', function () { it('should delete the folder using the project entity handler', function () { return this.ProjectEntityUpdateHandler.deleteEntity - .calledWith(this.project_id, this.entity_id, this.type, this.user_id) + .calledWith( + this.project_id, + this.entity_id, + this.type, + this.user_id, + this.source + ) .should.equal(true) }) @@ -617,7 +629,7 @@ describe('EditorController', function () { it('should delete the folder using the project entity handler', function () { return this.ProjectEntityUpdateHandler.deleteEntityWithPath - .calledWith(this.project_id, this.path, this.user_id) + .calledWith(this.project_id, this.path, this.user_id, this.source) .should.equal(true) }) @@ -692,6 +704,7 @@ describe('EditorController', function () { this.entityType, this.newName, this.user_id, + this.source, done ) }) @@ -703,7 +716,8 @@ describe('EditorController', function () { this.entity_id, this.entityType, this.newName, - this.user_id + this.user_id, + this.source ) .should.equal(true) }) @@ -731,6 +745,7 @@ describe('EditorController', function () { this.folder_id, this.entityType, this.user_id, + this.source, this.callback ) }) @@ -742,7 +757,8 @@ describe('EditorController', function () { this.entity_id, this.folder_id, this.entityType, - this.user_id + this.user_id, + this.source ) .should.equal(true) }) diff --git a/services/web/test/unit/src/Editor/EditorHttpControllerTests.js b/services/web/test/unit/src/Editor/EditorHttpControllerTests.js index c9c74baeb6..07b79295da 100644 --- a/services/web/test/unit/src/Editor/EditorHttpControllerTests.js +++ b/services/web/test/unit/src/Editor/EditorHttpControllerTests.js @@ -34,6 +34,7 @@ describe('EditorHttpController', function () { this.doc = { _id: new ObjectId(), name: 'excellent-original-idea.tex' } this.file = { _id: new ObjectId() } this.folder = { _id: new ObjectId() } + this.source = 'editor' this.parentFolderId = 'mock-folder-id' this.req = { i18n: { translate: string => string } } @@ -405,7 +406,7 @@ describe('EditorHttpController', function () { describe('successfully', function () { beforeEach(function (done) { this.newName = 'new-name' - this.req.body = { name: this.newName } + this.req.body = { name: this.newName, source: this.source } this.res.sendStatus.callsFake(() => done()) this.EditorHttpController.renameEntity(this.req, this.res) }) @@ -418,7 +419,8 @@ describe('EditorHttpController', function () { this.entityId, this.entityType, this.newName, - this.user._id + this.user._id, + this.source ) }) @@ -429,7 +431,7 @@ describe('EditorHttpController', function () { describe('with long name', function () { beforeEach(function () { this.newName = 'long'.repeat(100) - this.req.body = { name: this.newName } + this.req.body = { name: this.newName, source: this.source } this.EditorHttpController.renameEntity(this.req, this.res) }) @@ -441,7 +443,7 @@ describe('EditorHttpController', function () { describe('with 0 length name', function () { beforeEach(function () { this.newName = '' - this.req.body = { name: this.newName } + this.req.body = { name: this.newName, source: this.source } this.EditorHttpController.renameEntity(this.req, this.res) }) @@ -461,7 +463,7 @@ describe('EditorHttpController', function () { entity_id: this.entityId, entity_type: this.entityType, } - this.req.body = { folder_id: this.folderId } + this.req.body = { folder_id: this.folderId, source: this.source } this.res.sendStatus.callsFake(() => done()) this.EditorHttpController.moveEntity(this.req, this.res) }) @@ -472,7 +474,8 @@ describe('EditorHttpController', function () { this.entityId, this.folderId, this.entityType, - this.user._id + this.user._id, + this.source ) }) diff --git a/services/web/test/unit/src/Project/ProjectDuplicatorTests.js b/services/web/test/unit/src/Project/ProjectDuplicatorTests.js index 0c286bdfc2..0f3b785873 100644 --- a/services/web/test/unit/src/Project/ProjectDuplicatorTests.js +++ b/services/web/test/unit/src/Project/ProjectDuplicatorTests.js @@ -303,7 +303,8 @@ describe('ProjectDuplicator', function () { newDocs: this.docEntries, newFiles: this.fileEntries, newProject: { version: this.newProjectVersion }, - } + }, + null ) }) diff --git a/services/web/test/unit/src/Project/ProjectEntityRestoreHandlerTests.js b/services/web/test/unit/src/Project/ProjectEntityRestoreHandlerTests.js index 7a8a863cad..c41d61c60a 100644 --- a/services/web/test/unit/src/Project/ProjectEntityRestoreHandlerTests.js +++ b/services/web/test/unit/src/Project/ProjectEntityRestoreHandlerTests.js @@ -18,6 +18,7 @@ describe('ProjectEntityRestoreHandler', function () { } this.docId = '4eecb1c1bffa66588e0000a2' + this.DocModel = class Doc { constructor(options) { this.name = options.name diff --git a/services/web/test/unit/src/Project/ProjectEntityUpdateHandlerTests.js b/services/web/test/unit/src/Project/ProjectEntityUpdateHandlerTests.js index 8f5ae8d8fc..3fdaf0ca85 100644 --- a/services/web/test/unit/src/Project/ProjectEntityUpdateHandlerTests.js +++ b/services/web/test/unit/src/Project/ProjectEntityUpdateHandlerTests.js @@ -531,6 +531,7 @@ describe('ProjectEntityUpdateHandler', function () { this.docName, this.docLines, userId, + this.source, this.callback ) }) @@ -550,10 +551,16 @@ describe('ProjectEntityUpdateHandler', function () { }, ] this.DocumentUpdaterHandler.updateProjectStructure - .calledWith(projectId, projectHistoryId, userId, { - newDocs, - newProject: this.project, - }) + .calledWith( + projectId, + projectHistoryId, + userId, + { + newDocs, + newProject: this.project, + }, + this.source + ) .should.equal(true) }) }) @@ -569,6 +576,7 @@ describe('ProjectEntityUpdateHandler', function () { `*${this.docName}`, this.docLines, userId, + this.source, this.callback ) }) @@ -609,6 +617,7 @@ describe('ProjectEntityUpdateHandler', function () { this.fileSystemPath, this.linkedFileData, userId, + this.source, this.callback ) }) @@ -661,10 +670,16 @@ describe('ProjectEntityUpdateHandler', function () { }, ] this.DocumentUpdaterHandler.updateProjectStructure - .calledWith(projectId, projectHistoryId, userId, { - newFiles, - newProject: this.project, - }) + .calledWith( + projectId, + projectHistoryId, + userId, + { + newFiles, + newProject: this.project, + }, + this.source + ) .should.equal(true) }) }) @@ -692,6 +707,7 @@ describe('ProjectEntityUpdateHandler', function () { this.fileSystemPath, this.linkedFileData, userId, + this.source, this.callback ) }) @@ -735,6 +751,7 @@ describe('ProjectEntityUpdateHandler', function () { this.fileSystemPath, this.linkedFileData, userId, + this.source, this.callback ) }) @@ -792,11 +809,17 @@ describe('ProjectEntityUpdateHandler', function () { }, ] this.DocumentUpdaterHandler.updateProjectStructure - .calledWith(projectId, projectHistoryId, userId, { - oldFiles, - newFiles, - newProject: this.newProject, - }) + .calledWith( + projectId, + projectHistoryId, + userId, + { + oldFiles, + newFiles, + newProject: this.newProject, + }, + this.source + ) .should.equal(true) }) }) @@ -916,7 +939,8 @@ describe('ProjectEntityUpdateHandler', function () { this.docName, this.docLines, {}, - userId + userId, + this.source ) .should.equal(true) }) @@ -1035,11 +1059,17 @@ describe('ProjectEntityUpdateHandler', function () { ] expect( this.DocumentUpdaterHandler.updateProjectStructure - ).to.have.been.calledWith(projectId, projectHistoryId, userId, { - oldFiles, - newDocs, - newProject: this.newProject, - }) + ).to.have.been.calledWith( + projectId, + projectHistoryId, + userId, + { + oldFiles, + newDocs, + newProject: this.newProject, + }, + this.source + ) }) it('should notify everyone of the file deletion', function () { @@ -1074,6 +1104,7 @@ describe('ProjectEntityUpdateHandler', function () { this.fileSystemPath, this.linkedFileData, userId, + this.source, this.callback ) }) @@ -1100,6 +1131,7 @@ describe('ProjectEntityUpdateHandler', function () { this.fileSystemPath, this.linkedFileData, userId, + this.source, this.callback ) }) @@ -1112,7 +1144,10 @@ describe('ProjectEntityUpdateHandler', function () { fileId, this.fileSystemPath, this.linkedFileData, - userId + userId, + this.newFile, + this.fileUrl, + this.source ) }) @@ -1126,6 +1161,11 @@ describe('ProjectEntityUpdateHandler', function () { this.folder = { _id: folderId, fileRefs: [], docs: [] } this.newFile = { _id: fileId } this.ProjectLocator.findElement.yields(null, this.folder) + this.FileStoreHandler.uploadFileFromDisk.yields( + null, + this.fileUrl, + this.newFile + ) this.ProjectEntityUpdateHandler.addFile = { mainTask: sinon.stub().yields(null, this.newFile), } @@ -1137,6 +1177,7 @@ describe('ProjectEntityUpdateHandler', function () { this.fileSystemPath, this.linkedFileData, userId, + this.source, this.callback ) }) @@ -1152,16 +1193,19 @@ describe('ProjectEntityUpdateHandler', function () { }) it('adds the file', function () { - this.ProjectEntityUpdateHandler.addFile.mainTask - .calledWith( - projectId, - folderId, - this.fileName, - this.fileSystemPath, - this.linkedFileData, - userId - ) - .should.equal(true) + expect( + this.ProjectEntityUpdateHandler.addFile.mainTask + ).to.have.been.calledWith( + projectId, + folderId, + this.fileName, + this.fileSystemPath, + this.linkedFileData, + userId, + this.newFile, + this.fileUrl, + this.source + ) }) it('returns the file', function () { @@ -1185,6 +1229,7 @@ describe('ProjectEntityUpdateHandler', function () { this.fileSystemPath, this.linkedFileData, userId, + this.source, this.callback ) }) @@ -1247,6 +1292,7 @@ describe('ProjectEntityUpdateHandler', function () { this.fileSystemPath, this.linkedFileData, userId, + this.source, done ) }) @@ -1278,7 +1324,13 @@ describe('ProjectEntityUpdateHandler', function () { } expect( this.DocumentUpdaterHandler.updateProjectStructure - ).to.have.been.calledWith(projectId, projectHistoryId, userId, updates) + ).to.have.been.calledWith( + projectId, + projectHistoryId, + userId, + updates, + this.source + ) }) it('tells everyone in the room the doc is removed', function () { @@ -1438,6 +1490,7 @@ describe('ProjectEntityUpdateHandler', function () { this.fileSystemPath, this.linkedFileData, userId, + this.source, this.callback ) }) @@ -1456,7 +1509,10 @@ describe('ProjectEntityUpdateHandler', function () { 'file.png', this.fileSystemPath, this.linkedFileData, - userId + userId, + this.newFile, + this.fileUrl, + this.source ) .should.equal(true) }) @@ -1495,6 +1551,7 @@ describe('ProjectEntityUpdateHandler', function () { this.fileSystemPath, this.linkedFileData, userId, + this.source, this.callback ) }) @@ -1525,6 +1582,7 @@ describe('ProjectEntityUpdateHandler', function () { this.fileSystemPath, this.linkedFileData, userId, + this.source, this.callback ) }) @@ -1556,6 +1614,7 @@ describe('ProjectEntityUpdateHandler', function () { docId, 'doc', userId, + this.source, this.callback ) }) @@ -1574,7 +1633,8 @@ describe('ProjectEntityUpdateHandler', function () { this.doc, 'doc', this.path, - userId + userId, + this.source ) .should.equal(true) }) @@ -1607,6 +1667,7 @@ describe('ProjectEntityUpdateHandler', function () { projectId, this.path, userId, + this.source, this.callback ) }) @@ -1619,7 +1680,14 @@ describe('ProjectEntityUpdateHandler', function () { it('deletes the entity', function () { this.ProjectEntityUpdateHandler.deleteEntity.withoutLock - .calledWith(projectId, this.doc._id, 'doc', userId, this.callback) + .calledWith( + projectId, + this.doc._id, + 'doc', + userId, + this.source, + this.callback + ) .should.equal(true) }) }) @@ -1632,6 +1700,7 @@ describe('ProjectEntityUpdateHandler', function () { projectId, this.path, userId, + this.source, this.callback ) }) @@ -1743,6 +1812,7 @@ describe('ProjectEntityUpdateHandler', function () { folderId, 'doc', userId, + this.source, this.callback ) }) @@ -1772,6 +1842,7 @@ describe('ProjectEntityUpdateHandler', function () { projectHistoryId, userId, this.changes, + this.source, this.callback ) .should.equal(true) @@ -1802,6 +1873,7 @@ describe('ProjectEntityUpdateHandler', function () { 'doc', this.newDocName, userId, + this.source, this.callback ) }) @@ -1831,6 +1903,7 @@ describe('ProjectEntityUpdateHandler', function () { projectHistoryId, userId, this.changes, + this.source, this.callback ) .should.equal(true) @@ -1860,6 +1933,7 @@ describe('ProjectEntityUpdateHandler', function () { 'doc', this.newDocName, userId, + this.source, this.callback ) }) @@ -2315,6 +2389,7 @@ describe('ProjectEntityUpdateHandler', function () { 'file', this.path, userId, + this.source, done ) }) @@ -2338,10 +2413,16 @@ describe('ProjectEntityUpdateHandler', function () { it('should should send the update to the doc updater', function () { const oldFiles = [{ file: this.entity, path: this.path }] this.DocumentUpdaterHandler.updateProjectStructure - .calledWith(projectId, projectHistoryId, userId, { - oldFiles, - newProject: this.newProject, - }) + .calledWith( + projectId, + projectHistoryId, + userId, + { + oldFiles, + newProject: this.newProject, + }, + this.source + ) .should.equal(true) }) }) @@ -2359,6 +2440,7 @@ describe('ProjectEntityUpdateHandler', function () { 'doc', this.path, userId, + this.source, done ) }) @@ -2372,10 +2454,16 @@ describe('ProjectEntityUpdateHandler', function () { it('should should send the update to the doc updater', function () { const oldDocs = [{ doc: this.entity, path: this.path }] this.DocumentUpdaterHandler.updateProjectStructure - .calledWith(projectId, projectHistoryId, userId, { - oldDocs, - newProject: this.newProject, - }) + .calledWith( + projectId, + projectHistoryId, + userId, + { + oldDocs, + newProject: this.newProject, + }, + this.source + ) .should.equal(true) }) }) @@ -2408,6 +2496,7 @@ describe('ProjectEntityUpdateHandler', function () { 'folder', path, userId, + this.source, done ) }) @@ -2450,11 +2539,17 @@ describe('ProjectEntityUpdateHandler', function () { { doc: this.doc1, path: '/folder/subfolder/doc-name-1' }, ] this.DocumentUpdaterHandler.updateProjectStructure - .calledWith(projectId, projectHistoryId, userId, { - oldFiles, - oldDocs, - newProject: this.newProject, - }) + .calledWith( + projectId, + projectHistoryId, + userId, + { + oldFiles, + oldDocs, + newProject: this.newProject, + }, + this.source + ) .should.equal(true) }) }) @@ -2571,6 +2666,7 @@ describe('ProjectEntityUpdateHandler', function () { this.project._id, this.doc._id, this.user._id, + this.source, done ) }) @@ -2615,7 +2711,8 @@ describe('ProjectEntityUpdateHandler', function () { { file: this.file, path: this.path, url: this.fileStoreUrl }, ], newProject: this.project, - } + }, + this.source ) }) @@ -2654,6 +2751,7 @@ describe('ProjectEntityUpdateHandler', function () { this.project._id, this.doc._id, this.user._id, + this.source, err => { expect(err).to.be.instanceof(Errors.DocHasRangesError) done() diff --git a/services/web/test/unit/src/Uploads/ProjectUploadManagerTests.js b/services/web/test/unit/src/Uploads/ProjectUploadManagerTests.js index 233095fd44..a2f9e522a5 100644 --- a/services/web/test/unit/src/Uploads/ProjectUploadManagerTests.js +++ b/services/web/test/unit/src/Uploads/ProjectUploadManagerTests.js @@ -211,7 +211,8 @@ describe('ProjectUploadManager', function () { newDocs: this.docEntries, newFiles: this.fileEntries, newProject: { version: this.newProjectVersion }, - } + }, + null ) }) @@ -298,7 +299,8 @@ describe('ProjectUploadManager', function () { newDocs: this.docEntries, newFiles: this.fileEntries, newProject: { version: this.newProjectVersion }, - } + }, + null ) })