Skip to content

Commit 004bd7d

Browse files
committed
Adds command 'data:pg:levels'
1 parent 19fe927 commit 004bd7d

File tree

4 files changed

+118
-2
lines changed

4 files changed

+118
-2
lines changed

src/commands/data/pg/levels.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {hux} from '@heroku/heroku-cli-util'
2+
import {Command} from '@heroku-cli/command'
3+
import {ux} from '@oclif/core'
4+
import {printTable, TableOptions} from '@oclif/table'
5+
6+
import BaseCommand from '../../../lib/data/baseCommand.js'
7+
import {PostgresLevelInfo, PostgresLevelsResponse} from '../../../lib/data/types.js'
8+
9+
export default class DataPgLevels extends BaseCommand {
10+
static baseFlags = Command.baseFlagsWithoutPrompt()
11+
static description = 'show available levels for Heroku Postgres Advanced databases'
12+
static promptFlagActive = false
13+
14+
async run(): Promise<void> {
15+
const {body: {items: levels}} = await this.dataApi.get<PostgresLevelsResponse>(`/data/postgres/v1/levels/advanced`)
16+
17+
ux.stdout()
18+
hux.styledHeader('Available levels for Heroku Postgres Advanced databases')
19+
20+
const defaultStyle = {
21+
borderColor: 'whiteBright',
22+
borderStyle: 'headers-only-with-underline',
23+
headerOptions: {
24+
bold: true,
25+
color: 'white',
26+
},
27+
} as Partial<TableOptions<PostgresLevelInfo>>
28+
29+
printTable<PostgresLevelInfo>({
30+
columns: [
31+
{key: 'name', name: 'Name'},
32+
{key: 'vcpu', name: 'vCPU', horizontalAlignment: 'right'},
33+
{key: 'memory_in_gb', name: 'Memory (GB)', horizontalAlignment: 'right'},
34+
{key: 'connection_limit', name: 'Max Connections', horizontalAlignment: 'right'},
35+
],
36+
data: levels,
37+
...defaultStyle,
38+
})
39+
}
40+
}

src/lib/data/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ type StorageLimitInGb = {
8585
export type PlanLimit = ConnectionLimit | StorageLimitInGb | TableLimit
8686

8787
export type PostgresLevelInfo = {
88+
connection_limit: number
8889
memory_in_gb: number
8990
name: string
9091
vcpu: number

test/fixtures/data/pg/fixtures.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,18 @@ export const essentialAddon: DeepRequired<Heroku.AddOn> = {
108108

109109
export const levelsResponse: PostgresLevelsResponse = {
110110
items: [
111-
{memory_in_gb: 4, name: '4G-Performance', vcpu: 2},
112-
{memory_in_gb: 8, name: '8G-Performance', vcpu: 4},
111+
{
112+
connection_limit: 400,
113+
memory_in_gb: 4,
114+
name: '4G-Performance',
115+
vcpu: 2,
116+
},
117+
{
118+
connection_limit: 800,
119+
memory_in_gb: 8,
120+
name: '8G-Performance',
121+
vcpu: 4,
122+
},
113123
],
114124
}
115125

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import {expect} from 'chai'
2+
import nock from 'nock'
3+
import {stderr, stdout} from 'stdout-stderr'
4+
5+
import DataPgLevels from '../../../../../src/commands/data/pg/levels.js'
6+
import {levelsResponse} from '../../../../fixtures/data/pg/fixtures.js'
7+
import runCommand from '../../../../helpers/runCommand.js'
8+
9+
describe.only('data:pg:levels', () => {
10+
let dataApi: nock.Scope
11+
12+
beforeEach(() => {
13+
dataApi = nock('https://api.data.heroku.com')
14+
})
15+
16+
afterEach(() => {
17+
dataApi.done()
18+
})
19+
20+
it('displays available levels for advanced databases', async () => {
21+
dataApi
22+
.get('/data/postgres/v1/levels/advanced')
23+
.reply(200, levelsResponse)
24+
25+
await runCommand(DataPgLevels, [])
26+
27+
expect(stderr.output).to.equal('')
28+
expect(stdout.output).to.include('Name')
29+
expect(stdout.output).to.include('vCPU')
30+
expect(stdout.output).to.include('Memory (GB)')
31+
expect(stdout.output).to.include('Max Connections')
32+
expect(stdout.output).to.include('4G-Performance')
33+
expect(stdout.output).to.include('8G-Performance')
34+
})
35+
36+
it('displays an empty table when no levels are available', async () => {
37+
dataApi
38+
.get('/data/postgres/v1/levels/advanced')
39+
.reply(200, {items: []})
40+
41+
await runCommand(DataPgLevels, [])
42+
43+
expect(stderr.output).to.equal('')
44+
expect(stdout.output).to.include('Name')
45+
expect(stdout.output).to.include('vCPU')
46+
expect(stdout.output).to.include('Memory (GB)')
47+
expect(stdout.output).to.include('Max Connections')
48+
expect(stdout.output).not.to.include('4G-Performance')
49+
expect(stdout.output).not.to.include('8G-Performance')
50+
})
51+
52+
it('handles API errors gracefully', async () => {
53+
dataApi
54+
.get('/data/postgres/v1/levels/advanced')
55+
.reply(500, {id: 'server_error', message: 'Internal Server Error'})
56+
57+
try {
58+
await runCommand(DataPgLevels, [])
59+
expect.fail('Expected command to throw an error')
60+
} catch (error: unknown) {
61+
const err = error as Error
62+
expect(err.message).to.include('Internal Server Error')
63+
}
64+
})
65+
})

0 commit comments

Comments
 (0)