Skip to content
Merged
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
15 changes: 11 additions & 4 deletions src/commands/addons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ const ADDON_EXPANSION_HEADERS = {
'Accept-Expansion': 'addon_service,plan',
}

// SDK resource types use required fields while @heroku-cli/schema uses
// optional fields for the same shapes. This cast bridges the gap until
// the codebase fully migrates off @heroku-cli/schema.
function asSchemaBased<T>(value: unknown): T {
return value as T
}

const topic = 'addons'

export default class Addons extends Command {
Expand Down Expand Up @@ -72,22 +79,22 @@ async function addonGetter(api: APIClient, app?: string) {
let attachmentsResponse: null | Promise<Heroku.AddOnAttachment[]> = null
let addonsResponse: Promise<Heroku.AddOn[]>
if (app) { // don't display attachments globally
addonsResponse = platformWithExpansion.addOn.listByApp(app) as unknown as Promise<Heroku.AddOn[]>
addonsResponse = platformWithExpansion.addOn.listByApp(app).then(asSchemaBased<Heroku.AddOn[]>)
const sudoHeaders = JSON.parse(process.env.HEROKU_HEADERS || '{}')
// eslint-disable-next-line unicorn/prefer-ternary
if (sudoHeaders['X-Heroku-Sudo'] && !sudoHeaders['X-Heroku-Sudo-User']) {
// because the root /addon-attachments endpoint won't include relevant
// attachments when sudo-ing for another app, we will use the more
// specific API call and sacrifice listing foreign attachments.
attachmentsResponse = platform.addOnAttachment.listByApp(app) as unknown as Promise<Heroku.AddOnAttachment[]>
attachmentsResponse = platform.addOnAttachment.listByApp(app).then(asSchemaBased<Heroku.AddOnAttachment[]>)
} else {
// In order to display all foreign attachments, we'll get out entire
// attachment list
attachmentsResponse = platform.addOnAttachment.list() as unknown as Promise<Heroku.AddOnAttachment[]>
attachmentsResponse = platform.addOnAttachment.list().then(asSchemaBased<Heroku.AddOnAttachment[]>)
}
} else {
// The global /addons endpoint doesn't support Accept-Expansion.
addonsResponse = platform.addOn.list() as unknown as Promise<Heroku.AddOn[]>
addonsResponse = platform.addOn.list().then(asSchemaBased<Heroku.AddOn[]>)
}

// Get addons and attachments in parallel
Expand Down
11 changes: 5 additions & 6 deletions src/commands/pg/backups/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import {Command, flags} from '@heroku-cli/command'
import {color, hux} from '@heroku/heroku-cli-util'
import {color, hux, utils} from '@heroku/heroku-cli-util'
import {HerokuSDK} from '@heroku/sdk'
import {transferExtensions} from '@heroku/sdk/extensions/data'
import {ux} from '@oclif/core/ux'

import type {BackupTransfer} from '../../../lib/pg/types.js'

import backupsFactory from '../../../lib/pg/backups.js'
import {listTransfersByApp} from '../../../lib/pg/sdk-adapter.js'
import type {BackupTransfer} from '../../../lib/pg/types.js'

export default class Index extends Command {
static description = 'list database backups'
Expand All @@ -26,8 +25,8 @@ export default class Index extends Command {
public async run(): Promise<void> {
const {flags: {app}} = await this.parse(Index)

const {data} = new HerokuSDK()
const transfers = await listTransfersByApp(data, app)
const {data} = new HerokuSDK({extensions: [transferExtensions]})
const transfers = await data.transfer.listByApp(app) as BackupTransfer[]
// NOTE that the sort order is descending
transfers.sort((transferA, transferB) => transferB.created_at.localeCompare(transferA.created_at))

Expand Down
19 changes: 9 additions & 10 deletions src/commands/pg/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ 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 {postgresDatabaseExtensions} from '@heroku/sdk/extensions/data'
import type {CredentialInfo} from '@heroku/sdk/resources/data/postgres-database'
import {Args} from '@oclif/core'

import type {NonAdvancedCredentialInfo} from '../../lib/data/types.js'

import {listCredentials} from '../../lib/pg/sdk-adapter.js'
import {presentCredentialAttachments} from '../../lib/pg/util.js'
import {huxTableNoWrapOptions} from '../../lib/utils/table-utils.js'
import {nls} from '../../nls.js'
Expand All @@ -23,7 +22,7 @@ export default class Credentials extends Command {
}
static topic = 'pg'

protected isDefaultCredential(cred: NonAdvancedCredentialInfo): boolean {
protected isDefaultCredential(cred: CredentialInfo): boolean {
return cred.name === 'default'
}

Expand All @@ -34,16 +33,16 @@ export default class Credentials extends Command {
const dbResolver = new utils.pg.DatabaseResolver(this.heroku)
const {addon} = await dbResolver.getAttachment(app, database)

const {data} = new HerokuSDK()
const credentials = await listCredentials(data, addon.id)
const {data} = new HerokuSDK({extensions: [postgresDatabaseExtensions]})
const credentials = await data.postgresDatabase.listCredentials(app, addon.name)
const sortedCredentials = this.sortByDefaultAndName(credentials)
const {body: attachments} = await this.heroku.get<Required<Heroku.AddOnAttachment>[]>(`/addons/${addon.id}/addon-attachments`)

const presentCredential = (cred: NonAdvancedCredentialInfo): string => {
const presentCredential = (cred: CredentialInfo): string => {
let credAttachments = [] as Required<Heroku.AddOnAttachment>[]
credAttachments = cred.name === 'default' ? attachments.filter(a => a.namespace === null) : attachments.filter(a => a.namespace === `credential:${cred.name}`)

return presentCredentialAttachments(app, credAttachments, sortedCredentials, cred.name)
return presentCredentialAttachments(app, credAttachments, sortedCredentials as any, cred.name)
}

hux.table(credentials, {
Expand All @@ -56,8 +55,8 @@ export default class Credentials extends Command {
}, huxTableNoWrapOptions(flags['no-wrap']))
}

protected sortByDefaultAndName(credentials: NonAdvancedCredentialInfo[]) {
return credentials.sort((a: NonAdvancedCredentialInfo, b: NonAdvancedCredentialInfo) => {
protected sortByDefaultAndName(credentials: CredentialInfo[]) {
return credentials.sort((a: CredentialInfo, b: CredentialInfo) => {
const isDefaultA = this.isDefaultCredential(a)
const isDefaultB = this.isDefaultCredential(b)

Expand Down
9 changes: 4 additions & 5 deletions src/commands/pg/upgrade/cancel.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import {Command, flags} from '@heroku-cli/command'
import {color, utils} from '@heroku/heroku-cli-util'
import {HerokuSDK} from '@heroku/sdk'
import {DatabaseCancelUpgradeResult} from '@heroku/types/data'
import {databaseExtensions} from '@heroku/sdk/extensions/data'
import {Args, ux} from '@oclif/core'
import tsheredoc from 'tsheredoc'

import ConfirmCommand from '../../../lib/confirm-command.js'
import {getDatabaseInfo} from '../../../lib/pg/sdk-adapter.js'
import {formatResponseWithCommands} from '../../../lib/pg/util.js'
import {nls} from '../../../nls.js'

Expand Down Expand Up @@ -38,8 +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 {data} = new HerokuSDK()
const replica = await getDatabaseInfo(data, db.id)
const {data} = new HerokuSDK({extensions: [databaseExtensions]})
const replica = await data.database.describe(app, db.name)
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 @@ -52,7 +51,7 @@ export default class Upgrade extends Command {

try {
ux.action.start(`Cancelling upgrade on ${color.addon(db.name)}`)
const response: DatabaseCancelUpgradeResult = await data.database.cancelUpgrade(db.id)
const response = await data.database.cancelUpgrade(app, db.name)
ux.action.stop('done\n' + formatResponseWithCommands(response.message))
} catch (error: any) {
if (error.id && error.message) {
Expand Down
8 changes: 4 additions & 4 deletions src/commands/pg/upgrade/dryrun.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {Command, flags} from '@heroku-cli/command'
import {color, utils} from '@heroku/heroku-cli-util'
import {HerokuSDK} from '@heroku/sdk'
import {databaseExtensions} from '@heroku/sdk/extensions/data'
import {Args, ux} from '@oclif/core'
import tsheredoc from 'tsheredoc'

import ConfirmCommand from '../../../lib/confirm-command.js'
import {dryRunUpgrade, getDatabaseInfo} from '../../../lib/pg/sdk-adapter.js'
import {formatResponseWithCommands} from '../../../lib/pg/util.js'
import {nls} from '../../../nls.js'

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

const versionPhrase = version ? heredoc(`Postgres version ${version}`) : heredoc('the latest supported Postgres version')
const {data} = new HerokuSDK()
const replica = await getDatabaseInfo(data, db.id)
const {data} = new HerokuSDK({extensions: [databaseExtensions]})
const replica = await data.database.describe(app, db.name)
if (replica.following)
ux.error(`You can't use ${color.code('pg:upgrade:dryrun')} on follower databases. You can only use this command on Standard-tier and higher leader databases.`)

Expand All @@ -50,7 +50,7 @@ export default class Upgrade extends Command {

try {
ux.action.start(`Starting a test upgrade on ${color.datastore(db.name)}`)
const response = await dryRunUpgrade(data, db.id, {version})
const response = await data.database.dryRunUpgrade(app, db.name, {version})
ux.action.stop('done\n' + formatResponseWithCommands(response.message))
} catch (error: any) {
if (error.id && error.message) {
Expand Down
8 changes: 4 additions & 4 deletions src/commands/pg/upgrade/prepare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {Args, ux} from '@oclif/core'
import tsheredoc from 'tsheredoc'

import ConfirmCommand from '../../../lib/confirm-command.js'
import {getDatabaseInfo, prepareUpgrade} from '../../../lib/pg/sdk-adapter.js'
import {databaseExtensions} from '@heroku/sdk/extensions/data'
import {formatResponseWithCommands} from '../../../lib/pg/util.js'
import {nls} from '../../../nls.js'

Expand Down Expand Up @@ -39,8 +39,8 @@ export default class Upgrade extends Command {
ux.error(`You can only use ${color.code('heroku pg:upgrade:prepare')} on Standard-tier and higher leader databases. For Essential-tier databases, use ${color.code('heroku pg:upgrade:run')} instead.`)

const versionPhrase = version ? heredoc(`Postgres version ${version}`) : heredoc('the latest supported Postgres version')
const {data} = new HerokuSDK()
const replica = await getDatabaseInfo(data, db.id)
const {data} = new HerokuSDK({extensions: [databaseExtensions]})
const replica = await data.database.describe(app, db.name)

if (replica.following)
ux.error(`You can only use ${color.code('heroku pg:upgrade:prepare')} on Standard-tier and higher leader databases. For follower databases, use ${color.code('heroku pg:upgrade:run')} instead.`)
Expand All @@ -52,7 +52,7 @@ export default class Upgrade extends Command {

try {
ux.action.start(`Preparing upgrade on ${color.addon(db.name)}`)
const response = await prepareUpgrade(data, db.id, {version})
const response = await data.database.prepareUpgrade(app, db.name, {version})
ux.action.stop(heredoc(`done\n${formatResponseWithCommands(response.message)}`))
} catch (error: any) {
if (error.id && error.message) {
Expand Down
8 changes: 4 additions & 4 deletions src/commands/pg/upgrade/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {Args, ux} from '@oclif/core'
import tsheredoc from 'tsheredoc'

import ConfirmCommand from '../../../lib/confirm-command.js'
import {getDatabaseInfo, runUpgrade} from '../../../lib/pg/sdk-adapter.js'
import {databaseExtensions} from '@heroku/sdk/extensions/data'
import {databaseNameFromUrl, formatResponseWithCommands} from '../../../lib/pg/util.js'
import {nls} from '../../../nls.js'

Expand Down Expand Up @@ -58,8 +58,8 @@ export default class Upgrade extends Command {
ux.error(`You can only use ${color.code('pg:upgrade:*')} commands on Essential-* and higher plans.`)

const versionPhrase = version ? heredoc(`Postgres version ${version}`) : heredoc('the latest supported Postgres version')
const {data} = new HerokuSDK()
const replica = await getDatabaseInfo(data, db.id)
const {data} = new HerokuSDK({extensions: [databaseExtensions]})
const replica = await data.database.describe(app, db.name)

if (utils.pg.isEssentialDatabase(db)) {
await new ConfirmCommand().confirm(app, confirm, heredoc(`
Expand Down Expand Up @@ -89,7 +89,7 @@ export default class Upgrade extends Command {

try {
ux.action.start(`Starting upgrade on ${color.datastore(db.name)}`)
const response = await runUpgrade(data, db.id, {version})
const response = await data.database.runUpgrade(app, db.name, {version})
ux.action.stop(heredoc(`done\n${formatResponseWithCommands(response.message)}`))
} catch (error: any) {
if (error.id && error.message) {
Expand Down
10 changes: 5 additions & 5 deletions src/commands/pg/upgrade/wait.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import debug from 'debug'
import tsheredoc from 'tsheredoc'

import notify from '../../../lib/notify.js'
import {getUpgradeWaitStatus} from '../../../lib/pg/sdk-adapter.js'
import {PgUpgradeStatus} from '../../../lib/pg/types.js'
import {databaseExtensions} from '@heroku/sdk/extensions/data'
import type {DatabaseUpgradeWaitResult} from '@heroku/sdk/resources/data/database'
import {formatResponseWithCommands} from '../../../lib/pg/util.js'
import {nls} from '../../../nls.js'

Expand Down Expand Up @@ -50,17 +50,17 @@ export default class Wait extends Command {
const dbName = args.database
const pgDebug = debug('pg')

const {data} = new HerokuSDK()
const {data} = new HerokuSDK({extensions: [databaseExtensions]})
const waitFor = async (db: pg.ExtendedAddonAttachment['addon']) => {
const interval = (!waitInterval || waitInterval < 0) ? 5 : waitInterval
let status: PgUpgradeStatus
let status: DatabaseUpgradeWaitResult
let waiting = false
let retries = 20
const notFoundMessage = 'Waiting to provision...'

while (true) {
try {
status = await getUpgradeWaitStatus(data, db.id)
status = await data.database.upgradeWaitStatus(app, db.name)
} catch (error: any) {
if (!retries || error.statusCode !== 404) {
pgDebug(error)
Expand Down
12 changes: 6 additions & 6 deletions src/commands/pipelines/create.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Command, flags} from '@heroku-cli/command'
import {StageCompletion} from '@heroku-cli/command/lib/completions.js'
import * as color from '@heroku/heroku-cli-util/color'
import {createPlatformClient} from '@heroku/sdk/platform'
import {HerokuSDK} from '@heroku/sdk'
import {PipelineCreateOpts} from '@heroku/types/3.sdk'
import {Args, ux} from '@oclif/core'
import {type Answers, type InputQuestion, type ListQuestion} from 'inquirer'
Expand Down Expand Up @@ -84,12 +84,12 @@ export default class Create extends Command {

const ownerType = teamName ? 'team' : 'user'

const heroku = createPlatformClient()
const {platform} = new HerokuSDK()

// If team or org is not specified, we assign ownership to the user creating
const ownerRecord = teamName
? await heroku.team.info(teamName)
: await heroku.account.infoByUser('~')
? await platform.team.info(teamName)
: await platform.account.infoByUser('~')
const ownerID = ownerRecord.id!

const answers: Answers = await inquirer.prompt(questions)
Expand All @@ -103,11 +103,11 @@ export default class Create extends Command {
name,
owner: {id: ownerID, type: ownerType},
}
const pipeline = await heroku.pipeline.create(body)
const pipeline = await platform.pipeline.create(body)
ux.action.stop()

ux.action.start(`Adding ${color.app(app)} to ${color.pipeline(pipeline.name || '')} pipeline as ${stage}`)
await heroku.pipelineCoupling.create({
await platform.pipelineCoupling.create({
app,
pipeline: pipeline.id!,
stage,
Expand Down
6 changes: 3 additions & 3 deletions src/commands/pipelines/destroy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Command} from '@heroku-cli/command'
import * as Heroku from '@heroku-cli/schema'
import * as color from '@heroku/heroku-cli-util/color'
import {createPlatformClient} from '@heroku/sdk/platform'
import {HerokuSDK} from '@heroku/sdk'
import {Args, ux} from '@oclif/core'

import disambiguate from '../../lib/pipelines/disambiguate.js'
Expand All @@ -23,8 +23,8 @@ export default class PipelinesDestroy extends Command {
const pipeline: Heroku.Pipeline = await disambiguate(this.heroku, args.pipeline)

ux.action.start(`Destroying ${color.pipeline(pipeline.name!)} pipeline`)
const heroku = createPlatformClient()
await heroku.pipeline.delete(pipeline.id!)
const {platform} = new HerokuSDK()
await platform.pipeline.delete(pipeline.id!)
ux.action.stop()
}
}
6 changes: 3 additions & 3 deletions src/commands/pipelines/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Command, flags} from '@heroku-cli/command'
import {color, hux} from '@heroku/heroku-cli-util'
import {createPlatformClient} from '@heroku/sdk/platform'
import {HerokuSDK} from '@heroku/sdk'
import {ux} from '@oclif/core/ux'

export default class Pipelines extends Command {
Expand All @@ -15,8 +15,8 @@ export default class Pipelines extends Command {
async run() {
const {flags} = await this.parse(Pipelines)

const heroku = createPlatformClient()
const pipelines = await heroku.pipeline.list()
const {platform} = new HerokuSDK()
const pipelines = await platform.pipeline.list()

if (flags.json) {
hux.styledJSON(pipelines)
Expand Down
11 changes: 6 additions & 5 deletions src/commands/pipelines/promote.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type {PipelinePromotionTarget} from '@heroku/types/3.sdk'
import type {AppWithPipelineCoupling} from '@heroku/sdk/resources/platform/pipeline-coupling'

import {APIClient, Command, flags} from '@heroku-cli/command'
Expand Down Expand Up @@ -118,12 +119,12 @@ export default class Promote extends Command {

const appsByID = keyBy(allApps, 'id')

const styledTargets = promotionTargets.reduce((memo: Heroku.App, target: any) => {
const styledTargets = promotionTargets.reduce((memo: Record<string, string[]>, target: PipelinePromotionTarget) => {
const app = appsByID[target.app.id]
const details = [target.status]
const details: string[] = [target.status]

if (isFailed(target)) {
details.push(target.error_message)
details.push(target.error_message ?? '')
}

memo[app.name] = details
Expand Down Expand Up @@ -158,10 +159,10 @@ async function getCoupling(heroku: APIClient, app: string): Promise<Heroku.Pipel
return coupling
}

function isFailed(promotionTarget: Heroku.PipelinePromotionTarget) {
function isFailed(promotionTarget: PipelinePromotionTarget) {
return promotionTarget.status === 'failed'
}

function isSucceeded(promotionTarget: Heroku.PipelinePromotionTarget) {
function isSucceeded(promotionTarget: PipelinePromotionTarget) {
return promotionTarget.status === 'succeeded'
}
Loading
Loading