-
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.
refactor(spaces): Move command spaces:topology to oclif (#2835)
* Convert spaces:topology to oclif * No need to set .sort as it is an in-place operation
- Loading branch information
Showing
6 changed files
with
293 additions
and
264 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,114 @@ | ||
import {Command, flags} from '@heroku-cli/command' | ||
import {Args, ux} from '@oclif/core' | ||
import * as Heroku from '@heroku-cli/schema' | ||
import heredoc from 'tsheredoc' | ||
import color from '@heroku-cli/color' | ||
|
||
export type SpaceTopology = { | ||
version: number, | ||
apps: Array<{ | ||
id?: string | ||
domains: string[] | ||
formations: Array<{ | ||
process_type: string | ||
dynos: Array<{ | ||
number: number | ||
private_ip: string | ||
hostname: string | ||
}> | ||
}> | ||
}> | ||
} | ||
|
||
export default class Topology extends Command { | ||
static topic = 'spaces'; | ||
static description = 'show space topology'; | ||
static flags = { | ||
space: flags.string({char: 's', description: 'space to get topology of'}), | ||
json: flags.boolean({description: 'output in json format'}), | ||
}; | ||
|
||
static args = { | ||
space: Args.string({hidden: true}), | ||
}; | ||
|
||
public async run(): Promise<void> { | ||
const {flags, args} = await this.parse(Topology) | ||
const spaceName = flags.space || args.space | ||
if (!spaceName) { | ||
ux.error(heredoc(` | ||
Error: Missing 1 required arg: | ||
space | ||
See more help with --help | ||
`)) | ||
} | ||
|
||
const {body: topology} = await this.heroku.get<SpaceTopology>(`/spaces/${spaceName}/topology`) | ||
let appInfo: Heroku.App[] = [] | ||
if (topology.apps) { | ||
appInfo = await Promise.all(topology.apps.map(async topologyApp => { | ||
const {body: app} = await this.heroku.get<Heroku.App>(`/apps/${topologyApp.id}`) | ||
return app | ||
})) | ||
} | ||
|
||
this.render(topology, appInfo, flags.json) | ||
} | ||
|
||
protected render(topology: SpaceTopology, appInfo: Heroku.App[], json: boolean) { | ||
if (json) { | ||
ux.styledJSON(topology) | ||
} else if (topology.apps) { | ||
topology.apps.forEach(app => { | ||
const formations: string[] = [] | ||
const dynos: string[] = [] | ||
if (app.formations) { | ||
app.formations.forEach(formation => { | ||
formations.push(formation.process_type) | ||
if (formation.dynos) { | ||
formation.dynos.forEach(dyno => { | ||
const dynoS = [`${formation.process_type}.${dyno.number}`, dyno.private_ip, dyno.hostname].filter(Boolean) | ||
dynos.push(dynoS.join(' - ')) | ||
}) | ||
} | ||
}) | ||
} | ||
|
||
const domains = app.domains.sort() | ||
formations.sort() | ||
dynos.sort((a, b) => { | ||
const apt = this.getProcessType(a) | ||
const bpt = this.getProcessType(b) | ||
if (apt > bpt) { | ||
return 1 | ||
} | ||
|
||
if (apt < bpt) { | ||
return -1 | ||
} | ||
|
||
return this.getProcessNum(a) - this.getProcessNum(b) | ||
}) | ||
const info = appInfo.find(info => info.id === app.id) | ||
let header = info?.name | ||
if (formations.length > 0) { | ||
header += ` (${color.cyan(formations.join(', '))})` | ||
} | ||
|
||
ux.styledHeader(header || '') | ||
ux.styledObject({ | ||
Domains: domains, Dynos: dynos, | ||
}, ['Domains', 'Dynos']) | ||
ux.log() | ||
}) | ||
} | ||
} | ||
|
||
protected getProcessType(s: string) { | ||
return s.split('-', 2)[0].split('.', 2)[0] | ||
} | ||
|
||
protected getProcessNum(s: string) { | ||
return Number.parseInt(s.split('-', 2)[0].split('.', 2)[1], 10) | ||
} | ||
} |
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
90 changes: 90 additions & 0 deletions
90
packages/cli/test/unit/commands/spaces/topology.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,90 @@ | ||
import {stdout} from 'stdout-stderr' | ||
import Cmd from '../../../../src/commands/spaces/topology' | ||
import runCommand from '../../../helpers/runCommand' | ||
import * as nock from 'nock' | ||
import heredoc from 'tsheredoc' | ||
import expectOutput from '../../../helpers/utils/expectOutput' | ||
import * as fixtures from '../../../fixtures/spaces/fixtures' | ||
import {expect} from 'chai' | ||
|
||
describe('spaces:topology', function () { | ||
const topo1 = fixtures.topologies['topology-one'] | ||
const topo2 = fixtures.topologies['topology-two'] | ||
const topo3 = fixtures.topologies['topology-three'] | ||
const app = fixtures.apps.www | ||
|
||
it('shows space topology', async function () { | ||
nock('https://api.heroku.com') | ||
.get('/spaces/my-space/topology') | ||
.reply(200, topo1) | ||
.get(`/apps/${app.id}`) | ||
.reply(200, app) | ||
|
||
await runCommand(Cmd, [ | ||
'--space', | ||
'my-space', | ||
]) | ||
expectOutput(stdout.output, heredoc(` | ||
=== ${app.name} (web) | ||
Domains: example.com | ||
example.net | ||
Dynos: web.1 - 10.0.134.42 - 1.example-app-90210.app.localspace | ||
web.2 - 10.0.134.42 - 1.example-app-90210.app.localspace | ||
`)) | ||
}) | ||
|
||
it('shows space topology with first dyno having higher process number', async function () { | ||
nock('https://api.heroku.com') | ||
.get('/spaces/my-space/topology') | ||
.reply(200, topo2) | ||
.get(`/apps/${app.id}`) | ||
.reply(200, app) | ||
|
||
await runCommand(Cmd, [ | ||
'--space', | ||
'my-space', | ||
]) | ||
expectOutput(stdout.output, heredoc(` | ||
=== ${app.name} (web) | ||
Domains: example.com | ||
example.net | ||
Dynos: web.1 - 10.0.134.42 - 1.example-app-90210.app.localspace | ||
web.2 - 10.0.134.42 - 1.example-app-90210.app.localspace | ||
`)) | ||
}) | ||
|
||
it('shows space topology with dynos having same process number', async function () { | ||
nock('https://api.heroku.com') | ||
.get('/spaces/my-space/topology') | ||
.reply(200, topo3) | ||
.get(`/apps/${app.id}`) | ||
.reply(200, app) | ||
|
||
await runCommand(Cmd, [ | ||
'--space', | ||
'my-space', | ||
]) | ||
expectOutput(stdout.output, heredoc(` | ||
=== ${app.name} (web) | ||
Domains: example.com | ||
example.net | ||
Dynos: web.1 - 10.0.134.42 - 1.example-app-90210.app.localspace | ||
web.1 - 10.0.134.42 - 1.example-app-90210.app.localspace | ||
`)) | ||
}) | ||
|
||
it('shows space topology --json', async function () { | ||
nock('https://api.heroku.com') | ||
.get('/spaces/my-space/topology') | ||
.reply(200, topo1) | ||
.get(`/apps/${app.id}`) | ||
.reply(200, app) | ||
|
||
await runCommand(Cmd, [ | ||
'--space', | ||
'my-space', | ||
'--json', | ||
]) | ||
expect(JSON.parse(stdout.output)).to.eql(topo1) | ||
}) | ||
}) |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.