Skip to content

Commit

Permalink
refactor(addons-v5): move addons:destroy to CLI & upgrade to oclif (#…
Browse files Browse the repository at this point in the history
…2602)

* wip: working through tests. Found an issue with the codemod

* tests working

* wip

* fix: delete OG files

* wip on unit tests

* fix: change command to have a required arg and allow variable args

* fix: unit tests passing

* chore: delete unused files that were migrated.

* chore: clean up. Remove unused waitForAddonDeprovisioning

* refactor: migrate to core & TS. wip

* fix: unit tests passing

* fix: tests passing

* Delete unused files

* add alias. cleanup

* wip: converting resolve.unit.test. 'does not memoize errors' test is hanging and can't find a way to catch the error.

* wip: attempt to change test to account for @heroku-cli/command's APIClient now retrying on two_factor 403

* fix: test 500 instead of 403 that is handled by APIClient retry

* chore: clean up.
  • Loading branch information
ryandagg authored Jan 24, 2024
1 parent 85dff14 commit 25e68e7
Show file tree
Hide file tree
Showing 11 changed files with 795 additions and 271 deletions.
68 changes: 0 additions & 68 deletions packages/addons-v5/commands/addons/destroy.js

This file was deleted.

2 changes: 0 additions & 2 deletions packages/addons-v5/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ exports.topic = {

exports.commands = _.flatten([
require('./commands/addons'),
require('./commands/addons/destroy'),
require('./commands/addons/docs'),
require('./commands/addons/info'),
require('./commands/addons/open'),
Expand All @@ -85,4 +84,3 @@ exports.commands = _.flatten([
])

exports.resolve = require('./lib/resolve')
exports.destroyAddon = require('./lib/destroy_addon')
46 changes: 0 additions & 46 deletions packages/addons-v5/lib/destroy_addon.js

This file was deleted.

155 changes: 0 additions & 155 deletions packages/addons-v5/test/unit/commands/addons/destroy.unit.test.js

This file was deleted.

64 changes: 64 additions & 0 deletions packages/cli/src/commands/addons/destroy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* eslint-disable no-await-in-loop */

import color from '@heroku-cli/color'
import {Command, flags} from '@heroku-cli/command'
import {Args} from '@oclif/core'
import * as Heroku from '@heroku-cli/schema'
import notify from '../../lib/notify'
import confirmApp from '../../lib/apps/confirm-app'
import destroyAddon from '../../lib/addons/destroy_addon'
import {resolveAddon} from '../../lib/addons/resolve'
import {groupBy} from 'lodash'

export default class Destroy extends Command {
static topic = 'addons'
static description = 'permanently destroy an add-on resource'
static strict = false
static examples = ['addons:destroy [ADDON]... [flags]']
static aliases = ['addons:remove']
static flags = {
force: flags.boolean({char: 'f', description: 'allow destruction even if connected to other apps'}),
confirm: flags.string({char: 'c'}),
wait: flags.boolean({description: 'watch add-on destruction status and exit when complete'}),
app: flags.app(),
}

static args = {
addonName: Args.string({required: true}),
}

public async run(): Promise<void> {
const {flags, argv} = await this.parse(Destroy)
const {app, wait, confirm} = flags
const force = flags.force || process.env.HEROKU_FORCE === '1'

const addons = await Promise.all(argv.map(name => resolveAddon(this.heroku, app, name)))
for (const addon of addons) {
// prevent deletion of add-on when context.app is set but the addon is attached to a different app
const addonApp = addon.app.name
if (app && addonApp !== app) {
throw new Error(`${color.yellow(addon.name)} is on ${color.magenta(addonApp)} not ${color.magenta(app)}`)
}
}

for (const addonApps of Object.entries(groupBy<Heroku.AddOn>(addons, 'app.name'))) {
const currentAddons = addonApps[1]
const appName = addonApps[0]
await confirmApp(appName, confirm)
for (const addon of currentAddons) {
try {
await destroyAddon(this.heroku, addon, force, wait)
if (wait) {
notify(`heroku addons:destroy ${addon.name}`, 'Add-on successfully deprovisioned')
}
} catch (error) {
if (wait) {
notify(`heroku addons:destroy ${addon.name}`, 'Add-on failed to deprovision', false)
}

throw error
}
}
}
}
}
31 changes: 31 additions & 0 deletions packages/cli/src/lib/addons/addons_wait.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,34 @@ export const waitForAddonProvisioning = async function (api: APIClient, addon: H
return addonBody
}

export const waitForAddonDeprovisioning = async function (api: APIClient, addon: Heroku.AddOn, interval: number) {
const app = addon.app?.name || ''
const addonName = addon.name || ''
let addonResponse = {...addon}

ux.action.start(`Destroying ${color.addon(addonName)}`)

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
while (addonResponse.state === 'deprovisioning') {
// eslint-disable-next-line no-promise-executor-return
await new Promise(resolve => setTimeout(resolve, interval * 1000))

await api.get<Heroku.AddOn>(`/apps/${app}/addons/${addonName}`, {
headers: {'Accept-Expansion': 'addon_service,plan'},
}).then(response => {
addonResponse = response?.body
}).catch(function (error) {
// Not ideal, but API deletes the record returning a 404 when deprovisioned.
if (error.statusCode === 404 || error.http?.statusCode === 404) {
addonResponse.state = 'deprovisioned'
} else {
throw error
}
})
}

ux.action.stop()
return addonResponse
}

Loading

0 comments on commit 25e68e7

Please sign in to comment.