Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
/*
import color from '@heroku-cli/color'
import {color} from '@heroku-cli/color'
import {Command, flags} from '@heroku-cli/command'
import {Args, ux} from '@oclif/core'
import {hux} from '@heroku/heroku-cli-util'
import {utils} from '@heroku/heroku-cli-util'
import pgBackupsApi from '../../../lib/pg/backups'
import {sortBy} from 'lodash'
import type {BackupTransfer} from '../../../lib/pg/types'
import pgBackupsApi from '../../../lib/pg/backups.js'
import type {BackupTransfer} from '../../../lib/pg/types.js'

function status(backup: BackupTransfer) {
if (backup.succeeded) {
Expand Down Expand Up @@ -35,27 +33,27 @@ function compression(compressed: number, total: number) {
}

export default class Info extends Command {
static topic = 'pg';
static description = 'get information about a specific backup';
static topic = 'pg'
static description = 'get information about a specific backup'
static flags = {
app: flags.app({required: true}),
remote: flags.remote(),
};
}

static args = {
backup_id: Args.string({description: 'ID of the backup. If omitted, we use the last backup ID.'}),
};
}

getBackup = async (id: string | undefined, app: string) => {
let backupID
if (id) {
const {num} = pgBackupsApi(app, this.heroku)
backupID = await num(id)
const pgbackups = pgBackupsApi(app, this.heroku)
backupID = await pgbackups.num(id)
if (!backupID)
throw new Error(`Invalid ID: ${id}`)
} else {
let {body: transfers} = await this.heroku.get<BackupTransfer[]>(`/client/v11/apps/${app}/transfers`, {hostname: utils.pg.host()})
transfers = sortBy(transfers, 'created_at')
const {body: transfers} = await this.heroku.get<BackupTransfer[]>(`/client/v11/apps/${app}/transfers`, {hostname: utils.pg.host()})
transfers.sort((a, b) => a.created_at.localeCompare(b.created_at))
const backups = transfers.filter(t => t.from_type === 'pg_dump' && t.to_type === 'gof3r')
const lastBackup = backups.pop()
if (!lastBackup)
Expand All @@ -68,24 +66,24 @@ export default class Info extends Command {
}

displayBackup = (backup: BackupTransfer, app: string) => {
const {filesize, name} = pgBackupsApi(app, this.heroku)
hux.styledHeader(`Backup ${color.cyan(name(backup))}`)
const pgbackups = pgBackupsApi(app, this.heroku)
hux.styledHeader(`Backup ${color.cyan(pgbackups.name(backup))}`)
hux.styledObject({
Database: color.green(backup.from_name),
'Started at': backup.started_at,
'Finished at': backup.finished_at,
Status: status(backup),
Type: backup.schedule ? 'Scheduled' : 'Manual', 'Original DB Size': filesize(backup.source_bytes),
'Backup Size': `${filesize(backup.processed_bytes)}${backup.finished_at ? compression(backup.processed_bytes, backup.source_bytes) : ''}`,
Type: backup.schedule ? 'Scheduled' : 'Manual', 'Original DB Size': pgbackups.filesize(backup.source_bytes),
'Backup Size': `${pgbackups.filesize(backup.processed_bytes)}${backup.finished_at ? compression(backup.processed_bytes, backup.source_bytes) : ''}`,
}, ['Database', 'Started at', 'Finished at', 'Status', 'Type', 'Original DB Size', 'Backup Size'])
ux.log()
ux.stdout('\n')
}

displayLogs = (backup: BackupTransfer) => {
hux.styledHeader('Backup Logs')
for (const log of backup.logs)
ux.log(`${log.created_at} ${log.message}`)
ux.log()
ux.stdout(`${log.created_at} ${log.message}\n`)
ux.stdout('\n')
}

public async run(): Promise<void> {
Expand All @@ -98,4 +96,4 @@ export default class Info extends Command {
this.displayLogs(backup)
}
}
*/

Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
/*
import color from '@heroku-cli/color'
import {color} from '@heroku-cli/color'
import {Command, flags} from '@heroku-cli/command'
import {Args, ux} from '@oclif/core'
import heredoc from 'tsheredoc'
import confirmCommand from '../../../lib/confirmCommand'
import backupsFactory from '../../../lib/pg/backups'
import tsheredoc from 'tsheredoc'
import ConfirmCommand from '../../../lib/confirmCommand.js'
import backupsFactory from '../../../lib/pg/backups.js'
import {utils} from '@heroku/heroku-cli-util'
import type {BackupTransfer} from '../../../lib/pg/types'
import {nls} from '../../../nls'
import type {BackupTransfer} from '../../../lib/pg/types.js'
import {nls} from '../../../nls.js'

const heredoc = tsheredoc.default

function dropboxURL(url: string) {
if (url.match(/^https?:\/\/www\.dropbox\.com/) && !url.endsWith('dl=1')) {
Expand Down Expand Up @@ -79,7 +80,7 @@ export default class Restore extends Command {
const interval = Math.max(3, waitInterval)
const dbResolver = new utils.pg.DatabaseResolver(this.heroku)
const {addon: db} = await dbResolver.getAttachment(app as string, args.database)
const {name, wait} = backupsFactory(app, this.heroku)
const pgbackups = backupsFactory(app, this.heroku)
let backupURL
let backupName = args.backup

Expand All @@ -98,36 +99,35 @@ export default class Restore extends Command {

let backup
if (backupName) {
backup = backups.find(b => name(b) === backupName)
backup = backups.find(b => pgbackups.name(b) === backupName)
if (!backup)
throw new Error(`Backup ${color.cyan(backupName)} not found for ${color.app(backupApp)}`)
if (!backup.succeeded)
throw new Error(`Backup ${color.cyan(backupName)} for ${color.app(backupApp)} did not complete successfully`)
} else {
backup = backups.filter(b => b.succeeded).sort((a, b) => {
if (a.finished_at < b.finished_at) {
return -1
}

if (a.finished_at > b.finished_at) {
return 1
if (a.finished_at && b.finished_at) {
return a.finished_at.localeCompare(b.finished_at)
}

if (a.finished_at) return 1
if (b.finished_at) return -1
return 0
}).pop()
if (!backup) {
throw new Error(`No backups for ${color.app(backupApp)}. Capture one with ${color.cyan.bold('heroku pg:backups:capture')}`)
}

backupName = name(backup)
backupName = pgbackups.name(backup)
}

backupURL = backup.to_url
}

await confirmCommand(app, confirm)
const confirmCmd = new ConfirmCommand()
await confirmCmd.confirm(app, confirm)
ux.action.start(`Starting restore of ${color.cyan(backupName)} to ${color.yellow(db.name)}`)
ux.log(heredoc(`
ux.stdout(heredoc(`

Use Ctrl-C at any time to stop monitoring progress; the backup will continue restoring.
Use ${color.cyan.bold('heroku pg:backups')} to check progress.
Expand All @@ -139,11 +139,11 @@ export default class Restore extends Command {
})

ux.action.stop()
await wait('Restoring', restore.uuid, interval, verbose, db.app.id as string)
await pgbackups.wait('Restoring', restore.uuid, interval, verbose, db.app.id as string)
}

protected getSortedExtensions(extensions: string | null | undefined): string[] | undefined {
return extensions?.split(',').map(ext => ext.trim().toLowerCase()).sort()
}
}
*/

Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
/*
import color from '@heroku-cli/color'
import {color} from '@heroku-cli/color'
import {Command, flags} from '@heroku-cli/command'
import {Args, ux} from '@oclif/core'
import {utils} from '@heroku/heroku-cli-util'
import {PgDatabase} from '../../../lib/pg/types'
import {PgDatabase} from '../../../lib/pg/types.js'
import {HTTPError} from '@heroku/http-call'
import {nls} from '../../../nls'
import {nls} from '../../../nls.js'

type Timezone = {
PST: string
Expand Down Expand Up @@ -103,4 +102,4 @@ export default class Schedule extends Command {
ux.action.stop()
}
}
*/

Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
/*
import color from '@heroku-cli/color'
import {color} from '@heroku-cli/color'
import {Command, flags} from '@heroku-cli/command'
import {ux} from '@oclif/core'
import {hux} from '@heroku/heroku-cli-util'
import {utils} from '@heroku/heroku-cli-util'
import type {TransferSchedule} from '../../../lib/pg/types'
import type {TransferSchedule} from '../../../lib/pg/types.js'

export default class Schedules extends Command {
static topic = 'pg';
static description = 'list backup schedule';
static topic = 'pg'
static description = 'list backup schedule'
static flags = {
app: flags.app({required: true}),
remote: flags.remote(),
};
}

public async run(): Promise<void> {
const {flags} = await this.parse(Schedules)
Expand All @@ -25,9 +24,9 @@ export default class Schedules extends Command {
} else {
hux.styledHeader('Backup Schedules')
for (const s of schedules) {
ux.log(`${color.green(s.name)}: daily at ${s.hour}:00 ${s.timezone}`)
ux.stdout(`${color.green(s.name)}: daily at ${s.hour}:00 ${s.timezone}\n`)
}
}
}
}
*/

Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
/*
import color from '@heroku-cli/color'
import {color} from '@heroku-cli/color'
import {Command, flags} from '@heroku-cli/command'
import {Args, ux} from '@oclif/core'
import {utils} from '@heroku/heroku-cli-util'
import {TransferSchedule} from '../../../lib/pg/types'
import {nls} from '../../../nls'
import {TransferSchedule} from '../../../lib/pg/types.js'
import {nls} from '../../../nls.js'

export default class Unschedule extends Command {
static topic = 'pg';
static description = 'stop daily backups';
static topic = 'pg'
static description = 'stop daily backups'
static flags = {
app: flags.app({required: true}),
remote: flags.remote(),
};
}

static args = {
database: Args.string({description: `${nls('pg:database:arg:description')} ${nls('pg:database:arg:description:arbitrary:suffix')}`}),
};
}

public async run(): Promise<void> {
const {flags, args} = await this.parse(Unschedule)
Expand Down Expand Up @@ -57,4 +56,4 @@ export default class Unschedule extends Command {
ux.action.stop()
}
}
*/

