Skip to content

Commit 7a280d2

Browse files
committed
feat: use shared no-wrap flag and table helper
Made-with: Cursor
1 parent 609cf8b commit 7a280d2

File tree

13 files changed

+191
-31
lines changed

13 files changed

+191
-31
lines changed

package-lock.json

Lines changed: 40 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"bin": "./bin/run",
77
"bugs": "https://github.com/heroku/cli/issues",
88
"dependencies": {
9-
"@heroku-cli/command": "^12.2.1",
9+
"@heroku-cli/command": "^12.3.1",
1010
"@heroku-cli/notifications": "^1.2.6",
1111
"@heroku-cli/schema": "^1.0.25",
1212
"@heroku/buildpack-registry": "^1.0.1",

src/commands/addons/index.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {ux} from '@oclif/core/ux'
66
import _ from 'lodash'
77

88
import {formatPrice, formatState, grandfatheredPrice} from '../../lib/addons/util.js'
9+
import {huxTableNoWrapOptions} from '../../lib/utils/tableUtils.js'
910

1011
const topic = 'addons'
1112

@@ -76,7 +77,7 @@ async function addonGetter(api: APIClient, app?: string) {
7677
return addons
7778
}
7879

79-
function displayAll(addons: Heroku.AddOn[]) {
80+
function displayAll(addons: Heroku.AddOn[], noWrap = false) {
8081
addons = _.sortBy(addons, 'app.name', 'plan.name', 'addon.name')
8182
if (addons.length === 0) {
8283
ux.stdout('No add-ons.')
@@ -138,9 +139,7 @@ function displayAll(addons: Heroku.AddOn[]) {
138139
},
139140
},
140141
},
141-
{
142-
overflow: 'wrap',
143-
},
142+
huxTableNoWrapOptions(noWrap),
144143
)
145144
/* eslint-enable perfectionist/sort-objects */
146145
}
@@ -162,7 +161,7 @@ export function renderAttachment(attachment: Heroku.AddOnAttachment, app: string
162161
return ` ${color.dim(line)} ${attName}`
163162
}
164163

165-
function displayForApp(app: string, addons: Heroku.AddOn[]) {
164+
function displayForApp(app: string, addons: Heroku.AddOn[], noWrap = false) {
166165
if (addons.length === 0) {
167166
ux.stdout(`No add-ons for app ${app}.`)
168167
return
@@ -221,9 +220,7 @@ function displayForApp(app: string, addons: Heroku.AddOn[]) {
221220
get: ({state}) => formatState(state || ''),
222221
},
223222
},
224-
{
225-
overflow: 'wrap',
226-
},
223+
huxTableNoWrapOptions(noWrap),
227224
)
228225
ux.stdout(`The table above shows add-ons and the attachments to the current app (${color.app(app)}) or other apps.\n `)
229226
}
@@ -249,6 +246,7 @@ export default class Addons extends Command {
249246
all: flags.boolean({char: 'A', description: 'show add-ons and attachments for all accessible apps'}),
250247
app: flags.app(),
251248
json: flags.boolean({description: 'return add-ons in json format'}),
249+
'no-wrap': flags.noWrap(),
252250
remote: flags.remote(),
253251
}
254252

@@ -265,13 +263,13 @@ export default class Addons extends Command {
265263
if (json)
266264
displayJSON(addons)
267265
else
268-
displayForApp(app, addons)
266+
displayForApp(app, addons, flags['no-wrap'])
269267
} else {
270268
const addons = await addonGetter(this.heroku)
271269
if (json)
272270
displayJSON(addons)
273271
else
274-
displayAll(addons)
272+
displayAll(addons, flags['no-wrap'])
275273
}
276274
}
277275
}

src/commands/data/pg/credentials/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {CredentialInfo, CredentialsInfo} from '../../../../lib/data/types.j
88
import BaseCommand from '../../../../lib/data/baseCommand.js'
99
import {sortByOwnerAndName} from '../../../../lib/data/credentialUtils.js'
1010
import {presentCredentialAttachments} from '../../../../lib/pg/util.js'
11+
import {huxTableNoWrapOptions} from '../../../../lib/utils/tableUtils.js'
1112

1213
export default class DataPgCredentialsIndex extends BaseCommand {
1314
static args = {
@@ -25,6 +26,7 @@ export default class DataPgCredentialsIndex extends BaseCommand {
2526

2627
static flags = {
2728
app: Flags.app({required: true}),
29+
'no-wrap': Flags.noWrap(),
2830
remote: Flags.remote(),
2931
}
3032

@@ -74,8 +76,6 @@ export default class DataPgCredentialsIndex extends BaseCommand {
7476
State: {
7577
get: cred => cred.state,
7678
},
77-
}, {
78-
overflow: 'wrap',
79-
})
79+
}, huxTableNoWrapOptions(flags['no-wrap']))
8080
}
8181
}

