heroku/heroku-pg

View on GitHub
lib/pgbackups.js

Summary

Maintainability
D
2 days
Test Coverage
'use strict'
 
const co = require('co')
const cli = require('heroku-cli-util')
 
Function `prefix` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.
function prefix (transfer) {
if (transfer.from_type === 'pg_dump') {
if (transfer.to_type === 'pg_restore') {
return 'c'
} else {
return transfer.schedule ? 'a' : 'b'
}
} else {
if (transfer.to_type === 'pg_restore') {
return 'r'
} else {
return 'b'
}
}
}
 
Function `exports` has a Cognitive Complexity of 74 (exceeds 5 allowed). Consider refactoring.
module.exports = (context, heroku) => ({
filesize: (size, opts = {}) => {
Object.assign(opts, {
decimalPlaces: 2,
fixedDecimals: true
})
const bytes = require('bytes')
return bytes(size, opts)
},
transfer: {
num: co.wrap(function * (name) {
let m = name.match(/^[abcr](\d+)$/)
if (m) return parseInt(m[1])
m = name.match(/^o[ab]\d+$/)
if (m) {
const host = require('./host')()
let transfers = yield heroku.get(`/client/v11/apps/${context.app}/transfers`, {host})
let transfer = transfers.find(t => module.exports(context, heroku).transfer.name(t) === name)
if (transfer) return transfer.num
}
}),
name: transfer => {
let oldPGBName = transfer.options && transfer.options.pgbackups_name
if (oldPGBName) return `o${oldPGBName}`
return `${prefix(transfer)}${(transfer.num || '').toString().padStart(3, '0')}`
},
status: transfer => {
if (transfer.finished_at && transfer.succeeded) {
let warnings = transfer.warnings
if (warnings > 0) {
return `Finished with ${warnings} warnings`
} else {
return `Completed ${transfer.finished_at}`
}
} else if (transfer.finished_at) {
return `Failed ${transfer.finished_at}`
} else if (transfer.started_at) {
return `Running (processed ${module.exports(context, heroku).filesize(transfer.processed_bytes)})`
} else {
Avoid too many `return` statements within this function.
return 'Pending'
}
}
},
Function `wait` has 56 lines of code (exceeds 25 allowed). Consider refactoring.
wait: (action, transferID, interval, verbose, app) => {
if (app === undefined) {
app = context.app
}
const wait = require('co-wait')
const host = require('./host')()
const pgbackups = module.exports(context, heroku)
 
let shownLogs = []
let displayLogs = logs => {
for (let log of logs) {
if (shownLogs.find(l => l.created_at === log.created_at && l.message === log.message)) continue
shownLogs.push(log)
cli.log(`${log.created_at} ${log.message}`)
}
}
 
Function `poll` has 34 lines of code (exceeds 25 allowed). Consider refactoring.
let poll = co.wrap(function * () {
let tty = process.env.TERM !== 'dumb' && process.stderr.isTTY
let backup
let failures = 0
 
let quietUrl = `/client/v11/apps/${app}/transfers/${transferID}`
let verboseUrl = quietUrl + '?verbose=true'
 
let url = verbose ? verboseUrl : quietUrl
 
while (true) {
try {
backup = yield heroku.get(url, {host})
} catch (err) {
if (failures++ > 20) throw err
}
if (verbose) {
displayLogs(backup.logs)
} else if (tty) {
let msg = backup.started_at ? pgbackups.filesize(backup.processed_bytes) : 'pending'
let log = backup.logs && backup.logs.pop()
if (log) {
cli.action.status(`${msg}\n${log.created_at + ' ' + log.message}`)
} else {
cli.action.status(msg)
}
}
if (backup && backup.finished_at) {
if (backup.succeeded) return
else {
// logs is undefined unless verbose=true is passed
backup = yield heroku.get(verboseUrl, {host})
 
throw new Error(`An error occurred and the backup did not finish.
 
${backup.logs.slice(-5).map(l => l.message).join('\n')}
 
Run ${cli.color.cmd('heroku pg:backups:info ' + pgbackups.transfer.name(backup))} for more details.`)
}
}
yield wait(interval * 1000)
}
})
 
if (verbose) {
cli.log(`${action}...`)
return poll()
} else {
return cli.action(action, poll())
}
}
})