44 changes: 44 additions & 0 deletions packages/cli/src/commands/pg/backups/url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {color} from '@heroku-cli/color'
import {Command, flags} from '@heroku-cli/command'
import {Args, ux} from '@oclif/core'
import {utils} from '@heroku/heroku-cli-util'
import pgBackupsApi from '../../../lib/pg/backups.js'
import type {BackupTransfer, PublicUrlResponse} from '../../../lib/pg/types.js'

export default class Url extends Command {
static topic = 'pg'
static description = 'get secret but publicly accessible URL of a backup'
static flags = {
app: flags.app({required: true}),
remote: flags.remote(),
}

static args = {
backup_id: Args.string({description: 'ID of the backup. If omitted, we use the last backup ID.'}),
}

public async run(): Promise<void> {
const {flags, args} = await this.parse(Url)
const {backup_id} = args
const {app} = flags

let num
if (backup_id) {
num = await pgBackupsApi(app, this.heroku).num(backup_id)
if (!num)
throw new Error(`Invalid Backup: ${backup_id}`)
} else {
const {body: transfers} = await this.heroku.get<BackupTransfer[]>(`/client/v11/apps/${app}/transfers`, {hostname: utils.pg.host()})
const succeededBackups = transfers.filter(t => t.succeeded && t.to_type === 'gof3r')
succeededBackups.sort((a, b) => a.created_at.localeCompare(b.created_at))
const lastBackup = succeededBackups.pop()
if (!lastBackup)
throw new Error(`No backups on ${color.app(app)}. Capture one with ${color.cyan.bold('heroku pg:backups:capture')}`)
num = lastBackup.num
}

const {body: info} = await this.heroku.post<PublicUrlResponse>(`/client/v11/apps/${app}/transfers/${num}/actions/public-url`, {hostname: utils.pg.host()})
ux.stdout(info.url + '\n')
}
}

45 changes: 0 additions & 45 deletions packages/cli/src/oldCommands/pg/backups/url.ts

This file was deleted.

3 changes: 3 additions & 0 deletions packages/cli/test/helpers/wrappers/backups-wrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Wrapper for backups ESM module to enable stubbing in tests
export {default} from '../../../src/lib/pg/backups.js'

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {stdout} from 'stdout-stderr'
// import Cmd from '../../../../../src/commands/pg/backups/info'
import Cmd from '../../../../../src/commands/pg/backups/info.js'
import runCommand from '../../../../helpers/runCommand.js'
import nock from 'nock'
import expectOutput from '../../../../helpers/utils/expectOutput.js'
Expand Down Expand Up @@ -116,9 +116,6 @@ const shouldInfo = function (cmdRun: (args: string[]) => Promise<any>) {
})
}

/*
describe('pg:backups:info', function () {
shouldInfo((args: string[]) => runCommand(Cmd, args))
})

*/
Loading
Loading