Skip to content

Commit

Permalink
fix: retry api requests on rate limit or persistent connection errors
Browse files Browse the repository at this point in the history
  • Loading branch information
leomp12 committed Jul 21, 2023
1 parent 58e380b commit c978555
Showing 1 changed file with 84 additions and 53 deletions.
137 changes: 84 additions & 53 deletions lib/Api.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,24 @@ let baseUri
let port

function requestEnd (res, rawData, errorCallback, successCallback) {
let str, err
switch (res.statusCode) {
case 200:
case 201:
case 204:
successCallback()
break

case 412:
// no store found with provided ID
errorCallback(null, 412, 'Invalid store ID')
break

default:
// unexpected status code
let str = 'Unexpected response status code from Store API' +
str = 'Unexpected response status code from Store API' +
'\nStatus: ' + res.statusCode +
'\nResponse: ' + rawData
let err = new Error(str)
err = new Error(str)
err.statusCode = res.statusCode
errorCallback(err)
}
}
Expand All @@ -48,8 +48,57 @@ require('./../lib/Config.js')((config) => {
port = config.apiPort
})

module.exports = (endpoint, method, body, storeId, errorCallback, successCallback, publicApi = false) => {
let options = {
const request = (
client,
options,
body,
errorCallback,
successCallback
) => {
const req = client.request(options, (res) => {
let rawData = ''
res.setEncoding('utf8')
res.on('data', (chunk) => { rawData += chunk })
res.on('end', () => {
requestEnd(res, rawData, errorCallback, () => {
// OK
let parsedData
if (rawData) {
try {
parsedData = JSON.parse(rawData)
} catch (e) {
// not a valid JSON
// callback without response body
errorCallback(e)
return
}
}
if (typeof successCallback === 'function') {
// pass parsed JSON
successCallback(parsedData)
}
})
})
})
req.on('error', errorCallback)
if (body) {
// write data to request body
req.write(JSON.stringify(body))
}
req.end()
}

module.exports = (
endpoint,
method,
body,
storeId,
errorCallback,
successCallback,
publicApi = false,
canRetry = true
) => {
const options = {
hostname: !publicApi ? host : 'api.e-com.plus',
path: baseUri + endpoint,
method: method,
Expand All @@ -60,53 +109,35 @@ module.exports = (endpoint, method, body, storeId, errorCallback, successCallbac
if (!publicApi && port) {
options.port = port
}
const client = !publicApi ? http : https

let client = !publicApi ? http : https
let req = client.request(options, (res) => {
if (typeof errorCallback === 'function') {
let rawData = ''
res.setEncoding('utf8')
res.on('data', (chunk) => { rawData += chunk })
res.on('end', () => {
requestEnd(res, rawData, errorCallback, () => {
// OK
let parsedData
if (rawData) {
try {
parsedData = JSON.parse(rawData)
} catch (e) {
// not a valid JSON
// callback without response body
errorCallback(e)
return
}
}

if (typeof successCallback === 'function') {
// pass parsed JSON
successCallback(parsedData)
}
})
})
} else {
// consume response data to free up memory
res.resume()
}
})

req.on('error', (e) => {
if (typeof errorCallback === 'function') {
errorCallback(e)
} else {
// no callback
// just log the unexpected error
logger.error(e)
}
})

if (body) {
// write data to request body
req.write(JSON.stringify(body))
let retries = 0
const maxRetries = canRetry
? endpoint.startsWith('orders') ? 2 : 1
: 0
const requestWithRetry = () => {
request(
client,
options,
body,
(err) => {
if (err instanceof Error && !(err.statusCode < 500) && retries < maxRetries) {
retries++
setTimeout(() => {
requestWithRetry()
}, retries * (publicApi ? 600 : 1000))
return
}
if (typeof errorCallback === 'function') {
errorCallback(err)
} else {
// no callback
// just log the unexpected error
logger.error(err)
}
},
successCallback
)
}
req.end()
requestWithRetry()
}

0 comments on commit c978555

Please sign in to comment.