heroku/heroku-pg

View on GitHub
commands/copy.js

Summary

Maintainability
B
6 hrs
Test Coverage
'use strict'
 
const co = require('co')
const cli = require('heroku-cli-util')
 
Function `run` has a Cognitive Complexity of 18 (exceeds 5 allowed). Consider refactoring.
Function `run` has 60 lines of code (exceeds 25 allowed). Consider refactoring.
function * run (context, heroku) {
const url = require('url')
const host = require('../lib/host')
const pgbackups = require('../lib/pgbackups')(context, heroku)
const fetcher = require('../lib/fetcher')(heroku)
const {app, args, flags} = context
const interval = Math.max(3, parseInt(flags['wait-interval'])) || 3
 
let resolve = co.wrap(function * (db) {
if (db.match(/^postgres:\/\//)) {
// For the case an input is URL format
let uri = url.parse(db)
let dbname = uri.path ? uri.path.slice(1) : ''
let host = `${uri.hostname}:${uri.port || 5432}`
return {
name: dbname ? `database ${dbname} on ${host}` : `database on ${host}`,
url: db,
confirm: dbname || uri.host
}
} else {
// Other case (need to resolve attachment)
let attachment = yield fetcher.attachment(app, db)
if (!attachment) throw new Error(`${db} not found on ${cli.color.app(app)}`)
let [addon, config] = yield [
heroku.get(`/addons/${attachment.addon.name}`),
heroku.get(`/apps/${attachment.app.name}/config-vars`)
]
attachment.addon = addon
return {
name: attachment.name.replace(/^HEROKU_POSTGRESQL_/, '').replace(/_URL$/, ''),
url: config[attachment.name + '_URL'],
attachment,
confirm: app
}
}
})
 
// Get source and target from inputs
// source/target format:
// * url: postgres://foo/bar
// * config var: DATABASE_URL
// * just color: PINK
// * addon name: my-heroku-addon-name
// * app name + color/config: myapp::ORANGE
let [source, target] = yield [resolve(args.source), resolve(args.target)]
if (source.url === target.url) throw new Error('Cannot copy database onto itself')
 
Similar blocks of code found in 2 locations. Consider refactoring.
yield cli.confirmApp(target.confirm, flags.confirm, `WARNING: Destructive action
This command will remove all data from ${cli.color.yellow(target.name)}
Data from ${cli.color.yellow(source.name)} will then be transferred to ${cli.color.yellow(target.name)}`)
 
let copy
let attachment
Similar blocks of code found in 4 locations. Consider refactoring.
yield cli.action(`Starting copy of ${cli.color.yellow(source.name)} to ${cli.color.yellow(target.name)}`, co(function * () {
attachment = target.attachment || source.attachment
if (!attachment) throw new Error('Heroku PostgreSQL database must be source or target')
copy = yield heroku.post(`/client/v11/databases/${attachment.addon.id}/transfers`, {
body: {
from_name: source.name,
from_url: source.url,
to_name: target.name,
to_url: target.url
},
host: host(attachment.addon)
})
}))
 
if (source.attachment) {
let credentials = yield heroku.get(`/postgres/v0/databases/${source.attachment.addon.name}/credentials`,
{ host: host(source.attachment.addon) })
if (credentials.length > 1) {
cli.action.warn(`pg:copy will only copy your default credential and the data it has access to. Any additional credentials and data that only they can access will not be copied.`)
}
}
yield pgbackups.wait('Copying', copy.uuid, interval, flags.verbose, attachment.addon.app.name)
}
 
module.exports = {
topic: 'pg',
command: 'copy',
needsApp: true,
needsAuth: true,
description: 'copy all data from source db to target',
help: 'at least one of the databases must be a Heroku PostgreSQL DB',
args: [
{name: 'source'},
{name: 'target'}
],
flags: [
{name: 'wait-interval', hasValue: true},
{name: 'verbose'},
{name: 'confirm', hasValue: true}
],
run: cli.command({preauth: true}, co.wrap(run))
}