mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-02 09:29:36 -05:00
9419cc3b37
* Fix: Invoices collected array length comparison
Update the code with the correct condition to respect the intent of the previous implementation ("exit with non zero code when no invoicess were processed").
See 5476f39984
However, I'm not sure if erroring when no invoices are collected is actually what we want to do.
* Wrap `collect_paypal_past_due_invoice` script and export the function
* Fixup typo `accoutns`
* Log invoices collection data before throwing
* Add note: `handleAPIError` is silencing the errors
* Create a test on `collect_paypal_past_due_invoice`
* Replace `console.log` by `@overleaf/logger` (bunyan)
Our `console.warn` show up as Errors (in red) in GCP. For example the following is an error in GCP:
```
Errors in attemptInvoiceCollection with id=2693634 OError: Recurly API returned with status code: 400
```
https://github.com/overleaf/internal/blob/5476f39/services/web/scripts/recurly/collect_paypal_past_due_invoice.js#L9
---
Does it correctly set the levels as warnings if we use `@overleaf/logger`
GitOrigin-RevId: 37c8bdf4afd8cef4706700aafb44480ec8966a74
136 lines
3.8 KiB
JavaScript
136 lines
3.8 KiB
JavaScript
const RecurlyWrapper = require('../../app/src/Features/Subscription/RecurlyWrapper')
|
|
const async = require('async')
|
|
const minimist = require('minimist')
|
|
const logger = require('@overleaf/logger')
|
|
|
|
const slowCallback =
|
|
require.main === module
|
|
? (callback, error, data) => setTimeout(() => callback(error, data), 80)
|
|
: (callback, error, data) => callback(error, data)
|
|
|
|
// NOTE: Errors are not propagated to the caller
|
|
const handleAPIError = (source, id, error, callback) => {
|
|
logger.warn(`Errors in ${source} with id=${id}`, error)
|
|
if (typeof error === 'string' && error.match(/429$/)) {
|
|
return setTimeout(callback, 1000 * 60 * 5)
|
|
}
|
|
slowCallback(callback)
|
|
}
|
|
|
|
/**
|
|
* @returns {Promise<{
|
|
* INVOICES_COLLECTED: string[],
|
|
* INVOICES_COLLECTED_SUCCESS: string[],
|
|
* USERS_COLLECTED: string[],
|
|
* }>}
|
|
*/
|
|
const main = async () => {
|
|
const attemptInvoiceCollection = (invoice, callback) => {
|
|
isAccountUsingPaypal(invoice, (error, isPaypal) => {
|
|
if (error || !isPaypal) {
|
|
return callback(error)
|
|
}
|
|
const accountId = invoice.account.url.match(/accounts\/(.*)/)[1]
|
|
if (USERS_COLLECTED.indexOf(accountId) > -1) {
|
|
logger.warn(`Skipping duplicate user ${accountId}`)
|
|
return callback()
|
|
}
|
|
INVOICES_COLLECTED.push(invoice.invoice_number)
|
|
USERS_COLLECTED.push(accountId)
|
|
if (DRY_RUN) {
|
|
return callback()
|
|
}
|
|
RecurlyWrapper.attemptInvoiceCollection(
|
|
invoice.invoice_number,
|
|
(error, response) => {
|
|
if (error) {
|
|
return handleAPIError(
|
|
'attemptInvoiceCollection',
|
|
invoice.invoice_number,
|
|
error,
|
|
callback
|
|
)
|
|
}
|
|
INVOICES_COLLECTED_SUCCESS.push(invoice.invoice_number)
|
|
slowCallback(callback, null)
|
|
}
|
|
)
|
|
})
|
|
}
|
|
|
|
const isAccountUsingPaypal = (invoice, callback) => {
|
|
const accountId = invoice.account.url.match(/accounts\/(.*)/)[1]
|
|
RecurlyWrapper.getBillingInfo(accountId, (error, response) => {
|
|
if (error) {
|
|
return handleAPIError('billing info', accountId, error, callback)
|
|
}
|
|
if (response.billing_info.paypal_billing_agreement_id) {
|
|
return slowCallback(callback, null, true)
|
|
}
|
|
slowCallback(callback, null, false)
|
|
})
|
|
}
|
|
|
|
const attemptInvoicesCollection = callback => {
|
|
RecurlyWrapper.getPaginatedEndpoint(
|
|
'invoices',
|
|
{ state: 'past_due' },
|
|
(error, invoices) => {
|
|
logger.info('invoices', invoices.length)
|
|
if (error) {
|
|
return callback(error)
|
|
}
|
|
async.eachSeries(invoices, attemptInvoiceCollection, callback)
|
|
}
|
|
)
|
|
}
|
|
const argv = minimist(process.argv.slice(2))
|
|
const DRY_RUN = argv.n !== undefined
|
|
const INVOICES_COLLECTED = []
|
|
const INVOICES_COLLECTED_SUCCESS = []
|
|
const USERS_COLLECTED = []
|
|
|
|
return new Promise(resolve => {
|
|
attemptInvoicesCollection(error => {
|
|
logger.info(
|
|
`DONE (DRY_RUN=${DRY_RUN}). ${INVOICES_COLLECTED.length} invoices collection attempts for ${USERS_COLLECTED.length} users. ${INVOICES_COLLECTED_SUCCESS.length} successful collections`
|
|
)
|
|
console.dir(
|
|
{
|
|
INVOICES_COLLECTED,
|
|
INVOICES_COLLECTED_SUCCESS,
|
|
USERS_COLLECTED,
|
|
},
|
|
{ maxArrayLength: null }
|
|
)
|
|
|
|
if (error) {
|
|
throw error
|
|
}
|
|
|
|
if (INVOICES_COLLECTED_SUCCESS.length === 0) {
|
|
throw new Error('No invoices collected')
|
|
}
|
|
|
|
resolve({
|
|
INVOICES_COLLECTED,
|
|
INVOICES_COLLECTED_SUCCESS,
|
|
USERS_COLLECTED,
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
if (require.main === module) {
|
|
main()
|
|
.then(() => {
|
|
logger.error('Done.')
|
|
process.exit(0)
|
|
})
|
|
.catch(err => {
|
|
logger.error('Error', err)
|
|
process.exit(1)
|
|
})
|
|
}
|
|
|
|
module.exports = { main }
|