PersistenceManager.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  4. no-unsafe-negation,
  5. no-unused-vars,
  6. */
  7. // TODO: This file was created by bulk-decaffeinate.
  8. // Fix any style issues and re-enable lint.
  9. /*
  10. * decaffeinate suggestions:
  11. * DS101: Remove unnecessary use of Array.from
  12. * DS102: Remove unnecessary code created because of implicit returns
  13. * DS205: Consider reworking code to avoid use of IIFEs
  14. * DS207: Consider shorter variations of null checks
  15. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  16. */
  17. let PersistenceManager
  18. const Settings = require('settings-sharelatex')
  19. const Errors = require('./Errors')
  20. const Metrics = require('./Metrics')
  21. const logger = require('logger-sharelatex')
  22. const request = require('requestretry').defaults({
  23. maxAttempts: 2,
  24. retryDelay: 10
  25. })
  26. // We have to be quick with HTTP calls because we're holding a lock that
  27. // expires after 30 seconds. We can't let any errors in the rest of the stack
  28. // hold us up, and need to bail out quickly if there is a problem.
  29. const MAX_HTTP_REQUEST_LENGTH = 5000 // 5 seconds
  30. const updateMetric = function (method, error, response) {
  31. // find the status, with special handling for connection timeouts
  32. // https://github.com/request/request#timeouts
  33. const status = (() => {
  34. if ((error != null ? error.connect : undefined) === true) {
  35. return `${error.code} (connect)`
  36. } else if (error != null) {
  37. return error.code
  38. } else if (response != null) {
  39. return response.statusCode
  40. }
  41. })()
  42. Metrics.inc(method, 1, { status })
  43. if ((error != null ? error.attempts : undefined) > 1) {
  44. Metrics.inc(`${method}-retries`, 1, { status: 'error' })
  45. }
  46. if ((response != null ? response.attempts : undefined) > 1) {
  47. return Metrics.inc(`${method}-retries`, 1, { status: 'success' })
  48. }
  49. }
  50. module.exports = PersistenceManager = {
  51. getDoc(project_id, doc_id, _callback) {
  52. if (_callback == null) {
  53. _callback = function (
  54. error,
  55. lines,
  56. version,
  57. ranges,
  58. pathname,
  59. projectHistoryId,
  60. projectHistoryType
  61. ) {}
  62. }
  63. const timer = new Metrics.Timer('persistenceManager.getDoc')
  64. const callback = function (...args) {
  65. timer.done()
  66. return _callback(...Array.from(args || []))
  67. }
  68. const url = `${Settings.apis.web.url}/project/${project_id}/doc/${doc_id}`
  69. return request(
  70. {
  71. url,
  72. method: 'GET',
  73. headers: {
  74. accept: 'application/json'
  75. },
  76. auth: {
  77. user: Settings.apis.web.user,
  78. pass: Settings.apis.web.pass,
  79. sendImmediately: true
  80. },
  81. jar: false,
  82. timeout: MAX_HTTP_REQUEST_LENGTH
  83. },
  84. function (error, res, body) {
  85. updateMetric('getDoc', error, res)
  86. if (error != null) {
  87. return callback(error)
  88. }
  89. if (res.statusCode >= 200 && res.statusCode < 300) {
  90. try {
  91. body = JSON.parse(body)
  92. } catch (e) {
  93. return callback(e)
  94. }
  95. if (body.lines == null) {
  96. return callback(new Error('web API response had no doc lines'))
  97. }
  98. if (body.version == null || !body.version instanceof Number) {
  99. return callback(
  100. new Error('web API response had no valid doc version')
  101. )
  102. }
  103. if (body.pathname == null) {
  104. return callback(
  105. new Error('web API response had no valid doc pathname')
  106. )
  107. }
  108. return callback(
  109. null,
  110. body.lines,
  111. body.version,
  112. body.ranges,
  113. body.pathname,
  114. body.projectHistoryId,
  115. body.projectHistoryType
  116. )
  117. } else if (res.statusCode === 404) {
  118. return callback(new Errors.NotFoundError(`doc not not found: ${url}`))
  119. } else {
  120. return callback(
  121. new Error(`error accessing web API: ${url} ${res.statusCode}`)
  122. )
  123. }
  124. }
  125. )
  126. },
  127. setDoc(
  128. project_id,
  129. doc_id,
  130. lines,
  131. version,
  132. ranges,
  133. lastUpdatedAt,
  134. lastUpdatedBy,
  135. _callback
  136. ) {
  137. if (_callback == null) {
  138. _callback = function (error) {}
  139. }
  140. const timer = new Metrics.Timer('persistenceManager.setDoc')
  141. const callback = function (...args) {
  142. timer.done()
  143. return _callback(...Array.from(args || []))
  144. }
  145. const url = `${Settings.apis.web.url}/project/${project_id}/doc/${doc_id}`
  146. return request(
  147. {
  148. url,
  149. method: 'POST',
  150. json: {
  151. lines,
  152. ranges,
  153. version,
  154. lastUpdatedBy,
  155. lastUpdatedAt
  156. },
  157. auth: {
  158. user: Settings.apis.web.user,
  159. pass: Settings.apis.web.pass,
  160. sendImmediately: true
  161. },
  162. jar: false,
  163. timeout: MAX_HTTP_REQUEST_LENGTH
  164. },
  165. function (error, res, body) {
  166. updateMetric('setDoc', error, res)
  167. if (error != null) {
  168. return callback(error)
  169. }
  170. if (res.statusCode >= 200 && res.statusCode < 300) {
  171. return callback(null)
  172. } else if (res.statusCode === 404) {
  173. return callback(new Errors.NotFoundError(`doc not not found: ${url}`))
  174. } else {
  175. return callback(
  176. new Error(`error accessing web API: ${url} ${res.statusCode}`)
  177. )
  178. }
  179. }
  180. )
  181. }
  182. }