mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Initialise full project history for old projects when project opened (#4687)
* Initialise full project history for old projects when project opened This begins a second attempt at initialising the full project history in the background for projects without a full project history id. The original web-internal#4345 was reverted in web-internal#4353. This commit reverts the revert, and adds an additional flush of the project before initialising full project history. GitOrigin-RevId: ac263dca8cf0d80186fee916a76e5572ec5649d4
This commit is contained in:
parent
3ace29999b
commit
7517a818b2
6 changed files with 104 additions and 30 deletions
|
@ -8,12 +8,14 @@ module.exports = {
|
||||||
initializeProject: callbackify(initializeProject),
|
initializeProject: callbackify(initializeProject),
|
||||||
flushProject: callbackify(flushProject),
|
flushProject: callbackify(flushProject),
|
||||||
resyncProject: callbackify(resyncProject),
|
resyncProject: callbackify(resyncProject),
|
||||||
|
forceResyncProject: callbackify(forceResyncProject),
|
||||||
deleteProject: callbackify(deleteProject),
|
deleteProject: callbackify(deleteProject),
|
||||||
injectUserDetails: callbackify(injectUserDetails),
|
injectUserDetails: callbackify(injectUserDetails),
|
||||||
promises: {
|
promises: {
|
||||||
initializeProject,
|
initializeProject,
|
||||||
flushProject,
|
flushProject,
|
||||||
resyncProject,
|
resyncProject,
|
||||||
|
forceResyncProject,
|
||||||
deleteProject,
|
deleteProject,
|
||||||
injectUserDetails,
|
injectUserDetails,
|
||||||
},
|
},
|
||||||
|
@ -65,6 +67,18 @@ async function resyncProject(projectId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function forceResyncProject(projectId) {
|
||||||
|
try {
|
||||||
|
await request.post({
|
||||||
|
url: `${settings.apis.project_history.url}/project/${projectId}/resync?force=true`,
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
throw OError.tag(err, 'failed to force resync project history', {
|
||||||
|
projectId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function deleteProject(projectId, historyId) {
|
async function deleteProject(projectId, historyId) {
|
||||||
try {
|
try {
|
||||||
const tasks = [
|
const tasks = [
|
||||||
|
|
|
@ -9,6 +9,7 @@ const { ObjectId } = require('mongodb')
|
||||||
const ProjectDeleter = require('./ProjectDeleter')
|
const ProjectDeleter = require('./ProjectDeleter')
|
||||||
const ProjectDuplicator = require('./ProjectDuplicator')
|
const ProjectDuplicator = require('./ProjectDuplicator')
|
||||||
const ProjectCreationHandler = require('./ProjectCreationHandler')
|
const ProjectCreationHandler = require('./ProjectCreationHandler')
|
||||||
|
const ProjectHistoryHandler = require('./ProjectHistoryHandler')
|
||||||
const EditorController = require('../Editor/EditorController')
|
const EditorController = require('../Editor/EditorController')
|
||||||
const ProjectHelper = require('./ProjectHelper')
|
const ProjectHelper = require('./ProjectHelper')
|
||||||
const metrics = require('@overleaf/metrics')
|
const metrics = require('@overleaf/metrics')
|
||||||
|
@ -680,6 +681,24 @@ const ProjectController = {
|
||||||
activate(cb) {
|
activate(cb) {
|
||||||
InactiveProjectManager.reactivateProjectIfRequired(projectId, cb)
|
InactiveProjectManager.reactivateProjectIfRequired(projectId, cb)
|
||||||
},
|
},
|
||||||
|
ensureHistoryExists(cb) {
|
||||||
|
// enable full project history in background for older projects
|
||||||
|
if (!Settings.apis.project_history || !Features.hasFeature('saas')) {
|
||||||
|
return cb()
|
||||||
|
}
|
||||||
|
ProjectHistoryHandler.ensureHistoryExistsForProject(
|
||||||
|
projectId,
|
||||||
|
err => {
|
||||||
|
if (err) {
|
||||||
|
logger.error(
|
||||||
|
{ err, projectId },
|
||||||
|
'error ensuring history exists for project'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
cb()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
markAsOpened(cb) {
|
markAsOpened(cb) {
|
||||||
// don't need to wait for this to complete
|
// don't need to wait for this to complete
|
||||||
ProjectUpdateHandler.markAsOpened(projectId, () => {})
|
ProjectUpdateHandler.markAsOpened(projectId, () => {})
|
||||||
|
|
|
@ -19,6 +19,7 @@ const logger = require('logger-sharelatex')
|
||||||
const settings = require('@overleaf/settings')
|
const settings = require('@overleaf/settings')
|
||||||
const HistoryManager = require('../History/HistoryManager')
|
const HistoryManager = require('../History/HistoryManager')
|
||||||
const ProjectEntityUpdateHandler = require('./ProjectEntityUpdateHandler')
|
const ProjectEntityUpdateHandler = require('./ProjectEntityUpdateHandler')
|
||||||
|
const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
|
||||||
const { promisifyAll } = require('../../util/promises')
|
const { promisifyAll } = require('../../util/promises')
|
||||||
|
|
||||||
const ProjectHistoryHandler = {
|
const ProjectHistoryHandler = {
|
||||||
|
@ -134,31 +135,44 @@ const ProjectHistoryHandler = {
|
||||||
if (history_id != null) {
|
if (history_id != null) {
|
||||||
return callback()
|
return callback()
|
||||||
} // history already exists, success
|
} // history already exists, success
|
||||||
return HistoryManager.initializeProject(function (err, history) {
|
return HistoryManager.flushProject(project_id, function (err) {
|
||||||
if (err != null) {
|
if (err != null) {
|
||||||
return callback(err)
|
return callback(err)
|
||||||
}
|
}
|
||||||
if (!(history != null ? history.overleaf_id : undefined)) {
|
return HistoryManager.initializeProject(function (err, history) {
|
||||||
return callback(new Error('failed to initialize history id'))
|
if (err != null) {
|
||||||
}
|
return callback(err)
|
||||||
return ProjectHistoryHandler.setHistoryId(
|
|
||||||
project_id,
|
|
||||||
history.overleaf_id,
|
|
||||||
function (err) {
|
|
||||||
if (err != null) {
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
return ProjectEntityUpdateHandler.resyncProjectHistory(
|
|
||||||
project_id,
|
|
||||||
function (err) {
|
|
||||||
if (err != null) {
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
return HistoryManager.flushProject(project_id, callback)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
if (!history || !history.overleaf_id) {
|
||||||
|
return callback(new Error('failed to initialize history id'))
|
||||||
|
}
|
||||||
|
return ProjectHistoryHandler.setHistoryId(
|
||||||
|
project_id,
|
||||||
|
history.overleaf_id,
|
||||||
|
function (err) {
|
||||||
|
if (err != null) {
|
||||||
|
return callback(err)
|
||||||
|
}
|
||||||
|
return DocumentUpdaterHandler.flushProjectToMongoAndDelete(
|
||||||
|
project_id,
|
||||||
|
function (err) {
|
||||||
|
if (err != null) {
|
||||||
|
return callback(err)
|
||||||
|
}
|
||||||
|
return HistoryManager.forceResyncProject(
|
||||||
|
project_id,
|
||||||
|
function (err) {
|
||||||
|
if (err != null) {
|
||||||
|
return callback(err)
|
||||||
|
}
|
||||||
|
return HistoryManager.flushProject(project_id, callback)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -128,6 +128,10 @@ class MockProjectHistoryApi extends AbstractMockApi {
|
||||||
this.app.post('/project/:projectId/flush', (req, res) => {
|
this.app.post('/project/:projectId/flush', (req, res) => {
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.app.post('/project/:projectId/resync', (req, res) => {
|
||||||
|
res.sendStatus(204)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,9 @@ describe('ProjectController', function () {
|
||||||
.stub()
|
.stub()
|
||||||
.callsArgWith(2, null, { _id: this.project_id }),
|
.callsArgWith(2, null, { _id: this.project_id }),
|
||||||
}
|
}
|
||||||
|
this.ProjectHistoryHandler = {
|
||||||
|
ensureHistoryExistsForProject: sinon.stub().callsArg(1),
|
||||||
|
}
|
||||||
this.SubscriptionLocator = { getUsersSubscription: sinon.stub() }
|
this.SubscriptionLocator = { getUsersSubscription: sinon.stub() }
|
||||||
this.LimitationsManager = { hasPaidSubscription: sinon.stub() }
|
this.LimitationsManager = { hasPaidSubscription: sinon.stub() }
|
||||||
this.TagsHandler = { getAllTags: sinon.stub() }
|
this.TagsHandler = { getAllTags: sinon.stub() }
|
||||||
|
@ -140,6 +143,7 @@ describe('ProjectController', function () {
|
||||||
'./ProjectDeleter': this.ProjectDeleter,
|
'./ProjectDeleter': this.ProjectDeleter,
|
||||||
'./ProjectDuplicator': this.ProjectDuplicator,
|
'./ProjectDuplicator': this.ProjectDuplicator,
|
||||||
'./ProjectCreationHandler': this.ProjectCreationHandler,
|
'./ProjectCreationHandler': this.ProjectCreationHandler,
|
||||||
|
'./ProjectHistoryHandler': this.ProjectHistoryHandler,
|
||||||
'../Editor/EditorController': this.EditorController,
|
'../Editor/EditorController': this.EditorController,
|
||||||
'../User/UserController': this.UserController,
|
'../User/UserController': this.UserController,
|
||||||
'./ProjectHelper': this.ProjectHelper,
|
'./ProjectHelper': this.ProjectHelper,
|
||||||
|
@ -1024,6 +1028,18 @@ describe('ProjectController', function () {
|
||||||
this.ProjectController.loadEditor(this.req, this.res)
|
this.ProjectController.loadEditor(this.req, this.res)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should ensureHistoryExistsForProject if saas and project_history enabled', function (done) {
|
||||||
|
this.Features.hasFeature.withArgs('saas').returns(true)
|
||||||
|
this.settings.apis.project_history = 'enabled'
|
||||||
|
this.res.render = (pageName, opts) => {
|
||||||
|
this.ProjectHistoryHandler.ensureHistoryExistsForProject
|
||||||
|
.calledWith(this.project_id)
|
||||||
|
.should.equal(true)
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
this.ProjectController.loadEditor(this.req, this.res)
|
||||||
|
})
|
||||||
|
|
||||||
it('should mark project as opened', function (done) {
|
it('should mark project as opened', function (done) {
|
||||||
this.res.render = (pageName, opts) => {
|
this.res.render = (pageName, opts) => {
|
||||||
this.ProjectUpdateHandler.markAsOpened
|
this.ProjectUpdateHandler.markAsOpened
|
||||||
|
|
|
@ -51,6 +51,7 @@ describe('ProjectHistoryHandler', function () {
|
||||||
'./ProjectDetailsHandler': (this.ProjectDetailsHandler = {}),
|
'./ProjectDetailsHandler': (this.ProjectDetailsHandler = {}),
|
||||||
'../History/HistoryManager': (this.HistoryManager = {}),
|
'../History/HistoryManager': (this.HistoryManager = {}),
|
||||||
'./ProjectEntityUpdateHandler': (this.ProjectEntityUpdateHandler = {}),
|
'./ProjectEntityUpdateHandler': (this.ProjectEntityUpdateHandler = {}),
|
||||||
|
'../DocumentUpdater/DocumentUpdaterHandler': (this.DocumentUpdaterHandler = {}),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
@ -62,9 +63,10 @@ describe('ProjectHistoryHandler', function () {
|
||||||
.stub()
|
.stub()
|
||||||
.callsArgWith(0, null, { overleaf_id: this.newHistoryId })
|
.callsArgWith(0, null, { overleaf_id: this.newHistoryId })
|
||||||
this.HistoryManager.flushProject = sinon.stub().callsArg(1)
|
this.HistoryManager.flushProject = sinon.stub().callsArg(1)
|
||||||
return (this.ProjectEntityUpdateHandler.resyncProjectHistory = sinon
|
this.HistoryManager.forceResyncProject = sinon.stub().callsArg(1)
|
||||||
|
this.DocumentUpdaterHandler.flushProjectToMongoAndDelete = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.callsArg(1))
|
.callsArg(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('when the history does not already exist', function () {
|
describe('when the history does not already exist', function () {
|
||||||
|
@ -101,14 +103,21 @@ describe('ProjectHistoryHandler', function () {
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should resync the project history', function () {
|
it('should trigger a hard resync of the project history', function () {
|
||||||
return this.ProjectEntityUpdateHandler.resyncProjectHistory
|
return this.HistoryManager.forceResyncProject
|
||||||
.calledWith(project_id)
|
.calledWith(project_id)
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should flush the project history', function () {
|
it('should flush the project history (twice)', function () {
|
||||||
|
this.HistoryManager.flushProject.calledTwice.should.equal(true)
|
||||||
return this.HistoryManager.flushProject
|
return this.HistoryManager.flushProject
|
||||||
|
.alwaysCalledWith(project_id)
|
||||||
|
.should.equal(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should tell docupdater to flush and delete', function () {
|
||||||
|
return this.DocumentUpdaterHandler.flushProjectToMongoAndDelete
|
||||||
.calledWith(project_id)
|
.calledWith(project_id)
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
})
|
})
|
||||||
|
@ -146,10 +155,8 @@ describe('ProjectHistoryHandler', function () {
|
||||||
return this.ProjectModel.updateOne.called.should.equal(false)
|
return this.ProjectModel.updateOne.called.should.equal(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not resync the project history', function () {
|
it('should not trigger a hard resync of the project history', function () {
|
||||||
return this.ProjectEntityUpdateHandler.resyncProjectHistory.called.should.equal(
|
return this.HistoryManager.forceResyncProject.called.should.equal(false)
|
||||||
false
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not flush the project history', function () {
|
it('should not flush the project history', function () {
|
||||||
|
|
Loading…
Reference in a new issue