Skip to content

Commit

Permalink
migrate(W-14179828): pg-v5: Upgrade pg:settings:auto-explain
Browse files Browse the repository at this point in the history
  • Loading branch information
justinwilaby committed Apr 4, 2024
1 parent 5f530ed commit 57803e9
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 36 deletions.
36 changes: 36 additions & 0 deletions packages/cli/src/commands/pg/settings/auto-explain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {Command, flags} from '@heroku-cli/command'
import {Args} from '@oclif/core'
import heredoc from 'tsheredoc'
import {generate, boolean} from '../../../lib/pg/setter'
// ref: https://www.postgresql.org/docs/current/auto-explain.html
function explain(setting: {value: string}) {
if (setting.value) {
return 'Execution plans of queries will be logged for future connections.'
}

return 'Execution plans of queries will not be logged for future connections.'
}

export default class AutoExplain extends Command {
static topic = 'pg';
static description = heredoc(`
Automatically log execution plans of queries without running EXPLAIN by hand.
The auto_explain module is loaded at session-time so existing connections will not be logged.
Restart your Heroku app and/or restart existing connections for logging to start taking place.'
`)

static flags = {
app: flags.app({required: true}),
remote: flags.remote(),
}

static args = {
database: Args.string(),
value: Args.string(),
}

public async run(): Promise<void> {
const {flags, args} = await this.parse(AutoExplain)
return generate('auto_explain', boolean, explain)({app: flags.app, args}, this.heroku)
}
}
62 changes: 62 additions & 0 deletions packages/cli/src/lib/pg/setter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {APIClient} from '@heroku-cli/command'
import {ux} from '@oclif/core'
import {addonResolver} from '../addons/resolve'
import host from './host'
import {essentialPlan} from './util'

type BooleanAsString = true | false | 'on' | 'ON' | 'true' | 'TRUE' | 'off' | 'OFF' | 'false' | 'FALSE'

export const boolean = (value: BooleanAsString) => {
switch (value) {
case 'true':
case 'TRUE':
case 'ON':
case 'on':
case true:
return true
case 'false':
case 'FALSE':
case 'OFF':
case 'off':
case null:
case false:
return false
default:
throw new TypeError('Invalid value. Valid options are: a boolean value')
}
}

export const numeric = (value: string | number) => {
const n = Number(value)
if (!Number.isFinite(n)) {
throw new TypeError('Invalid value. Valid options are: a numeric value')
}

return n
}

export const generate = (name: string, convert: CallableFunction, explain: CallableFunction) => {
return async function (context:{app: string, args:{value: string | undefined, database:string | undefined}}, heroku: APIClient) {
const {app, args} = context
const {value, database} = args

const db = await addonResolver(heroku, app, database || '')

if (essentialPlan(db)) throw new Error('You can’t perform this operation on Essential-tier databases.')

if (value) {
const {body: settings} = await heroku.patch<Record<string, {value: unknown}>>(`/postgres/v0/databases/${db.id}/config`, {
hostname: host(),
body: {[name]: convert(value)},
})
const setting = settings[name]
ux.log(`${name.replace(/_/g, '-')} has been set to ${setting.value} for ${db.name}.`)
ux.log(explain(setting))
} else {
const {body: settings} = await heroku.get<Record<string, {value: unknown}>>(`/postgres/v0/databases/${db.id}/config`, {hostname: host()})
const setting = settings[name]
ux.log(`${name.replace(/_/g, '-')} is set to ${setting.value} for ${db.name}.`)
ux.log(explain(setting))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {expect} from '@oclif/test'
import * as nock from 'nock'
import {stdout} from 'stdout-stderr'
import runCommand from '../../../../helpers/runCommand'
import Cmd from '../../../../../src/commands/pg/settings/auto-explain'

describe('pg:settings:auto-explain', () => {
let api: nock.Scope
let pg: nock.Scope

beforeEach(() => {
const addon = {
id: 1,
name: 'postgres-1',
app: {name: 'myapp'},
config_vars: ['READONLY_URL', 'DATABASE_URL', 'HEROKU_POSTGRESQL_RED_URL'],
plan: {name: 'heroku-postgresql:standard-0'},
}

api = nock('https://api.heroku.com')
api.post('/actions/addons/resolve', {
app: 'myapp',
addon: 'test-database',
}).reply(200, [addon])

pg = nock('https://api.data.heroku.com')
})

afterEach(() => {
api.done()
pg.done()
})

it('shows settings for auto_explain with value', async () => {
pg.get('/postgres/v0/databases/1/config').reply(200, {auto_explain: {value: 'test_value'}})
await runCommand(Cmd, ['--app', 'myapp', 'test-database'])
expect(stdout.output).to.equal('auto-explain is set to test_value for postgres-1.\nExecution plans of queries will be logged for future connections.\n')
})
})
24 changes: 0 additions & 24 deletions packages/pg-v5/commands/settings/auto_explain.js

This file was deleted.

12 changes: 0 additions & 12 deletions packages/pg-v5/test/unit/commands/settings/settings.unit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,6 @@ describe('pg:settings', () => {
.then(() => expect(cli.stdout).to.equal(`${settingsResultName} is set to ${settingResult.value} for postgres-1.\n${settingResult.values[settingResult.value]}\n`))
})

it('shows settings for auto_explain with value', () => {
setupSettingsMockData('auto_explain')
cmd = proxyquire('../../../../commands/settings/auto_explain', {
settings: proxyquire.noCallThru().load('../../../../lib/setter', {
'./fetcher': fetcher,
}),
})
pg.get('/postgres/v0/databases/1/config').reply(200, settingsResult)
return cmd.run({args: {database: 'test-database', value: ''}, flags: {}})
.then(() => expect(cli.stdout).to.equal('auto-explain is set to test_value for postgres-1.\nExecution plans of queries will be logged for future connections.\n'))
})

it('shows settings for auto_explain with no value', () => {
setupSettingsMockData('auto_explain', '')
cmd = proxyquire('../../../../commands/settings/auto_explain', {
Expand Down

0 comments on commit 57803e9

Please sign in to comment.