mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #3754 from overleaf/jpa-project-restore-handle-deleted-docs
[ProjectDeleter] restore project.deletedDocs meta data into docstore GitOrigin-RevId: 570543d2cb9c5c790ac49328382ed88ef6ac3129
This commit is contained in:
parent
9692392bf8
commit
76b1cdff51
6 changed files with 106 additions and 3 deletions
|
@ -22,12 +22,12 @@ const { promisifyAll } = require('../../util/promises')
|
|||
const TIMEOUT = 30 * 1000 // request timeout
|
||||
|
||||
const DocstoreManager = {
|
||||
deleteDoc(project_id, doc_id, name, callback) {
|
||||
deleteDoc(project_id, doc_id, name, deletedAt, callback) {
|
||||
if (callback == null) {
|
||||
callback = function(error) {}
|
||||
}
|
||||
const url = `${settings.apis.docstore.url}/project/${project_id}/doc/${doc_id}`
|
||||
const docMetaData = { deleted: true, deletedAt: new Date(), name }
|
||||
const docMetaData = { deleted: true, deletedAt, name }
|
||||
const options = { url, json: docMetaData, timeout: TIMEOUT }
|
||||
request.patch(options, function(error, res) {
|
||||
if (error != null) {
|
||||
|
|
|
@ -303,6 +303,19 @@ async function undeleteProject(projectId, options = {}) {
|
|||
)
|
||||
restored.archived = undefined
|
||||
|
||||
if (restored.deletedDocs && restored.deletedDocs.length > 0) {
|
||||
await promiseMapWithLimit(10, restored.deletedDocs, async deletedDoc => {
|
||||
// back fill context of deleted docs
|
||||
const { _id: docId, name, deletedAt } = deletedDoc
|
||||
await DocstoreManager.promises.deleteDoc(
|
||||
projectId,
|
||||
docId,
|
||||
name,
|
||||
deletedAt
|
||||
)
|
||||
})
|
||||
restored.deletedDocs = []
|
||||
}
|
||||
if (restored.deletedFiles && restored.deletedFiles.length > 0) {
|
||||
filterDuplicateDeletedFilesInPlace(restored)
|
||||
const deletedFiles = restored.deletedFiles.map(file => {
|
||||
|
|
|
@ -1507,7 +1507,9 @@ const ProjectEntityUpdateHandler = {
|
|||
if (error != null) {
|
||||
return callback(error)
|
||||
}
|
||||
DocstoreManager.deleteDoc(projectId, docId, doc.name, error => {
|
||||
const { name } = doc
|
||||
const deletedAt = new Date()
|
||||
DocstoreManager.deleteDoc(projectId, docId, name, deletedAt, error => {
|
||||
if (error) {
|
||||
return callback(error)
|
||||
}
|
||||
|
|
|
@ -502,4 +502,77 @@ describe('Deleting a project', function() {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the deleted project has deletedDocs', function() {
|
||||
beforeEach('delete project', function(done) {
|
||||
this.user.deleteProject(this.projectId, done)
|
||||
})
|
||||
|
||||
let deletedDocs
|
||||
beforeEach('set deletedDocs', function() {
|
||||
deletedDocs = [
|
||||
{ _id: ObjectId(), name: 'foo.tex', deletedAt: new Date() },
|
||||
{ _id: ObjectId(), name: 'bar.tex', deletedAt: new Date() }
|
||||
]
|
||||
deletedDocs.forEach(doc => {
|
||||
MockDocstoreApi.createLegacyDeletedDoc(
|
||||
this.projectId,
|
||||
doc._id.toString()
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach('insert deletedDocs', async function() {
|
||||
await db.deletedProjects.updateOne(
|
||||
{ 'deleterData.deletedProjectId': ObjectId(this.projectId) },
|
||||
{ $set: { 'project.deletedDocs': deletedDocs } }
|
||||
)
|
||||
})
|
||||
|
||||
it('should not see any doc names before', async function() {
|
||||
const docs = MockDocstoreApi.getDeletedDocs(this.projectId)
|
||||
expect(docs).to.deep.equal(
|
||||
deletedDocs.map(doc => {
|
||||
const { _id } = doc
|
||||
return { _id: _id.toString(), name: undefined }
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
describe('when undeleting the project', function() {
|
||||
let admin
|
||||
beforeEach('create admin', function(done) {
|
||||
admin = new User()
|
||||
async.series(
|
||||
[
|
||||
cb => admin.ensureUserExists(cb),
|
||||
cb => admin.ensureAdmin(cb),
|
||||
cb => admin.login(cb)
|
||||
],
|
||||
done
|
||||
)
|
||||
})
|
||||
beforeEach('undelete project', function(done) {
|
||||
admin.undeleteProject(this.projectId, done)
|
||||
})
|
||||
|
||||
it('should not insert deletedDocs into the projects collection', function(done) {
|
||||
this.user.getProject(this.projectId, (error, project) => {
|
||||
if (error) return done(error)
|
||||
expect(project.deletedDocs).to.deep.equal([])
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should back fill deleted docs context', async function() {
|
||||
const docs = MockDocstoreApi.getDeletedDocs(this.projectId)
|
||||
expect(docs).to.deep.equal(
|
||||
deletedDocs.map(doc => {
|
||||
const { _id, name } = doc
|
||||
return { _id: _id.toString(), name }
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -5,6 +5,18 @@ class MockDocstoreApi extends AbstractMockApi {
|
|||
this.docs = {}
|
||||
}
|
||||
|
||||
createLegacyDeletedDoc(projectId, docId) {
|
||||
if (!this.docs[projectId]) {
|
||||
this.docs[projectId] = {}
|
||||
}
|
||||
this.docs[projectId][docId] = {
|
||||
lines: [],
|
||||
version: 1,
|
||||
ranges: {},
|
||||
deleted: true
|
||||
}
|
||||
}
|
||||
|
||||
getDeletedDocs(projectId) {
|
||||
return Object.entries(this.docs[projectId] || {})
|
||||
.filter(([_, doc]) => doc.deleted)
|
||||
|
|
|
@ -58,6 +58,7 @@ describe('DocstoreManager', function() {
|
|||
this.project_id,
|
||||
this.doc_id,
|
||||
'wombat.tex',
|
||||
new Date(),
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
@ -86,6 +87,7 @@ describe('DocstoreManager', function() {
|
|||
this.project_id,
|
||||
this.doc_id,
|
||||
'main.tex',
|
||||
new Date(),
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
@ -115,6 +117,7 @@ describe('DocstoreManager', function() {
|
|||
this.project_id,
|
||||
this.doc_id,
|
||||
'main.tex',
|
||||
new Date(),
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue