Merge pull request #2618 from overleaf/ew-clear-root-doc-on-delete

Clear root doc on delete

GitOrigin-RevId: 4121d198f5253417bca2284c5f750c088debcb8c
This commit is contained in:
Eric Mc Sween 2020-02-27 07:46:37 -05:00 committed by Copybot
parent 17969c50ce
commit 1da929fcdb
4 changed files with 154 additions and 60 deletions

View file

@ -10,7 +10,6 @@
/* /*
* decaffeinate suggestions: * decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns * DS102: Remove unnecessary code created because of implicit returns
* DS103: Rewrite code to no longer use __guard__
* DS207: Consider shorter variations of null checks * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
@ -35,59 +34,52 @@ const COMPILE_TIMEOUT_MS = 10 * 60 * 1000
module.exports = CompileController = { module.exports = CompileController = {
compile(req, res, next) { compile(req, res, next) {
if (next == null) {
next = function(error) {}
}
res.setTimeout(COMPILE_TIMEOUT_MS) res.setTimeout(COMPILE_TIMEOUT_MS)
const project_id = req.params.Project_id const project_id = req.params.Project_id
const isAutoCompile = !!(req.query != null const isAutoCompile = !!req.query.auto_compile
? req.query.auto_compile
: undefined)
const user_id = AuthenticationController.getLoggedInUserId(req) const user_id = AuthenticationController.getLoggedInUserId(req)
const options = { const options = {
isAutoCompile isAutoCompile
} }
if ((req.body != null ? req.body.rootDoc_id : undefined) != null) {
if (req.body.rootDoc_id) {
options.rootDoc_id = req.body.rootDoc_id options.rootDoc_id = req.body.rootDoc_id
} else if ( } else if (
__guard__( req.body.settingsOverride &&
req.body != null ? req.body.settingsOverride : undefined, req.body.settingsOverride.rootDoc_id
x => x.rootDoc_id
) != null
) { ) {
// Can be removed after deploy // Can be removed after deploy
options.rootDoc_id = req.body.settingsOverride.rootDoc_id options.rootDoc_id = req.body.settingsOverride.rootDoc_id
} }
if (req.body != null ? req.body.compiler : undefined) { if (req.body.compiler) {
options.compiler = req.body.compiler options.compiler = req.body.compiler
} }
if (req.body != null ? req.body.draft : undefined) { if (req.body.draft) {
options.draft = req.body.draft options.draft = req.body.draft
} }
if ( if (['validate', 'error', 'silent'].includes(req.body.check)) {
['validate', 'error', 'silent'].includes(
req.body != null ? req.body.check : undefined
)
) {
options.check = req.body.check options.check = req.body.check
} }
if (req.body != null ? req.body.incrementalCompilesEnabled : undefined) { if (req.body.incrementalCompilesEnabled) {
options.incrementalCompilesEnabled = true options.incrementalCompilesEnabled = true
} }
return CompileManager.compile(project_id, user_id, options, function(
error, CompileManager.compile(
status, project_id,
outputFiles, user_id,
clsiServerId, options,
limits, (
validationProblems error,
) { status,
if (error != null) { outputFiles,
return next(error) clsiServerId,
} limits,
res.contentType('application/json') validationProblems
return res.status(200).send( ) => {
JSON.stringify({ if (error) {
return next(error)
}
res.json({
status, status,
outputFiles, outputFiles,
compileGroup: limits != null ? limits.compileGroup : undefined, compileGroup: limits != null ? limits.compileGroup : undefined,
@ -95,8 +87,8 @@ module.exports = CompileController = {
validationProblems, validationProblems,
pdfDownloadDomain: Settings.pdfDownloadDomain pdfDownloadDomain: Settings.pdfDownloadDomain
}) })
) }
}) )
}, },
stopCompile(req, res, next) { stopCompile(req, res, next) {
@ -540,9 +532,3 @@ module.exports = CompileController = {
}) })
} }
} }
function __guard__(value, transform) {
return typeof value !== 'undefined' && value !== null
? transform(value)
: undefined
}

View file

@ -314,8 +314,12 @@ async function moveEntity(projectId, entityId, destFolderId, entityType) {
async function deleteEntity(projectId, entityId, entityType, callback) { async function deleteEntity(projectId, entityId, entityType, callback) {
const project = await ProjectGetter.promises.getProjectWithoutLock( const project = await ProjectGetter.promises.getProjectWithoutLock(
projectId, projectId,
{ name: true, rootFolder: true, overleaf: true } { name: true, rootFolder: true, overleaf: true, rootDoc_id: true }
) )
const deleteRootDoc =
project.rootDoc_id &&
entityId &&
project.rootDoc_id.toString() === entityId.toString()
const { element: entity, path } = await ProjectLocator.promises.findElement({ const { element: entity, path } = await ProjectLocator.promises.findElement({
project, project,
element_id: entityId, element_id: entityId,
@ -325,7 +329,8 @@ async function deleteEntity(projectId, entityId, entityType, callback) {
Project, Project,
projectId, projectId,
path.mongo, path.mongo,
entityId entityId,
deleteRootDoc
) )
return { entity, path, projectBeforeDeletion: project, newProject } return { entity, path, projectBeforeDeletion: project, newProject }
} }
@ -414,19 +419,24 @@ async function _insertDeletedFileReference(projectId, fileRef) {
).exec() ).exec()
} }
async function _removeElementFromMongoArray(model, modelId, path, elementId) { async function _removeElementFromMongoArray(
model,
modelId,
path,
elementId,
deleteRootDoc = false
) {
const nonArrayPath = path.slice(0, path.lastIndexOf('.')) const nonArrayPath = path.slice(0, path.lastIndexOf('.'))
const newDoc = model const options = { new: true }
.findOneAndUpdate( const query = { _id: modelId }
{ _id: modelId }, const update = {
{ $pull: { [nonArrayPath]: { _id: elementId } },
$pull: { [nonArrayPath]: { _id: elementId } }, $inc: { version: 1 }
$inc: { version: 1 } }
}, if (deleteRootDoc) {
{ new: true } update.$unset = { rootDoc_id: 1 }
) }
.exec() return model.findOneAndUpdate(query, update, options).exec()
return newDoc
} }
function _countElements(project) { function _countElements(project) {

View file

@ -6,6 +6,7 @@ const fs = require('fs')
const Settings = require('settings-sharelatex') const Settings = require('settings-sharelatex')
const _ = require('underscore') const _ = require('underscore')
const { Project } = require('../../../app/src/models/Project')
const ProjectGetter = require('../../../app/src/Features/Project/ProjectGetter.js') const ProjectGetter = require('../../../app/src/Features/Project/ProjectGetter.js')
const MockDocUpdaterApi = require('./helpers/MockDocUpdaterApi') const MockDocUpdaterApi = require('./helpers/MockDocUpdaterApi')
@ -1098,6 +1099,105 @@ describe('ProjectStructureChanges', function() {
}) })
}) })
describe('deleting docs', function() {
beforeEach(function(done) {
createExampleProject(owner, (err, projectId) => {
if (err) {
return done(err)
}
this.exampleProjectId = projectId
createExampleFolder(owner, projectId, (err, folderId) => {
if (err) {
return done(err)
}
this.exampleFolderId = folderId
createExampleDoc(owner, projectId, (err, docId) => {
if (err) {
return done(err)
}
this.exampleDocId = docId
MockDocUpdaterApi.clearProjectStructureUpdates()
ProjectGetter.getProject(
this.exampleProjectId,
(error, project) => {
if (error) {
throw error
}
this.project0 = project
done()
}
)
})
})
})
})
describe('when rootDoc_id matches doc being deleted', function() {
beforeEach(function(done) {
Project.update(
{ _id: this.exampleProjectId },
{ $set: { rootDoc_id: this.exampleDocId } },
done
)
})
it('should clear rootDoc_id', function(done) {
deleteItem(
owner,
this.exampleProjectId,
'doc',
this.exampleDocId,
() => {
ProjectGetter.getProject(
this.exampleProjectId,
(error, project) => {
if (error) {
throw error
}
expect(project.rootDoc_id).to.be.undefined
done()
}
)
}
)
})
})
describe('when rootDoc_id does not match doc being deleted', function() {
beforeEach(function(done) {
this.exampleRootDocId = new ObjectId()
Project.update(
{ _id: this.exampleProjectId },
{ $set: { rootDoc_id: this.exampleRootDocId } },
done
)
})
it('should not clear rootDoc_id', function(done) {
deleteItem(
owner,
this.exampleProjectId,
'doc',
this.exampleDocId,
() => {
ProjectGetter.getProject(
this.exampleProjectId,
(error, project) => {
if (error) {
throw error
}
expect(project.rootDoc_id.toString()).to.equal(
this.exampleRootDocId.toString()
)
done()
}
)
}
)
})
})
})
describe('tpds', function() { describe('tpds', function() {
let projectName, exampleProjectId, oldVersion, rootFolderId let projectName, exampleProjectId, oldVersion, rootFolderId

View file

@ -120,14 +120,12 @@ describe('CompileController', function() {
}) })
it('should set the content-type of the response to application/json', function() { it('should set the content-type of the response to application/json', function() {
return this.res.contentType this.res.type.should.equal('application/json')
.calledWith('application/json')
.should.equal(true)
}) })
it('should send a successful response reporting the status and files', function() { it('should send a successful response reporting the status and files', function() {
this.res.statusCode.should.equal(200) this.res.statusCode.should.equal(200)
return this.res.body.should.equal( this.res.body.should.equal(
JSON.stringify({ JSON.stringify({
status: this.status, status: this.status,
outputFiles: this.outputFiles outputFiles: this.outputFiles