-
Notifications
You must be signed in to change notification settings - Fork 224
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Convert pg credentials destroy to oclif
- Loading branch information
Showing
6 changed files
with
194 additions
and
213 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import color from '@heroku-cli/color' | ||
import {Command, flags} from '@heroku-cli/command' | ||
import {Args, ux} from '@oclif/core' | ||
import * as Heroku from '@heroku-cli/schema' | ||
import {essentialPlan} from '../../../lib/pg/util' | ||
import {getAddon} from '../../../lib/pg/fetcher' | ||
import pgHost from '../../../lib/pg/host' | ||
import confirmApp from '../../../lib/apps/confirm-app' | ||
|
||
export default class Destroy extends Command { | ||
static topic = 'pg'; | ||
static description = 'destroy credential within database'; | ||
static example = '$ heroku pg:credentials:destroy postgresql-transparent-56874 --name cred-name -a woodstock-production'; | ||
static flags = { | ||
name: flags.string({char: 'n', required: true, description: 'unique identifier for the credential'}), | ||
confirm: flags.string({char: 'c'}), | ||
app: flags.app({required: true}), | ||
}; | ||
|
||
static args = { | ||
database: Args.string(), | ||
}; | ||
|
||
public async run(): Promise<void> { | ||
const {flags, args} = await this.parse(Destroy) | ||
const {database} = args | ||
const {app, name, confirm} = flags | ||
if (name === 'default') { | ||
throw new Error('Default credential cannot be destroyed.') | ||
} | ||
|
||
const db = await getAddon(this.heroku, app, database) | ||
if (essentialPlan(db)) { | ||
throw new Error("You can't destroy the default credential on Essential-tier databases.") | ||
} | ||
|
||
const {body: attachments} = await this.heroku.get<Heroku.AddOnAttachment[]>(`/addons/${db.name}/addon-attachments`) | ||
const credAttachments = attachments.filter(a => a.namespace === `credential:${name}`) | ||
const credAttachmentApps = Array.from(new Set(credAttachments.map(a => a.app?.name))) | ||
if (credAttachmentApps.length > 0) | ||
throw new Error(`Credential ${name} must be detached from the app${credAttachmentApps.length > 1 ? 's' : ''} ${credAttachmentApps.map(appName => color.app(appName || '')) | ||
.join(', ')} before destroying.`) | ||
|
||
await confirmApp(app, confirm, 'WARNING: Destructive action') | ||
ux.action.start(`Destroying credential ${color.cyan.bold(name)}`) | ||
await this.heroku.delete(`/postgres/v0/databases/${db.name}/credentials/${encodeURIComponent(name)}`, {hostname: pgHost()}) | ||
ux.action.stop() | ||
ux.log(`The credential has been destroyed within ${db.name}.`) | ||
ux.log(`Database objects owned by ${name} will be assigned to the default credential.`) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
142 changes: 142 additions & 0 deletions
142
packages/cli/test/unit/commands/credentials/destroy.unit.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import {stderr, stdout} from 'stdout-stderr' | ||
import Cmd from '../../../../src/commands/pg/credentials/destroy' | ||
import runCommand from '../../../helpers/runCommand' | ||
import * as nock from 'nock' | ||
import expectOutput from '../../../helpers/utils/expectOutput' | ||
import {expect} from 'chai' | ||
import heredoc from 'tsheredoc' | ||
import stripAnsi = require('strip-ansi') | ||
|
||
describe('pg:credentials:destroy', () => { | ||
const addon = { | ||
name: 'postgres-1', plan: {name: 'heroku-postgresql:standard-0'}, | ||
} | ||
afterEach(() => { | ||
nock.cleanAll() | ||
}) | ||
|
||
it('destroys the credential', async () => { | ||
nock('https://api.heroku.com') | ||
.post('/actions/addon-attachments/resolve') | ||
.reply(200, [{addon}]) | ||
nock('https://api.data.heroku.com') | ||
.delete('/postgres/v0/databases/postgres-1/credentials/credname') | ||
.reply(200) | ||
const attachments = [ | ||
{ | ||
app: {name: 'myapp'}, addon: {id: 100, name: 'postgres-1'}, config_vars: ['HEROKU_POSTGRESQL_PINK_URL'], | ||
}, | ||
] | ||
nock('https://api.heroku.com') | ||
.get('/addons/postgres-1/addon-attachments') | ||
.reply(200, attachments) | ||
|
||
await runCommand(Cmd, [ | ||
'--app', | ||
'myapp', | ||
'--name', | ||
'credname', | ||
'--confirm', | ||
'myapp', | ||
]) | ||
expectOutput(stderr.output, heredoc(` | ||
Destroying credential credname... | ||
Destroying credential credname... done | ||
`)) | ||
expectOutput(stdout.output, heredoc(` | ||
The credential has been destroyed within postgres-1. | ||
Database objects owned by credname will be assigned to the default credential. | ||
`)) | ||
}) | ||
|
||
it('throws an error when the db is starter plan', async () => { | ||
const hobbyAddon = { | ||
name: 'postgres-1', plan: {name: 'heroku-postgresql:hobby-dev'}, | ||
} | ||
nock('https://api.heroku.com') | ||
.post('/actions/addon-attachments/resolve') | ||
.reply(200, [{addon: hobbyAddon}]) | ||
|
||
const err = "You can't destroy the default credential on Essential-tier databases." | ||
await runCommand(Cmd, [ | ||
'--app', | ||
'myapp', | ||
'--name', | ||
'jeff', | ||
]).catch((error: Error) => { | ||
expect(error.message).to.equal(err) | ||
}) | ||
}) | ||
|
||
it('throws an error when the db is numbered essential plan', async () => { | ||
const essentialAddon = { | ||
name: 'postgres-1', plan: {name: 'heroku-postgresql:essential-0'}, | ||
} | ||
nock('https://api.heroku.com') | ||
.post('/actions/addon-attachments/resolve') | ||
.reply(200, [{addon: essentialAddon}]) | ||
const err = "You can't destroy the default credential on Essential-tier databases." | ||
await runCommand(Cmd, [ | ||
'--app', | ||
'myapp', | ||
'--name', | ||
'gandalf', | ||
]).catch((error: Error) => { | ||
expect(error.message).to.equal(err) | ||
}) | ||
}) | ||
|
||
it('throws an error when the credential is still used for an attachment', async () => { | ||
const attachments = [ | ||
{ | ||
app: {name: 'myapp'}, addon: {id: 100, name: 'postgres-1'}, config_vars: ['HEROKU_POSTGRESQL_PINK_URL'], | ||
}, { | ||
app: {name: 'otherapp'}, addon: {id: 100, name: 'postgres-1'}, namespace: 'credential:gandalf', config_vars: ['HEROKU_POSTGRESQL_PURPLE_URL'], | ||
}, | ||
] | ||
nock('https://api.heroku.com') | ||
.post('/actions/addon-attachments/resolve') | ||
.reply(200, [{addon}]) | ||
nock('https://api.heroku.com') | ||
.get('/addons/postgres-1/addon-attachments') | ||
.reply(200, attachments) | ||
const err = 'Credential gandalf must be detached from the app ⬢ otherapp before destroying.' | ||
await runCommand(Cmd, [ | ||
'--app', | ||
'myapp', | ||
'--name', | ||
'gandalf', | ||
]).catch((error: Error) => { | ||
expect(stripAnsi(error.message)).to.equal(err) | ||
}) | ||
}) | ||
|
||
it('only mentions an app with multiple attachments once', async () => { | ||
const attachments = [ | ||
{ | ||
app: {name: 'myapp'}, addon: {id: 100, name: 'postgres-1'}, config_vars: ['HEROKU_POSTGRESQL_PINK_URL'], | ||
}, { | ||
app: {name: 'otherapp'}, addon: {id: 100, name: 'postgres-1'}, namespace: 'credential:gandalf', config_vars: ['HEROKU_POSTGRESQL_PURPLE_URL'], | ||
}, { | ||
app: {name: 'otherapp'}, addon: {id: 100, name: 'postgres-1'}, namespace: 'credential:gandalf', config_vars: ['HEROKU_POSTGRESQL_RED_URL'], | ||
}, { | ||
app: {name: 'yetanotherapp'}, addon: {id: 100, name: 'postgres-1'}, namespace: 'credential:gandalf', config_vars: ['HEROKU_POSTGRESQL_BLUE_URL'], | ||
}, | ||
] | ||
nock('https://api.heroku.com') | ||
.post('/actions/addon-attachments/resolve') | ||
.reply(200, [{addon}]) | ||
nock('https://api.heroku.com') | ||
.get('/addons/postgres-1/addon-attachments') | ||
.reply(200, attachments) | ||
const err = 'Credential gandalf must be detached from the apps ⬢ otherapp, ⬢ yetanotherapp before destroying.' | ||
await runCommand(Cmd, [ | ||
'--app', | ||
'myapp', | ||
'--name', | ||
'gandalf', | ||
]).catch((error: Error) => { | ||
expect(stripAnsi(error.message)).to.equal(err) | ||
}) | ||
}) | ||
}) |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.