DocUpdaterClient.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  4. */
  5. // TODO: This file was created by bulk-decaffeinate.
  6. // Fix any style issues and re-enable lint.
  7. /*
  8. * decaffeinate suggestions:
  9. * DS101: Remove unnecessary use of Array.from
  10. * DS102: Remove unnecessary code created because of implicit returns
  11. * DS207: Consider shorter variations of null checks
  12. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  13. */
  14. let DocUpdaterClient;
  15. const Settings = require('settings-sharelatex');
  16. const rclient = require("redis-sharelatex").createClient(Settings.redis.documentupdater);
  17. const keys = Settings.redis.documentupdater.key_schema;
  18. const request = require("request").defaults({jar: false});
  19. const async = require("async");
  20. const rclient_sub = require("redis-sharelatex").createClient(Settings.redis.pubsub);
  21. rclient_sub.subscribe("applied-ops");
  22. rclient_sub.setMaxListeners(0);
  23. module.exports = (DocUpdaterClient = {
  24. randomId() {
  25. const chars = __range__(1, 24, true).map((i) =>
  26. Math.random().toString(16)[2]);
  27. return chars.join("");
  28. },
  29. subscribeToAppliedOps(callback) {
  30. if (callback == null) { callback = function(message) {}; }
  31. return rclient_sub.on("message", callback);
  32. },
  33. sendUpdate(project_id, doc_id, update, callback) {
  34. if (callback == null) { callback = function(error) {}; }
  35. return rclient.rpush(keys.pendingUpdates({doc_id}), JSON.stringify(update), (error) => {
  36. if (error != null) { return callback(error); }
  37. const doc_key = `${project_id}:${doc_id}`;
  38. return rclient.sadd("DocsWithPendingUpdates", doc_key, (error) => {
  39. if (error != null) { return callback(error); }
  40. return rclient.rpush("pending-updates-list", doc_key, callback);
  41. });
  42. });
  43. },
  44. sendUpdates(project_id, doc_id, updates, callback) {
  45. if (callback == null) { callback = function(error) {}; }
  46. return DocUpdaterClient.preloadDoc(project_id, doc_id, (error) => {
  47. if (error != null) { return callback(error); }
  48. const jobs = [];
  49. for (const update of Array.from(updates)) {
  50. ((update => jobs.push(callback => DocUpdaterClient.sendUpdate(project_id, doc_id, update, callback))))(update);
  51. }
  52. return async.series(jobs, err => DocUpdaterClient.waitForPendingUpdates(project_id, doc_id, callback));
  53. });
  54. },
  55. waitForPendingUpdates(project_id, doc_id, callback) {
  56. return async.retry({times: 30, interval: 100}, cb => rclient.llen(keys.pendingUpdates({doc_id}), (err, length) => {
  57. if (length > 0) {
  58. return cb(new Error("updates still pending"));
  59. } else {
  60. return cb();
  61. }
  62. })
  63. , callback);
  64. },
  65. getDoc(project_id, doc_id, callback) {
  66. if (callback == null) { callback = function(error, res, body) {}; }
  67. return request.get(`http://localhost:3003/project/${project_id}/doc/${doc_id}`, (error, res, body) => {
  68. if ((body != null) && (res.statusCode >= 200) && (res.statusCode < 300)) {
  69. body = JSON.parse(body);
  70. }
  71. return callback(error, res, body);
  72. });
  73. },
  74. getDocAndRecentOps(project_id, doc_id, fromVersion, callback) {
  75. if (callback == null) { callback = function(error, res, body) {}; }
  76. return request.get(`http://localhost:3003/project/${project_id}/doc/${doc_id}?fromVersion=${fromVersion}`, (error, res, body) => {
  77. if ((body != null) && (res.statusCode >= 200) && (res.statusCode < 300)) {
  78. body = JSON.parse(body);
  79. }
  80. return callback(error, res, body);
  81. });
  82. },
  83. preloadDoc(project_id, doc_id, callback) {
  84. if (callback == null) { callback = function(error) {}; }
  85. return DocUpdaterClient.getDoc(project_id, doc_id, callback);
  86. },
  87. flushDoc(project_id, doc_id, callback) {
  88. if (callback == null) { callback = function(error) {}; }
  89. return request.post(`http://localhost:3003/project/${project_id}/doc/${doc_id}/flush`, (error, res, body) => callback(error, res, body));
  90. },
  91. setDocLines(project_id, doc_id, lines, source, user_id, undoing, callback) {
  92. if (callback == null) { callback = function(error) {}; }
  93. return request.post({
  94. url: `http://localhost:3003/project/${project_id}/doc/${doc_id}`,
  95. json: {
  96. lines,
  97. source,
  98. user_id,
  99. undoing
  100. }
  101. }, (error, res, body) => callback(error, res, body));
  102. },
  103. deleteDoc(project_id, doc_id, callback) {
  104. if (callback == null) { callback = function(error) {}; }
  105. return request.del(`http://localhost:3003/project/${project_id}/doc/${doc_id}`, (error, res, body) => callback(error, res, body));
  106. },
  107. flushProject(project_id, callback) {
  108. if (callback == null) { callback = function() {}; }
  109. return request.post(`http://localhost:3003/project/${project_id}/flush`, callback);
  110. },
  111. deleteProject(project_id, callback) {
  112. if (callback == null) { callback = function() {}; }
  113. return request.del(`http://localhost:3003/project/${project_id}`, callback);
  114. },
  115. deleteProjectOnShutdown(project_id, callback) {
  116. if (callback == null) { callback = function() {}; }
  117. return request.del(`http://localhost:3003/project/${project_id}?background=true&shutdown=true`, callback);
  118. },
  119. flushOldProjects(callback) {
  120. if (callback == null) { callback = function() {}; }
  121. return request.get("http://localhost:3003/flush_queued_projects?min_delete_age=1", callback);
  122. },
  123. acceptChange(project_id, doc_id, change_id, callback) {
  124. if (callback == null) { callback = function() {}; }
  125. return request.post(`http://localhost:3003/project/${project_id}/doc/${doc_id}/change/${change_id}/accept`, callback);
  126. },
  127. removeComment(project_id, doc_id, comment, callback) {
  128. if (callback == null) { callback = function() {}; }
  129. return request.del(`http://localhost:3003/project/${project_id}/doc/${doc_id}/comment/${comment}`, callback);
  130. },
  131. getProjectDocs(project_id, projectStateHash, callback) {
  132. if (callback == null) { callback = function() {}; }
  133. return request.get(`http://localhost:3003/project/${project_id}/doc?state=${projectStateHash}`, (error, res, body) => {
  134. if ((body != null) && (res.statusCode >= 200) && (res.statusCode < 300)) {
  135. body = JSON.parse(body);
  136. }
  137. return callback(error, res, body);
  138. });
  139. },
  140. sendProjectUpdate(project_id, userId, docUpdates, fileUpdates, version, callback) {
  141. if (callback == null) { callback = function(error) {}; }
  142. return request.post({
  143. url: `http://localhost:3003/project/${project_id}`,
  144. json: { userId, docUpdates, fileUpdates, version }
  145. }, (error, res, body) => callback(error, res, body));
  146. }
  147. });
  148. function __range__(left, right, inclusive) {
  149. const range = [];
  150. const ascending = left < right;
  151. const end = !inclusive ? right : ascending ? right + 1 : right - 1;
  152. for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {
  153. range.push(i);
  154. }
  155. return range;
  156. }