overleaf/services/document-updater/app/coffee/PersistenceManager.coffee
2020-01-14 13:53:50 +00:00

100 lines
3.4 KiB
CoffeeScript

Settings = require "settings-sharelatex"
Errors = require "./Errors"
Metrics = require "./Metrics"
logger = require "logger-sharelatex"
request = (require("requestretry")).defaults({
maxAttempts: 2
retryDelay: 10
})
# We have to be quick with HTTP calls because we're holding a lock that
# expires after 30 seconds. We can't let any errors in the rest of the stack
# hold us up, and need to bail out quickly if there is a problem.
MAX_HTTP_REQUEST_LENGTH = 5000 # 5 seconds
updateMetric = (method, error, response) ->
# find the status, with special handling for connection timeouts
# https://github.com/request/request#timeouts
status = if error?.connect is true
"#{error.code} (connect)"
else if error?
error.code
else if response?
response.statusCode
Metrics.inc method, {status: status}
if error?.attempts > 0
Metrics.inc "#{method}-attempts", {status: 'error'}
if response?.attempts > 0
Metrics.inc "#{method}-attempts", {status: 'success'}
module.exports = PersistenceManager =
getDoc: (project_id, doc_id, _callback = (error, lines, version, ranges, pathname, projectHistoryId, projectHistoryType) ->) ->
timer = new Metrics.Timer("persistenceManager.getDoc")
callback = (args...) ->
timer.done()
_callback(args...)
url = "#{Settings.apis.web.url}/project/#{project_id}/doc/#{doc_id}"
request {
url: url
method: "GET"
headers:
"accept": "application/json"
auth:
user: Settings.apis.web.user
pass: Settings.apis.web.pass
sendImmediately: true
jar: false
timeout: MAX_HTTP_REQUEST_LENGTH
}, (error, res, body) ->
updateMetric('getDoc', error, res)
return callback(error) if error?
if res.statusCode >= 200 and res.statusCode < 300
try
body = JSON.parse body
catch e
return callback(e)
if !body.lines?
return callback(new Error("web API response had no doc lines"))
if !body.version? or not body.version instanceof Number
return callback(new Error("web API response had no valid doc version"))
if !body.pathname?
return callback(new Error("web API response had no valid doc pathname"))
return callback null, body.lines, body.version, body.ranges, body.pathname, body.projectHistoryId, body.projectHistoryType
else if res.statusCode == 404
return callback(new Errors.NotFoundError("doc not not found: #{url}"))
else
return callback(new Error("error accessing web API: #{url} #{res.statusCode}"))
setDoc: (project_id, doc_id, lines, version, ranges, lastUpdatedAt, lastUpdatedBy,_callback = (error) ->) ->
timer = new Metrics.Timer("persistenceManager.setDoc")
callback = (args...) ->
timer.done()
_callback(args...)
url = "#{Settings.apis.web.url}/project/#{project_id}/doc/#{doc_id}"
request {
url: url
method: "POST"
json:
lines: lines
ranges: ranges
version: version
lastUpdatedBy: lastUpdatedBy
lastUpdatedAt: lastUpdatedAt
auth:
user: Settings.apis.web.user
pass: Settings.apis.web.pass
sendImmediately: true
jar: false
timeout: MAX_HTTP_REQUEST_LENGTH
}, (error, res, body) ->
updateMetric('setDoc', error, res)
return callback(error) if error?
if res.statusCode >= 200 and res.statusCode < 300
return callback null
else if res.statusCode == 404
return callback(new Errors.NotFoundError("doc not not found: #{url}"))
else
return callback(new Error("error accessing web API: #{url} #{res.statusCode}"))