Merge pull request #9383 from overleaf/em-file-tree-histories

Track source for all file tree operations

GitOrigin-RevId: ff95ea8e99bfa30203a2a42968519bbaba65e708
This commit is contained in:
Eric Mc Sween 2022-08-25 08:01:39 -04:00 committed by Copybot
parent 2432b265eb
commit 8b2f8ce243
24 changed files with 666 additions and 527 deletions

View file

@ -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) {

View file

@ -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)
},
}

View file

@ -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)

View file

@ -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)
})

View file

@ -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 () {

View file

@ -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
)
})

View file

@ -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',
},

View file

@ -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()
},
}

View file

@ -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)
}

View file

@ -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) {

View file

@ -205,6 +205,7 @@ async function _notifyDocumentUpdater(project, userId, changes) {
project._id,
projectHistoryId,
userId,
changes
changes,
null
)
}

View file

@ -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)

View file

@ -204,6 +204,7 @@ async function _notifyDocumentUpdater(project, userId, changes) {
project._id,
projectHistoryId,
userId,
changes
changes,
null
)
}

View file

@ -21,7 +21,8 @@ async function main() {
await ProjectEntityUpdateHandler.promises.convertDocToFile(
projectId,
docId,
userId
userId,
null
)
} catch (err) {
if (err instanceof Errors.NotFoundError) {

View file

@ -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
)
}

View file

@ -304,7 +304,8 @@ async function convertLargeDocsToFile(projectId, userId) {
await ProjectEntityUpdateHandler.promises.convertDocToFile(
projectId,
doc._id,
userId
userId,
null
)
convertedDocCount++
}

View file

@ -123,7 +123,8 @@ async function processDoc(docId) {
restoredName,
doc.lines,
doc.ranges,
null
null,
'recovery-script'
)
await deleteDocFromRedis(projectId, docId)
} catch (err) {

View file

@ -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
)

View file

@ -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)
})

View file

@ -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
)
})

View file

@ -303,7 +303,8 @@ describe('ProjectDuplicator', function () {
newDocs: this.docEntries,
newFiles: this.fileEntries,
newProject: { version: this.newProjectVersion },
}
},
null
)
})

View file

@ -18,6 +18,7 @@ describe('ProjectEntityRestoreHandler', function () {
}
this.docId = '4eecb1c1bffa66588e0000a2'
this.DocModel = class Doc {
constructor(options) {
this.name = options.name

View file

@ -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()

View file

@ -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
)
})