2020-05-06 06:12:36 -04:00
|
|
|
/* eslint-disable
|
|
|
|
camelcase,
|
|
|
|
handle-callback-err,
|
|
|
|
*/
|
|
|
|
// TODO: This file was created by bulk-decaffeinate.
|
|
|
|
// Fix any style issues and re-enable lint.
|
2020-05-06 06:12:17 -04:00
|
|
|
/*
|
|
|
|
* decaffeinate suggestions:
|
|
|
|
* DS101: Remove unnecessary use of Array.from
|
|
|
|
* 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
|
|
|
|
*/
|
2020-05-06 06:12:47 -04:00
|
|
|
let DocUpdaterClient
|
|
|
|
const Settings = require('settings-sharelatex')
|
|
|
|
const rclient = require('redis-sharelatex').createClient(
|
|
|
|
Settings.redis.documentupdater
|
|
|
|
)
|
|
|
|
const keys = Settings.redis.documentupdater.key_schema
|
|
|
|
const request = require('request').defaults({ jar: false })
|
|
|
|
const async = require('async')
|
|
|
|
|
|
|
|
const rclient_sub = require('redis-sharelatex').createClient(
|
|
|
|
Settings.redis.pubsub
|
|
|
|
)
|
|
|
|
rclient_sub.subscribe('applied-ops')
|
|
|
|
rclient_sub.setMaxListeners(0)
|
|
|
|
|
|
|
|
module.exports = DocUpdaterClient = {
|
|
|
|
randomId() {
|
|
|
|
const chars = __range__(1, 24, true).map(
|
|
|
|
(i) => Math.random().toString(16)[2]
|
|
|
|
)
|
|
|
|
return chars.join('')
|
|
|
|
},
|
|
|
|
|
|
|
|
subscribeToAppliedOps(callback) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function (message) {}
|
|
|
|
}
|
|
|
|
return rclient_sub.on('message', callback)
|
|
|
|
},
|
|
|
|
|
|
|
|
sendUpdate(project_id, doc_id, update, callback) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function (error) {}
|
|
|
|
}
|
|
|
|
return rclient.rpush(
|
|
|
|
keys.pendingUpdates({ doc_id }),
|
|
|
|
JSON.stringify(update),
|
|
|
|
(error) => {
|
|
|
|
if (error != null) {
|
|
|
|
return callback(error)
|
|
|
|
}
|
|
|
|
const doc_key = `${project_id}:${doc_id}`
|
|
|
|
return rclient.sadd('DocsWithPendingUpdates', doc_key, (error) => {
|
|
|
|
if (error != null) {
|
|
|
|
return callback(error)
|
|
|
|
}
|
|
|
|
return rclient.rpush('pending-updates-list', doc_key, callback)
|
2020-05-06 06:12:17 -04:00
|
|
|
})
|
2020-05-06 06:12:47 -04:00
|
|
|
}
|
|
|
|
)
|
|
|
|
},
|
|
|
|
|
|
|
|
sendUpdates(project_id, doc_id, updates, callback) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function (error) {}
|
|
|
|
}
|
|
|
|
return DocUpdaterClient.preloadDoc(project_id, doc_id, (error) => {
|
|
|
|
if (error != null) {
|
|
|
|
return callback(error)
|
|
|
|
}
|
|
|
|
const jobs = []
|
|
|
|
for (const update of Array.from(updates)) {
|
|
|
|
;((update) =>
|
|
|
|
jobs.push((callback) =>
|
|
|
|
DocUpdaterClient.sendUpdate(project_id, doc_id, update, callback)
|
|
|
|
))(update)
|
|
|
|
}
|
|
|
|
return async.series(jobs, (err) =>
|
|
|
|
DocUpdaterClient.waitForPendingUpdates(project_id, doc_id, callback)
|
|
|
|
)
|
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
waitForPendingUpdates(project_id, doc_id, callback) {
|
|
|
|
return async.retry(
|
|
|
|
{ times: 30, interval: 100 },
|
|
|
|
(cb) =>
|
|
|
|
rclient.llen(keys.pendingUpdates({ doc_id }), (err, length) => {
|
|
|
|
if (length > 0) {
|
|
|
|
return cb(new Error('updates still pending'))
|
|
|
|
} else {
|
|
|
|
return cb()
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
callback
|
|
|
|
)
|
|
|
|
},
|
|
|
|
|
|
|
|
getDoc(project_id, doc_id, callback) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function (error, res, body) {}
|
|
|
|
}
|
|
|
|
return request.get(
|
|
|
|
`http://localhost:3003/project/${project_id}/doc/${doc_id}`,
|
|
|
|
(error, res, body) => {
|
|
|
|
if (body != null && res.statusCode >= 200 && res.statusCode < 300) {
|
|
|
|
body = JSON.parse(body)
|
|
|
|
}
|
|
|
|
return callback(error, res, body)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
},
|
|
|
|
|
|
|
|
getDocAndRecentOps(project_id, doc_id, fromVersion, callback) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function (error, res, body) {}
|
|
|
|
}
|
|
|
|
return request.get(
|
|
|
|
`http://localhost:3003/project/${project_id}/doc/${doc_id}?fromVersion=${fromVersion}`,
|
|
|
|
(error, res, body) => {
|
|
|
|
if (body != null && res.statusCode >= 200 && res.statusCode < 300) {
|
|
|
|
body = JSON.parse(body)
|
|
|
|
}
|
|
|
|
return callback(error, res, body)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
},
|
|
|
|
|
|
|
|
preloadDoc(project_id, doc_id, callback) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function (error) {}
|
|
|
|
}
|
|
|
|
return DocUpdaterClient.getDoc(project_id, doc_id, callback)
|
|
|
|
},
|
|
|
|
|
|
|
|
flushDoc(project_id, doc_id, callback) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function (error) {}
|
|
|
|
}
|
|
|
|
return request.post(
|
|
|
|
`http://localhost:3003/project/${project_id}/doc/${doc_id}/flush`,
|
|
|
|
(error, res, body) => callback(error, res, body)
|
|
|
|
)
|
|
|
|
},
|
|
|
|
|
|
|
|
setDocLines(project_id, doc_id, lines, source, user_id, undoing, callback) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function (error) {}
|
|
|
|
}
|
|
|
|
return request.post(
|
|
|
|
{
|
|
|
|
url: `http://localhost:3003/project/${project_id}/doc/${doc_id}`,
|
|
|
|
json: {
|
|
|
|
lines,
|
|
|
|
source,
|
|
|
|
user_id,
|
|
|
|
undoing
|
|
|
|
}
|
|
|
|
},
|
|
|
|
(error, res, body) => callback(error, res, body)
|
|
|
|
)
|
|
|
|
},
|
|
|
|
|
|
|
|
deleteDoc(project_id, doc_id, callback) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function (error) {}
|
|
|
|
}
|
|
|
|
return request.del(
|
|
|
|
`http://localhost:3003/project/${project_id}/doc/${doc_id}`,
|
|
|
|
(error, res, body) => callback(error, res, body)
|
|
|
|
)
|
|
|
|
},
|
|
|
|
|
|
|
|
flushProject(project_id, callback) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function () {}
|
|
|
|
}
|
|
|
|
return request.post(
|
|
|
|
`http://localhost:3003/project/${project_id}/flush`,
|
|
|
|
callback
|
|
|
|
)
|
|
|
|
},
|
|
|
|
|
|
|
|
deleteProject(project_id, callback) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function () {}
|
|
|
|
}
|
|
|
|
return request.del(`http://localhost:3003/project/${project_id}`, callback)
|
|
|
|
},
|
|
|
|
|
|
|
|
deleteProjectOnShutdown(project_id, callback) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function () {}
|
|
|
|
}
|
|
|
|
return request.del(
|
|
|
|
`http://localhost:3003/project/${project_id}?background=true&shutdown=true`,
|
|
|
|
callback
|
|
|
|
)
|
|
|
|
},
|
|
|
|
|
|
|
|
flushOldProjects(callback) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function () {}
|
|
|
|
}
|
|
|
|
return request.get(
|
|
|
|
'http://localhost:3003/flush_queued_projects?min_delete_age=1',
|
|
|
|
callback
|
|
|
|
)
|
|
|
|
},
|
|
|
|
|
|
|
|
acceptChange(project_id, doc_id, change_id, callback) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function () {}
|
|
|
|
}
|
|
|
|
return request.post(
|
|
|
|
`http://localhost:3003/project/${project_id}/doc/${doc_id}/change/${change_id}/accept`,
|
|
|
|
callback
|
|
|
|
)
|
|
|
|
},
|
|
|
|
|
|
|
|
removeComment(project_id, doc_id, comment, callback) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function () {}
|
|
|
|
}
|
|
|
|
return request.del(
|
|
|
|
`http://localhost:3003/project/${project_id}/doc/${doc_id}/comment/${comment}`,
|
|
|
|
callback
|
|
|
|
)
|
|
|
|
},
|
|
|
|
|
|
|
|
getProjectDocs(project_id, projectStateHash, callback) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function () {}
|
|
|
|
}
|
|
|
|
return request.get(
|
|
|
|
`http://localhost:3003/project/${project_id}/doc?state=${projectStateHash}`,
|
|
|
|
(error, res, body) => {
|
|
|
|
if (body != null && res.statusCode >= 200 && res.statusCode < 300) {
|
|
|
|
body = JSON.parse(body)
|
|
|
|
}
|
|
|
|
return callback(error, res, body)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
},
|
|
|
|
|
|
|
|
sendProjectUpdate(
|
|
|
|
project_id,
|
|
|
|
userId,
|
|
|
|
docUpdates,
|
|
|
|
fileUpdates,
|
|
|
|
version,
|
|
|
|
callback
|
|
|
|
) {
|
|
|
|
if (callback == null) {
|
|
|
|
callback = function (error) {}
|
|
|
|
}
|
|
|
|
return request.post(
|
|
|
|
{
|
|
|
|
url: `http://localhost:3003/project/${project_id}`,
|
|
|
|
json: { userId, docUpdates, fileUpdates, version }
|
|
|
|
},
|
|
|
|
(error, res, body) => callback(error, res, body)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2020-05-06 06:12:17 -04:00
|
|
|
|
|
|
|
function __range__(left, right, inclusive) {
|
2020-05-06 06:12:47 -04:00
|
|
|
const range = []
|
|
|
|
const ascending = left < right
|
|
|
|
const end = !inclusive ? right : ascending ? right + 1 : right - 1
|
2020-05-06 06:12:17 -04:00
|
|
|
for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {
|
2020-05-06 06:12:47 -04:00
|
|
|
range.push(i)
|
2020-05-06 06:12:17 -04:00
|
|
|
}
|
2020-05-06 06:12:47 -04:00
|
|
|
return range
|
|
|
|
}
|