Skip to content

Commit c73944c

Browse files
authored
Fix organization cmd, support json/md flags (#342)
1 parent d3cf6cf commit c73944c

File tree

7 files changed

+183
-82
lines changed

7 files changed

+183
-82
lines changed

src/cli.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { cmdNpm } from './commands/npm/cmd-npm'
2222
import { cmdNpx } from './commands/npx/cmd-npx'
2323
import { cmdOops } from './commands/oops/cmd-oops'
2424
import { cmdOptimize } from './commands/optimize/cmd-optimize'
25-
import { cmdOrganizations } from './commands/organizations/cmd-organizations'
25+
import { cmdOrganization } from './commands/organization/cmd-organization'
2626
import { cmdRawNpm } from './commands/raw-npm/cmd-raw-npm'
2727
import { cmdRawNpx } from './commands/raw-npx/cmd-raw-npx'
2828
import { cmdReport } from './commands/report/cmd-report'
@@ -58,7 +58,7 @@ void (async () => {
5858
npx: cmdNpx,
5959
oops: cmdOops,
6060
optimize: cmdOptimize,
61-
organization: cmdOrganizations,
61+
organization: cmdOrganization,
6262
'raw-npm': cmdRawNpm,
6363
'raw-npx': cmdRawNpx,
6464
report: cmdReport,
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { getOrganization } from './get-organization.ts'
2+
import { meowOrExit } from '../../utils/meow-with-subcommands'
3+
import { getFlagListOutput } from '../../utils/output-formatting'
4+
5+
import type { CliCommandConfig } from '../../utils/meow-with-subcommands'
6+
import { commonFlags, outputFlags } from '../../flags.ts'
7+
import colors from 'yoctocolors-cjs'
8+
9+
const config: CliCommandConfig = {
10+
commandName: 'organizations',
11+
description: 'List organizations associated with the API key used',
12+
hidden: false,
13+
flags: {
14+
...commonFlags,
15+
...outputFlags
16+
},
17+
help: (command, _config) => `
18+
Usage
19+
$ ${command}
20+
21+
Options
22+
${getFlagListOutput(config.flags, 6)}
23+
`
24+
}
25+
26+
export const cmdOrganization = {
27+
description: config.description,
28+
hidden: config.hidden,
29+
run
30+
}
31+
32+
async function run(
33+
argv: readonly string[],
34+
importMeta: ImportMeta,
35+
{ parentName }: { parentName: string }
36+
): Promise<void> {
37+
const cli = meowOrExit({
38+
argv,
39+
config,
40+
importMeta,
41+
parentName
42+
})
43+
44+
const json = Boolean(cli.flags['json'])
45+
const markdown = Boolean(cli.flags['markdown'])
46+
if (json && markdown) {
47+
// Use exit status of 2 to indicate incorrect usage, generally invalid
48+
// options or missing arguments.
49+
// https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
50+
process.exitCode = 2
51+
console.error(`
52+
${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
53+
- The json and markdown flags cannot be both set, pick one
54+
`)
55+
return
56+
}
57+
58+
if (cli.flags['dryRun']) {
59+
return console.log('[DryRun] Bailing now')
60+
}
61+
62+
await getOrganization(json ? 'json' : markdown ? 'markdown' : 'text')
63+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import colors from 'yoctocolors-cjs'
2+
3+
import { Spinner } from '@socketsecurity/registry/lib/spinner'
4+
5+
import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'
6+
import { AuthError } from '../../utils/errors'
7+
import { getDefaultToken, setupSdk } from '../../utils/sdk'
8+
9+
export async function getOrganization(
10+
format: 'text' | 'json' | 'markdown' = 'text'
11+
): Promise<void> {
12+
const apiToken = getDefaultToken()
13+
if (!apiToken) {
14+
throw new AuthError(
15+
'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'
16+
)
17+
}
18+
19+
await printOrganizationsFromToken(apiToken, format)
20+
}
21+
22+
async function printOrganizationsFromToken(
23+
apiToken: string,
24+
format: 'text' | 'json' | 'markdown' = 'text'
25+
) {
26+
const spinner = new Spinner({ text: 'Fetching organizations...' }).start()
27+
const socketSdk = await setupSdk(apiToken)
28+
const result = await handleApiCall(
29+
socketSdk.getOrganizations(),
30+
'looking up organizations'
31+
)
32+
33+
if (result.success === false) {
34+
handleUnsuccessfulApiResponse('getOrganizations', result, spinner)
35+
return
36+
}
37+
38+
spinner.stop('')
39+
40+
const organizations = Object.values(result.data.organizations)
41+
42+
if (format === 'json') {
43+
const obj = Array.from(organizations).map(o => ({
44+
name: o.name,
45+
id: o.id,
46+
plan: o.plan
47+
}))
48+
console.log(JSON.stringify(obj, null, 2))
49+
return
50+
}
51+
52+
if (format === 'markdown') {
53+
// | Syntax | Description |
54+
// | ----------- | ----------- |
55+
// | Header | Title |
56+
// | Paragraph | Text |
57+
58+
let mw1 = 4
59+
let mw2 = 2
60+
let mw3 = 4
61+
for (const o of organizations) {
62+
mw1 = Math.max(mw1, (o?.name || '').length)
63+
mw2 = Math.max(mw2, (o?.id || '').length)
64+
mw3 = Math.max(mw3, (o?.plan || '').length)
65+
}
66+
67+
console.log('# Organizations\n')
68+
console.log(
69+
`List of organizations associated with your API key, ending with: ${colors.italic(apiToken.slice(-10, -4))}\n`
70+
)
71+
console.log(
72+
`| Name${' '.repeat(mw1 - 4)} | ID${' '.repeat(mw2 - 2)} | Plan${' '.repeat(mw3 - 4)} |`
73+
)
74+
console.log(
75+
`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} | ${'-'.repeat(mw3)} |`
76+
)
77+
for (const o of organizations) {
78+
console.log(
79+
`| ${(o?.name || '').padEnd(mw1, ' ')} | ${(o?.id || '').padEnd(mw2, ' ')} | ${(o?.plan || '').padEnd(mw3, ' ')} |`
80+
)
81+
}
82+
console.log(
83+
`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} | ${'-'.repeat(mw3)} |`
84+
)
85+
return
86+
}
87+
88+
console.log(
89+
`List of organizations associated with your API key, ending with: ${colors.italic(apiToken.slice(-10, -4))}\n`
90+
)
91+
92+
// Just dump
93+
for (const o of organizations) {
94+
console.log(
95+
`- Name: ${colors.bold(o?.name)}, ID: ${colors.bold(o?.id)}, Plan: ${colors.bold(o?.plan)}`
96+
)
97+
}
98+
}

src/commands/organizations/cmd-organizations.ts

Lines changed: 0 additions & 40 deletions
This file was deleted.

src/commands/organizations/get-organizations.ts

Lines changed: 0 additions & 40 deletions
This file was deleted.

src/utils/meow-with-subcommands.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ export function meowOrExit({
172172
if (constants.ENV[SOCKET_CLI_SHOW_BANNER]) {
173173
console.log(getAsciiHeader(command))
174174
}
175+
175176
// This exits if .printHelp() is called either by meow itself or by us.
176177
const cli = meow({
177178
argv,

test/dry-run.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,25 @@ describe('dry-run on all commands', async () => {
507507
)
508508
})
509509

510+
cmdit(['organization', '--dry-run'], 'should support', async cmd => {
511+
const { code, status, stderr, stdout } = await invoke(...cmd)
512+
expect(`\n ${stdout}`).toMatchInlineSnapshot(`
513+
"
514+
_____ _ _ /---------------
515+
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
516+
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
517+
|_____|___|___|_,_|___|_|.dev | Command: \`socket organizations\`, cwd: <redacted>
518+
519+
[DryRun] Bailing now"
520+
`)
521+
expect(stderr).toMatchInlineSnapshot(`""`)
522+
523+
expect(code, 'dry-run should exit with code 0 if input is ok').toBe(0)
524+
expect(stdout, 'header should include command (without params)').toContain(
525+
cmd.slice(0, cmd.indexOf('--dry-run')).join(' ')
526+
)
527+
})
528+
510529
cmdit(['raw-npm', '--dry-run'], 'should support', async cmd => {
511530
const { code, status, stderr, stdout } = await invoke(...cmd)
512531
expect(`\n ${stdout}`).toMatchInlineSnapshot(`

0 commit comments

Comments
 (0)