/* eslint-disable camelcase, handle-callback-err, */ // 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 */ 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) { return rclient_sub.on('message', callback) }, sendUpdate(project_id, doc_id, update, callback) { return rclient.rpush( keys.pendingUpdates({ doc_id }), JSON.stringify(update), (error) => { if (error) { return callback(error) } const doc_key = `${project_id}:${doc_id}` return rclient.sadd('DocsWithPendingUpdates', doc_key, (error) => { if (error) { return callback(error) } return rclient.rpush('pending-updates-list', doc_key, callback) }) } ) }, sendUpdates(project_id, doc_id, updates, callback) { return DocUpdaterClient.preloadDoc(project_id, doc_id, (error) => { if (error) { 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) { 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) { 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) { return DocUpdaterClient.getDoc(project_id, doc_id, callback) }, flushDoc(project_id, doc_id, callback) { 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) { 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) { return request.del( `http://localhost:3003/project/${project_id}/doc/${doc_id}`, (error, res, body) => callback(error, res, body) ) }, flushProject(project_id, callback) { return request.post( `http://localhost:3003/project/${project_id}/flush`, callback ) }, deleteProject(project_id, callback) { return request.del(`http://localhost:3003/project/${project_id}`, callback) }, deleteProjectOnShutdown(project_id, callback) { return request.del( `http://localhost:3003/project/${project_id}?background=true&shutdown=true`, callback ) }, flushOldProjects(callback) { return request.get( 'http://localhost:3003/flush_queued_projects?min_delete_age=1', callback ) }, acceptChange(project_id, doc_id, change_id, callback) { return request.post( `http://localhost:3003/project/${project_id}/doc/${doc_id}/change/${change_id}/accept`, callback ) }, removeComment(project_id, doc_id, comment, callback) { return request.del( `http://localhost:3003/project/${project_id}/doc/${doc_id}/comment/${comment}`, callback ) }, getProjectDocs(project_id, projectStateHash, callback) { 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 ) { return request.post( { url: `http://localhost:3003/project/${project_id}`, json: { userId, docUpdates, fileUpdates, version } }, (error, res, body) => callback(error, res, body) ) } } function __range__(left, right, inclusive) { const range = [] const ascending = left < right const end = !inclusive ? right : ascending ? right + 1 : right - 1 for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) { range.push(i) } return range }