src/commands/domains/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Uri from 'urijs'
99

1010
import parseKeyValue from '../../lib/utils/keyValueParser.js'
1111
import {paginateRequest} from '../../lib/utils/paginator.js'
12+
import {huxTableNoWrapOptions} from '../../lib/utils/tableUtils.js'
1213

1314
function isApexDomain(hostname: string) {
1415
if (hostname.includes('*')) return false
@@ -38,7 +39,7 @@ www.example.com CNAME www.example.herokudns.com`]
3839
extended: flags.boolean({char: 'x', description: 'show extra columns'}),
3940
filter: flags.string({description: 'filter property by partial string matching, ex: name=foo'}),
4041
json: flags.boolean({char: 'j', description: 'output in json format'}),
41-
'no-wrap': flags.boolean({description: 'disable wrapped table cells for easier copy/paste'}),
42+
'no-wrap': flags.noWrap(),
4243
remote: flags.remote(),
4344
sort: flags.string({description: 'sort by property'}),
4445
}
@@ -223,8 +224,7 @@ www.example.com CNAME www.example.herokudns.com`]
223224
this.outputCSV(customDomains, tableConfig, sortProperty)
224225
} else {
225226
hux.table(customDomains, tableConfig, {
226-
maxWidth: flags['no-wrap'] ? 'none' : undefined,
227-
overflow: flags['no-wrap'] ? 'truncate' : 'wrap',
227+
...huxTableNoWrapOptions(flags['no-wrap']),
228228
sort: flags.sort ? {[sortProperty]: 'asc'} : undefined,
229229
})
230230
}

src/commands/pg/credentials.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {Args} from '@oclif/core'
66
import type {NonAdvancedCredentialInfo} from '../../lib/data/types.js'
77

88
import {presentCredentialAttachments} from '../../lib/pg/util.js'
9+
import {huxTableNoWrapOptions} from '../../lib/utils/tableUtils.js'
910
import {nls} from '../../nls.js'
1011

1112
export default class Credentials extends Command {
@@ -16,6 +17,7 @@ export default class Credentials extends Command {
1617
static description = 'show information on credentials in the database'
1718
static flags = {
1819
app: flags.app({required: true}),
20+
'no-wrap': flags.noWrap(),
1921
remote: flags.remote(),
2022
}
2123

@@ -62,9 +64,7 @@ export default class Credentials extends Command {
6264
State: {
6365
get: cred => cred.state,
6466
},
65-
}, {
66-
overflow: 'wrap',
67-
})
67+
}, huxTableNoWrapOptions(flags['no-wrap']))
6868
}
6969

