Skip to content
Merged
14 changes: 6 additions & 8 deletions src/commands/data/maintenances/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {flags as Flags} from '@heroku-cli/command'
import {Command, flags as Flags} from '@heroku-cli/command'
import * as Heroku from '@heroku-cli/schema'
import {hux} from '@heroku/heroku-cli-util'
import {HerokuSDK} from '@heroku/sdk'
import {ux} from '@oclif/core/ux'

import BaseCommand from '../../../lib/data/base-command.js'
import {Maintenance, MaintenanceStatus} from '../../../lib/data/types.js'
import {constructSortFilterTableOptions, constructTableColumns, outputCSV} from '../../../lib/utils/table-utils.js'

export default class DataMaintenancesIndex extends BaseCommand {
export default class DataMaintenancesIndex extends Command {
static description = 'list maintenances for an app\'s data addons'
static examples = [
'$ heroku data:maintenances --app production-app',
Expand Down Expand Up @@ -50,13 +50,11 @@ export default class DataMaintenancesIndex extends BaseCommand {
private async fetchMaintenances(appName: string) {
ux.action.start('Fetching maintenances')
const {body: app} = await this.heroku.get<Heroku.App>(`/apps/${appName}`)
const {body: {maintenances}} = await this.dataApi.get<{maintenances: Maintenance[]}>(
`/data/maintenances/v1/apps/${app.id}`,
this.dataApi.defaults,
)
const {data} = new HerokuSDK()
const result = await data.maintenance.infoByApp(app.id!) as unknown as {maintenances: Maintenance[]}
ux.action.stop()

return maintenances
return result.maintenances
}

private getTableColumns(extended: boolean, columns: string | undefined) {
Expand Down
23 changes: 16 additions & 7 deletions src/commands/data/maintenances/info.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {flags} from '@heroku-cli/command'
import {Command, flags} from '@heroku-cli/command'
import {color, hux, utils} from '@heroku/heroku-cli-util'
import {HerokuSDK} from '@heroku/sdk'
import {Args, ux} from '@oclif/core'

import BaseCommand from '../../../lib/data/base-command.js'
import {Maintenance} from '../../../lib/data/types.js'
import {lazyModuleLoader} from '../../../lib/lazy-module-loader.js'

Expand All @@ -11,7 +11,7 @@ interface StyledMaintenance extends Maintenance {
duration_approximate?: string;
}

export default class DataMaintenancesInfo extends BaseCommand {
export default class DataMaintenancesInfo extends Command {
static args = {
addon: Args.string({
description: 'data addon to show maintenance for',
Expand Down Expand Up @@ -87,10 +87,19 @@ export default class DataMaintenancesInfo extends BaseCommand {
const addon = await addonResolver.resolve(args.addon, app)

ux.action.start(`Fetching maintenance for ${color.addon(addon.name!)}`)
const {body: maintenance} = await this.dataApi.get<Maintenance>(
`/data/maintenances/v1/${addon!.id}`,
this.dataApi.defaults,
)
const {data} = new HerokuSDK()
let maintenance: Maintenance
try {
maintenance = await data.maintenance.info(addon.id!) as unknown as Maintenance
} catch (error: any) {
ux.action.stop()
if (error.statusCode === 404) {
this.error('no maintenance found for this add-on')
}

throw error
}

ux.action.stop()

if (json) {
Expand Down
7 changes: 3 additions & 4 deletions src/commands/data/maintenances/run.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {flags as Flags} from '@heroku-cli/command'
import * as Heroku from '@heroku-cli/schema'
import {color, utils} from '@heroku/heroku-cli-util'
import {HerokuSDK} from '@heroku/sdk'
import {Args, ux} from '@oclif/core'

import BaseCommand from '../../../lib/data/base-command.js'
Expand Down Expand Up @@ -63,10 +64,8 @@ export default class DataMaintenancesRun extends BaseCommand {
await this.confirmMaintenanceMode(addon, confirm, force || false)

ux.action.start('Triggering maintenance')
await this.dataApi.post(
`/data/maintenances/v1/${addon.id}/run`,
this.dataApi.defaults,
)
const {data} = new HerokuSDK()
await data.maintenance.run(addon.id!)
ux.action.stop('maintenance triggered')

if (wait) {
Expand Down
30 changes: 10 additions & 20 deletions src/commands/data/maintenances/schedule.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {flags} from '@heroku-cli/command'
import {Command, flags} from '@heroku-cli/command'
import * as Heroku from '@heroku-cli/schema'
import {color, utils} from '@heroku/heroku-cli-util'
import {HerokuSDK} from '@heroku/sdk'
import {Args, ux} from '@oclif/core'

import BaseCommand from '../../../lib/data/base-command.js'
import {Maintenance} from '../../../lib/data/types.js'
import {lazyModuleLoader} from '../../../lib/lazy-module-loader.js'

export default class DataMaintenancesSchedule extends BaseCommand {
export default class DataMaintenancesSchedule extends Command {
static args = {
addon: Args.string({
description: 'addon to schedule or re-schedule maintenance for',
Expand Down Expand Up @@ -36,11 +36,8 @@ export default class DataMaintenancesSchedule extends BaseCommand {
}),
}

protected async computeDelayWeeks(addon: Heroku.AddOn, week: string, differenceInCalendarWeeks: any) {
const {body: maintenance} = await this.dataApi.get<Maintenance>(
`/data/maintenances/v1/${addon!.id}`,
this.dataApi.defaults,
)
protected async computeDelayWeeks(addon: Heroku.AddOn, week: string, differenceInCalendarWeeks: any, data: HerokuSDK['data']) {
const maintenance = await data.maintenance.info(addon.id!) as unknown as Maintenance

const scheduled = (maintenance.status === 'completed' || maintenance.scheduled_for === null)
? Date.now()
Expand All @@ -62,25 +59,18 @@ export default class DataMaintenancesSchedule extends BaseCommand {
const {app, week, weeks} = flags

const addon = await addonResolver.resolve(args.addon, app)
const {data} = new HerokuSDK()

const delayWeeks = week === undefined
? weeks
: await this.computeDelayWeeks(addon, week, differenceInCalendarWeeks)
: await this.computeDelayWeeks(addon, week, differenceInCalendarWeeks, data)

await this.scheduleMaintenance(addon, delayWeeks)
await this.scheduleMaintenance(addon, delayWeeks, data)
}

protected async scheduleMaintenance(addon: Heroku.AddOn, delayWeeks: string) {
protected async scheduleMaintenance(addon: Heroku.AddOn, delayWeeks: string, data: HerokuSDK['data']) {
ux.action.start(`Scheduling maintenance for ${color.addon(addon.name!)}`)
const {body: schedule} = await this.dataApi.post<Maintenance>(
`/data/maintenances/v1/${addon.id}/schedule`,
{
...this.dataApi.defaults,
body: {
delay_weeks: delayWeeks,
},
},
)
const schedule = await data.maintenance.schedule(addon.id!, {delay_weeks: delayWeeks}) as unknown as Maintenance
ux.action.stop('maintenance scheduled')

const alreadyScheduled = schedule.previously_scheduled_for !== null
Expand Down
7 changes: 3 additions & 4 deletions src/commands/data/maintenances/wait.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {flags as Flags} from '@heroku-cli/command'
import {color, utils} from '@heroku/heroku-cli-util'
import {HerokuSDK} from '@heroku/sdk'
import {Args, ux} from '@oclif/core'

import BaseCommand from '../../../lib/data/base-command.js'
Expand Down Expand Up @@ -30,10 +31,8 @@ export default class DataMaintenancesWait extends BaseCommand {
this.error('You can\'t await maintenance on an Essential tier database.')
}

const {body: maintenance} = await this.dataApi.get<Maintenance>(
`/data/maintenances/v1/${addon.id}`,
this.dataApi.defaults,
)
const {data} = new HerokuSDK()
const maintenance = await data.maintenance.info(addon.id!) as unknown as Maintenance

if (maintenance.status !== MaintenanceStatus.running) {
this.error(`There currently isn't any maintenance in progress for ${color.addon(addon.name!)}`)
Expand Down
12 changes: 5 additions & 7 deletions src/commands/data/maintenances/window/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {flags as Flags} from '@heroku-cli/command'
import {Command, flags as Flags} from '@heroku-cli/command'
import {color, hux, utils} from '@heroku/heroku-cli-util'
import {HerokuSDK} from '@heroku/sdk'
import {Args, ux} from '@oclif/core'

import BaseCommand from '../../../../lib/data/base-command.js'
import {Window} from '../../../../lib/data/types.js'

export default class DataMaintenancesWindow extends BaseCommand {
export default class DataMaintenancesWindow extends Command {
static args = {
addon: Args.string({
description: 'addon to show window for',
Expand All @@ -29,10 +29,8 @@ export default class DataMaintenancesWindow extends BaseCommand {
const addon = await addonResolver.resolve(args.addon, flags.app)

ux.action.start(`Fetching maintenance window for ${color.addon(addon.name!)}`)
const {body: window} = await this.dataApi.get<Window>(
`/data/maintenances/v1/${addon.id}/window`,
this.dataApi.defaults,
)
const {data} = new HerokuSDK()
const window = await data.maintenance.window(addon.id!) as unknown as Window
ux.action.stop()

if (flags.json) {
Expand Down
21 changes: 8 additions & 13 deletions src/commands/data/maintenances/window/update.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {flags as Flags} from '@heroku-cli/command'
import {Command, flags as Flags} from '@heroku-cli/command'
import {color, hux, utils} from '@heroku/heroku-cli-util'
import {HerokuSDK} from '@heroku/sdk'
import {Args, ux} from '@oclif/core'

import BaseCommand from '../../../../lib/data/base-command.js'
import {Window} from '../../../../lib/data/types.js'

export default class DataMaintenancesWindowUpdate extends BaseCommand {
export default class DataMaintenancesWindowUpdate extends Command {
static args = {
addon: Args.string({
description: 'addon to change window for',
Expand Down Expand Up @@ -40,16 +40,11 @@ export default class DataMaintenancesWindowUpdate extends BaseCommand {
const combinedWindowLabel = `${args.day_of_week} ${args.time_of_day}`
ux.action.start(`Setting maintenance window for ${color.addon(addon.name!)} to ${combinedWindowLabel}`)

const {body: result} = await this.dataApi.post<Window>(
`/data/maintenances/v1/${addon.id}/window`,
{
...this.dataApi.defaults,
body: {
day_of_week: args.day_of_week,
time_of_day: args.time_of_day,
},
},
)
const {data} = new HerokuSDK()
const result = await data.maintenance.updateWindow(addon.id!, {
day_of_week: args.day_of_week,
time_of_day: args.time_of_day,
}) as unknown as Window
ux.action.stop()

if (flags.json) {
Expand Down
6 changes: 4 additions & 2 deletions src/commands/pg/backups/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {Command, flags} from '@heroku-cli/command'
import {color, hux, utils} from '@heroku/heroku-cli-util'
import {color, hux} from '@heroku/heroku-cli-util'
import {HerokuSDK} from '@heroku/sdk'
import {ux} from '@oclif/core/ux'

import type {BackupTransfer} from '../../../lib/pg/types.js'
Expand All @@ -24,7 +25,8 @@ export default class Index extends Command {
public async run(): Promise<void> {
const {flags: {app}} = await this.parse(Index)

const {body: transfers} = await this.heroku.get<BackupTransfer[]>(`/client/v11/apps/${app}/transfers`, {hostname: utils.pg.host()})
const {data} = new HerokuSDK()
const transfers = await data.transfer.listByApp(app) as unknown as BackupTransfer[]
// NOTE that the sort order is descending
transfers.sort((transferA, transferB) => transferB.created_at.localeCompare(transferA.created_at))

Expand Down
12 changes: 3 additions & 9 deletions src/commands/pg/credentials.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Command, flags} from '@heroku-cli/command'
import * as Heroku from '@heroku-cli/schema'
import {hux, utils} from '@heroku/heroku-cli-util'
import {HerokuSDK} from '@heroku/sdk'
import {Args} from '@oclif/core'

import type {NonAdvancedCredentialInfo} from '../../lib/data/types.js'
Expand Down Expand Up @@ -32,15 +33,8 @@ export default class Credentials extends Command {
const dbResolver = new utils.pg.DatabaseResolver(this.heroku)
const {addon} = await dbResolver.getAttachment(app, database)

const {body: credentials} = await this.heroku.get<NonAdvancedCredentialInfo[]>(
`/postgres/v0/databases/${addon.id}/credentials`,
{
headers: {
Authorization: `Basic ${Buffer.from(`:${this.heroku.auth}`).toString('base64')}`,
},
hostname: utils.pg.host(),
},
)
const {data} = new HerokuSDK()
const credentials = await data.postgresDatabase.listCredentials(addon.id) as unknown as NonAdvancedCredentialInfo[]
const sortedCredentials = this.sortByDefaultAndName(credentials)
const {body: attachments} = await this.heroku.get<Required<Heroku.AddOnAttachment>[]>(`/addons/${addon.id}/addon-attachments`)

Expand Down
11 changes: 4 additions & 7 deletions src/commands/pg/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as Heroku from '@heroku-cli/schema'
import {
color, hux, pg, utils,
} from '@heroku/heroku-cli-util'
import {HerokuSDK} from '@heroku/sdk'
import {Args, ux} from '@oclif/core'

import {PgDatabaseTenant} from '../../lib/pg/types.js'
Expand Down Expand Up @@ -49,19 +50,15 @@ export default class Info extends Command {
}
}

const {data} = new HerokuSDK()
let dbs: DBObject[] = await Promise.all(addons.map(async addon => {
const pgResponse = await this.heroku.get<PgDatabaseTenant>(
`/client/v11/databases/${addon.id}`,
{
hostname: utils.pg.host(),
},
)
const dbInfo = await (data.database.info(addon.id) as Promise<PgDatabaseTenant>)
.catch(error => {
if (error.statusCode !== 404)
throw error
ux.warn(`${color.datastore(addon.name)} is not yet provisioned.\nRun ${color.code('heroku addons:wait')} to wait until the db is provisioned.`)
return null
})
const {body: dbInfo} = pgResponse || {body: null}
return {
addon,
config,
Expand Down
19 changes: 12 additions & 7 deletions src/commands/pg/upgrade/cancel.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {Command, flags} from '@heroku-cli/command'
import {color, utils} from '@heroku/heroku-cli-util'
import {HerokuSDK} from '@heroku/sdk'
import {Args, ux} from '@oclif/core'
import tsheredoc from 'tsheredoc'

import ConfirmCommand from '../../../lib/confirm-command.js'
import {PgDatabase, PgUpgradeError, PgUpgradeResponse} from '../../../lib/pg/types.js'
import {PgDatabase} from '../../../lib/pg/types.js'
import {formatResponseWithCommands} from '../../../lib/pg/util.js'
import {nls} from '../../../nls.js'

Expand Down Expand Up @@ -36,7 +37,8 @@ export default class Upgrade extends Command {
if (utils.pg.isEssentialDatabase(db))
ux.error(`You can't use ${color.code('pg:upgrade:cancel')} on Essential-tier databases. You can only use this command on Standard-tier and higher leader databases.`)

const {body: replica} = await this.heroku.get<PgDatabase>(`/client/v11/databases/${db.id}`, {hostname: utils.pg.host()})
const {data} = new HerokuSDK()
const replica = await data.database.info(db.id) as unknown as PgDatabase
if (replica.following)
ux.error(`You can't use ${color.code('pg:upgrade:cancel')} on follower databases. You can only use this command on Standard-tier and higher leader databases.`)

Expand All @@ -49,11 +51,14 @@ export default class Upgrade extends Command {

try {
ux.action.start(`Cancelling upgrade on ${color.addon(db.name)}`)
const response = await this.heroku.post<PgUpgradeResponse>(`/client/v11/databases/${db.id}/upgrade/cancel`, {body: {}, hostname: utils.pg.host()})
ux.action.stop('done\n' + formatResponseWithCommands(response.body.message))
} catch (error) {
const response = error as PgUpgradeError
ux.error(formatResponseWithCommands(response.body.message) + `\n\nError ID: ${response.body.id}`)
const response = await data.database.cancelUpgrade(db.id) as unknown as {message: string}
ux.action.stop('done\n' + formatResponseWithCommands(response.message))
} catch (error: any) {
if (error.id && error.message) {
ux.error(formatResponseWithCommands(error.message) + `\n\nError ID: ${error.id}`)
} else {
throw error
}
}
}
}
Loading
Loading