Skip to content

Commit a4519ea

Browse files
authored
improvement(cli): sim credentials providers lists providers (#9522)
1 parent d3f4739 commit a4519ea

4 files changed

Lines changed: 65 additions & 2 deletions

File tree

‎packages/sim-cli/src/contract/commands.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,6 +1461,8 @@ export const CLI_CONTRACT: CliContract = {
14611461
// `serviceId`, while `credentials create` matches a service-account provider
14621462
// on `providerId`. Each is empty on the kind of row that does not use it.
14631463
listCredentialProviders: {
1464+
// The providers group holds nothing but this list, so naming the group lists them.
1465+
defaultSubcommand: true,
14641466
columns: [
14651467
{ header: 'type' },
14661468
{ header: 'service', path: 'serviceId' },

‎packages/sim-cli/src/contract/types.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,11 @@ export interface CommandSpec {
255255
command?: string
256256
/** Run this operation when its top-level group is invoked without a subcommand. */
257257
groupDefault?: boolean
258+
/**
259+
* Run this leaf when its group is named without a subcommand, as the group's default. The group
260+
* keeps no options of its own, so the leaf parses its options in either spelling.
261+
*/
262+
defaultSubcommand?: true
258263
/** Alternate leaf command names, such as `ls` for `list`. */
259264
aliases?: readonly string[]
260265
/**

‎packages/sim-cli/src/runtime/build.ts‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,18 +477,23 @@ function addLeafCommand(
477477
for (const segment of rest.slice(0, -1)) {
478478
parent = nestedGroup(parent, segment)
479479
}
480-
parent.addCommand(buildLeaf(operation, spec, rest[rest.length - 1]))
480+
parent.addCommand(buildLeaf(operation, spec, rest[rest.length - 1]), {
481+
isDefault: spec.defaultSubcommand === true,
482+
})
481483
return
482484
}
483485

484-
group.addCommand(buildLeaf(operation, spec, rest[0]))
486+
group.addCommand(buildLeaf(operation, spec, rest[0]), {
487+
isDefault: spec.defaultSubcommand === true,
488+
})
485489
}
486490

487491
function variantCommandSpec(spec: CommandSpec, variant: CommandVariantSpec): CommandSpec {
488492
return {
489493
...spec,
490494
command: variant.command,
491495
groupDefault: false,
496+
defaultSubcommand: undefined,
492497
aliases: [],
493498
positionals: variant.positionals,
494499
requestFields: variant.requestFields,
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { runEmbeddedCli } from '../embed'
3+
4+
const identity = { endpoint: 'https://sim.test', apiKey: 'fixture', workspaceId: 'workspace' }
5+
6+
/** Runs one embedded invocation, recording the path of each request it sends. */
7+
async function run(argv: string[]) {
8+
const paths: string[] = []
9+
const result = await runEmbeddedCli(argv, {
10+
...identity,
11+
transport: async (url) => {
12+
paths.push(new URL(String(url)).pathname)
13+
return Response.json({ data: [] })
14+
},
15+
})
16+
return { ...result, paths }
17+
}
18+
19+
describe('a nested group that defaults to its list', () => {
20+
it('lists credential providers when the providers group is named, as list does', async () => {
21+
const bare = await run(['credentials', 'providers'])
22+
const listed = await run(['credentials', 'providers', 'list'])
23+
expect(bare.exitCode).toBe(0)
24+
expect(bare.paths).toEqual(['/api/v2/credentials/providers'])
25+
expect(listed.paths).toEqual(bare.paths)
26+
})
27+
28+
it('keeps each spelling’s own options: a leaf option reaches the leaf, the bare group takes it too', async () => {
29+
const searchOf = async (argv: string[]) => {
30+
const queries: string[] = []
31+
await runEmbeddedCli(argv, {
32+
...identity,
33+
transport: async (url) => {
34+
queries.push(new URL(String(url)).searchParams.get('search') ?? '')
35+
return Response.json({ data: [] })
36+
},
37+
})
38+
return queries
39+
}
40+
expect(await searchOf(['credentials', 'providers', 'list', '--search', 'github'])).toEqual([
41+
'github',
42+
])
43+
expect(await searchOf(['credentials', 'providers', '--search', 'github'])).toEqual(['github'])
44+
})
45+
46+
it('leaves a group without a default printing its usage, sending nothing', async () => {
47+
const group = await run(['credentials'])
48+
expect(group.paths).toEqual([])
49+
expect(group.stderr + group.stdout).toContain('Usage: sim credentials')
50+
})
51+
})

0 commit comments

Comments
 (0)