7070
protected sortByDefaultAndName(credentials: NonAdvancedCredentialInfo[]) {

src/commands/ps/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {ux} from '@oclif/core/ux'
55
import tsheredoc from 'tsheredoc'
66

77
import {ago} from '../../lib/time.js'
8+
import {huxTableNoWrapOptions} from '../../lib/utils/tableUtils.js'
89
import {AccountQuota} from '../../lib/types/account_quota.js'
910
import {AppProcessTier} from '../../lib/types/app_process_tier.js'
1011
import {DynoExtended} from '../../lib/types/dyno_extended.js'
@@ -57,7 +58,7 @@ function truncate(s: string) {
5758
return s.length > 35 ? `${s.slice(0, 34)}…` : s
5859
}
5960

60-
function printExtended(dynos: DynoExtended[]) {
61+
function printExtended(dynos: DynoExtended[], noWrap = false) {
6162
const sortedDynos = dynos.sort(byProcessTypeAndNumber)
6263

6364
/* eslint-disable perfectionist/sort-objects */
@@ -79,9 +80,7 @@ function printExtended(dynos: DynoExtended[]) {
7980
Route: {get: (dyno: DynoExtended) => dyno.extended?.route ?? ''},
8081
Size: {get: (dyno: DynoExtended) => dyno.size},
8182
},
82-
{
83-
overflow: 'wrap',
84-
},
83+
huxTableNoWrapOptions(noWrap),
8584
)
8685
/* eslint-enable perfectionist/sort-objects */
8786
}
@@ -184,6 +183,7 @@ export default class Index extends Command {
184183
app: flags.app({required: true}),
185184
extended: flags.boolean({char: 'x', hidden: true}), // only works with sudo privileges
186185
json: flags.boolean({description: 'display as json'}),
186+
'no-wrap': flags.noWrap(),
187187
remote: flags.remote(),
188188
}
189189

@@ -234,7 +234,7 @@ export default class Index extends Command {
234234
if (json)
235235
hux.styledJSON(selectedDynos)
236236
else if (extended)
237-
printExtended(selectedDynos)
237+
printExtended(selectedDynos, flags['no-wrap'])
238238
else {
239239
await printAccountQuota(this.heroku, appInfo, accountInfo)
240240
if (selectedDynos.length === 0)

src/lib/utils/tableUtils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@ import {ux} from '@oclif/core/ux'
22

33
import parseKeyValue from './keyValueParser.js'
44

5+
export function huxTableNoWrapOptions(noWrap: boolean): {
6+
maxWidth: 'none' | undefined
7+
overflow: 'truncate' | 'wrap'
8+
} {
9+
return {
10+
maxWidth: noWrap ? 'none' : undefined,
11+
overflow: noWrap ? 'truncate' : 'wrap',
12+
}
13+
}
14+
515
export const constructSortFilterTableOptions = (flags: Record<string, string>, tableColumns: Record<string, any>) => {
616
const {filter, sort} = flags
717
const columnNames = new Set(Object.keys(tableColumns).map(key => key.toLowerCase()))

test/unit/commands/addons/index.unit.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
/* eslint-disable max-nested-callbacks */
22
import * as Heroku from '@heroku-cli/schema'
33
import {expect} from 'chai'
4+
import {hux} from '@heroku/heroku-cli-util'
45
import nock from 'nock'
6+
import sinon from 'sinon'
57

68
import Cmd from '../../../../src/commands/addons/index.js'
79
import * as fixtures from '../../../fixtures/addons/fixtures.js'
@@ -24,6 +26,7 @@ describe('addons', function () {
2426
afterEach(function () {
2527
api.done()
2628
nock.cleanAll()
29+
sinon.restore()
2730
})
2831

2932
describe('--all', function () {
@@ -59,6 +62,14 @@ describe('addons', function () {
5962
expect(stdout.indexOf('acme-inc-api')).to.be.lt(stdout.indexOf('acme-inc-www'))
6063
expect(stdout.indexOf('www-db')).to.be.lt(stdout.indexOf('www-redis'))
6164
})
65+
it('passes no-wrap option through to table rendering', async function () {
66+
const tableStub = sinon.stub(hux, 'table')
67+
68+
await runCommand(Cmd, ['--all', '--no-wrap'])
69+
70+
const callArgs = tableStub.firstCall.args
71+
expect(callArgs[2]).to.include({maxWidth: 'none', overflow: 'truncate'})
72+
})
6273
context('--json', function () {
6374
it('prints the output in json format', async function () {
6475
const {stdout} = await runCommand(Cmd, [

test/unit/commands/data/pg/credentials/index.unit.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import ansis from 'ansis'
22
import {expect} from 'chai'
3+
import {hux} from '@heroku/heroku-cli-util'
34
import nock from 'nock'
5+
import sinon from 'sinon'
46
import {stdout} from 'stdout-stderr'
57
import tsheredoc from 'tsheredoc'
68

@@ -18,6 +20,10 @@ import removeAllWhitespace from '../../../../../helpers/utils/remove-whitespaces
1820
const heredoc = tsheredoc.default
1921

2022
describe('data:pg:credentials:index', function () {
23+
afterEach(function () {
24+
sinon.restore()
25+
})
26+
2127
it('shows error for non-advanced databases', async function () {
2228
const herokuApi = nock('https://api.heroku.com')
2329
.post('/actions/addons/resolve')
@@ -97,4 +103,25 @@ describe('data:pg:credentials:index', function () {
97103
herokuApi.done()
98104
dataApi.done()
99105
})
106+
107+
it('passes no-wrap option through to table rendering', async function () {
108+
const herokuApi = nock('https://api.heroku.com')
109+
.post('/actions/addons/resolve')
110+
.reply(200, [addon])
111+
.get(`/addons/${addon.id}/addon-attachments`)
112+
.reply(200, advancedCredentialsAttachmentsResponse)
113+
114+
const dataApi = nock('https://api.data.heroku.com')
115+
.get(`/data/postgres/v1/${addon.id}/credentials`)
116+
.reply(200, advancedCredentialsResponse)
117+
118+
const tableStub = sinon.stub(hux, 'table')
119+
await runCommand(DataPgCredentialsIndex, ['DATABASE', '--app=myapp', '--no-wrap'])
120+
121+
const callArgs = tableStub.firstCall.args
122+
expect(callArgs[2]).to.include({maxWidth: 'none', overflow: 'truncate'})
123+
124+
herokuApi.done()
125+
dataApi.done()
126+
})
100127
})

0 commit comments

Comments
 (0)