diff --git a/src/commands/organization/cmd-organization-list.ts b/src/commands/organization/cmd-organization-list.ts index ece71225..74e0ad75 100644 --- a/src/commands/organization/cmd-organization-list.ts +++ b/src/commands/organization/cmd-organization-list.ts @@ -3,7 +3,7 @@ import colors from 'yoctocolors-cjs' import { logger } from '@socketsecurity/registry/lib/logger' -import { getOrganization } from './get-organization' +import { handleOrganizationList } from './handle-organization-list' import constants from '../../constants' import { commonFlags, outputFlags } from '../../flags' import { meowOrExit } from '../../utils/meow-with-subcommands' @@ -68,5 +68,5 @@ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields return } - await getOrganization(json ? 'json' : markdown ? 'markdown' : 'text') + await handleOrganizationList(json ? 'json' : markdown ? 'markdown' : 'text') } diff --git a/src/commands/organization/cmd-organization-policy-security.ts b/src/commands/organization/cmd-organization-policy-security.ts index 51d717f4..0d799850 100644 --- a/src/commands/organization/cmd-organization-policy-security.ts +++ b/src/commands/organization/cmd-organization-policy-security.ts @@ -3,7 +3,7 @@ import colors from 'yoctocolors-cjs' import { logger } from '@socketsecurity/registry/lib/logger' -import { getSecurityPolicy } from './get-security-policy' +import { handleSecurityPolicy } from './handle-security-policy' import constants from '../../constants' import { commonFlags, outputFlags } from '../../flags' import { meowOrExit } from '../../utils/meow-with-subcommands' @@ -80,7 +80,7 @@ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields return } - await getSecurityPolicy( + await handleSecurityPolicy( orgSlug, json ? 'json' : markdown ? 'markdown' : 'text' ) diff --git a/src/commands/organization/cmd-organization-quota.ts b/src/commands/organization/cmd-organization-quota.ts index 777e90b7..22254de6 100644 --- a/src/commands/organization/cmd-organization-quota.ts +++ b/src/commands/organization/cmd-organization-quota.ts @@ -3,7 +3,7 @@ import colors from 'yoctocolors-cjs' import { logger } from '@socketsecurity/registry/lib/logger' -import { getQuota } from './get-quota' +import { handleQuota } from './handle-quota' import constants from '../../constants' import { commonFlags, outputFlags } from '../../flags' import { meowOrExit } from '../../utils/meow-with-subcommands' @@ -68,5 +68,5 @@ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields return } - await getQuota(json ? 'json' : markdown ? 'markdown' : 'text') + await handleQuota(json ? 'json' : markdown ? 'markdown' : 'text') } diff --git a/src/commands/organization/fetch-organization-list.ts b/src/commands/organization/fetch-organization-list.ts new file mode 100644 index 00000000..de50dd2d --- /dev/null +++ b/src/commands/organization/fetch-organization-list.ts @@ -0,0 +1,44 @@ +import constants from '../../constants' +import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' +import { AuthError } from '../../utils/errors' +import { getDefaultToken, setupSdk } from '../../utils/sdk' + +import type { SocketSdkReturnType } from '@socketsecurity/sdk' + +export async function fetchOrganization(): Promise< + SocketSdkReturnType<'getOrganizations'>['data'] | undefined +> { + const apiToken = getDefaultToken() + if (!apiToken) { + throw new AuthError( + 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' + ) + } + + return await fetchOrganizationWithToken(apiToken) +} + +async function fetchOrganizationWithToken( + apiToken: string +): Promise['data'] | undefined> { + const socketSdk = await setupSdk(apiToken) + + // Lazily access constants.spinner. + const { spinner } = constants + + spinner.start('Fetching organization list...') + + const result = await handleApiCall( + socketSdk.getOrganizations(), + 'looking up organizations' + ) + + spinner.successAndStop('Received organization list response.') + + if (!result.success) { + handleUnsuccessfulApiResponse('getOrganizations', result) + return + } + + return result.data +} diff --git a/src/commands/organization/get-quota.ts b/src/commands/organization/fetch-quota.ts similarity index 50% rename from src/commands/organization/get-quota.ts rename to src/commands/organization/fetch-quota.ts index c6ed1ebc..a7c4fa30 100644 --- a/src/commands/organization/get-quota.ts +++ b/src/commands/organization/fetch-quota.ts @@ -1,64 +1,43 @@ -import { logger } from '@socketsecurity/registry/lib/logger' - import constants from '../../constants' import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' import { AuthError } from '../../utils/errors' import { getDefaultToken, setupSdk } from '../../utils/sdk' -export async function getQuota( - format: 'text' | 'json' | 'markdown' = 'text' -): Promise { +import type { SocketSdkReturnType } from '@socketsecurity/sdk' + +export async function fetchQuota(): Promise< + SocketSdkReturnType<'getQuota'>['data'] | undefined +> { const apiToken = getDefaultToken() if (!apiToken) { throw new AuthError( 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' ) } - await getQuotaWithToken(apiToken, format) + return await fetchQuotaWithToken(apiToken) } -async function getQuotaWithToken( - apiToken: string, - format: 'text' | 'json' | 'markdown' = 'text' -) { +async function fetchQuotaWithToken( + apiToken: string +): Promise['data'] | undefined> { // Lazily access constants.spinner. const { spinner } = constants + const socketSdk = await setupSdk(apiToken) + spinner.start('Fetching organization quota...') - const socketSdk = await setupSdk(apiToken) const result = await handleApiCall( socketSdk.getQuota(), 'looking up organization quota' ) + spinner?.successAndStop('Recieved organization quota response.') + if (!result.success) { handleUnsuccessfulApiResponse('getQuota', result) return } - spinner.stop() - - switch (format) { - case 'json': { - logger.log( - JSON.stringify( - { - quota: result.data.quota - }, - null, - 2 - ) - ) - return - } - case 'markdown': { - logger.log('# Quota\n') - logger.log(`Quota left on the current API token: ${result.data.quota}\n`) - return - } - default: { - logger.log(`Quota left on the current API token: ${result.data.quota}\n`) - } - } + return result.data } diff --git a/src/commands/organization/fetch-security-policy.ts b/src/commands/organization/fetch-security-policy.ts new file mode 100644 index 00000000..9697d0a8 --- /dev/null +++ b/src/commands/organization/fetch-security-policy.ts @@ -0,0 +1,45 @@ +import constants from '../../constants' +import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' +import { AuthError } from '../../utils/errors' +import { getDefaultToken, setupSdk } from '../../utils/sdk' + +import type { SocketSdkReturnType } from '@socketsecurity/sdk' + +export async function fetchSecurityPolicy( + orgSlug: string +): Promise['data'] | undefined> { + const apiToken = getDefaultToken() + if (!apiToken) { + throw new AuthError( + 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' + ) + } + + return await fetchSecurityPolicyWithToken(apiToken, orgSlug) +} + +async function fetchSecurityPolicyWithToken( + apiToken: string, + orgSlug: string +): Promise['data'] | undefined> { + // Lazily access constants.spinner. + const { spinner } = constants + + const socketSdk = await setupSdk(apiToken) + + spinner.start('Fetching organization quota...') + + const result = await handleApiCall( + socketSdk.getOrgSecurityPolicy(orgSlug), + 'looking up organization quota' + ) + + spinner?.successAndStop('Received organization quota response.') + + if (!result.success) { + handleUnsuccessfulApiResponse('getOrgSecurityPolicy', result) + return + } + + return result.data +} diff --git a/src/commands/organization/get-security-policy.ts b/src/commands/organization/get-security-policy.ts deleted file mode 100644 index b58f53fe..00000000 --- a/src/commands/organization/get-security-policy.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { logger } from '@socketsecurity/registry/lib/logger' - -import constants from '../../constants' -import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { mdTableOfPairs } from '../../utils/markdown' -import { getDefaultToken, setupSdk } from '../../utils/sdk' - -export async function getSecurityPolicy( - orgSlug: string, - format: 'text' | 'json' | 'markdown' -): Promise { - const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - await getSecurityPolicyWithToken(apiToken, orgSlug, format) -} - -async function getSecurityPolicyWithToken( - apiToken: string, - orgSlug: string, - format: 'text' | 'json' | 'markdown' -) { - // Lazily access constants.spinner. - const { spinner } = constants - - spinner.start('Fetching organization quota...') - - const socketSdk = await setupSdk(apiToken) - const result = await handleApiCall( - socketSdk.getOrgSecurityPolicy(orgSlug), - 'looking up organization quota' - ) - - if (!result.success) { - handleUnsuccessfulApiResponse('getOrgSecurityPolicy', result) - return - } - - spinner.stop() - - switch (format) { - case 'json': { - logger.log(JSON.stringify(result.data, null, 2)) - return - } - default: { - logger.log('# Security policy\n') - logger.log( - `The default security policy setting is: "${result.data.securityPolicyDefault}"\n` - ) - logger.log( - 'These are the security policies per setting for your organization:\n' - ) - const data = result.data - const rules = data.securityPolicyRules - const entries: Array< - [string, { action: 'defer' | 'error' | 'warn' | 'monitor' | 'ignore' }] - // @ts-ignore -- not sure why TS is complaining tbh but it does not like it - > = Object.entries(rules) - const mapped: Array<[string, string]> = entries.map(([key, value]) => [ - key, - value.action - ]) - mapped.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)) - logger.log(mdTableOfPairs(mapped, ['name', 'action'])) - } - } -} diff --git a/src/commands/organization/handle-organization-list.ts b/src/commands/organization/handle-organization-list.ts new file mode 100644 index 00000000..3aad9324 --- /dev/null +++ b/src/commands/organization/handle-organization-list.ts @@ -0,0 +1,11 @@ +import { fetchOrganization } from './fetch-organization-list' +import { outputOrganizationList } from './output-organization-list' + +export async function handleOrganizationList( + outputKind: 'text' | 'json' | 'markdown' = 'text' +): Promise { + const data = await fetchOrganization() + if (!data) return + + await outputOrganizationList(data, outputKind) +} diff --git a/src/commands/organization/handle-quota.ts b/src/commands/organization/handle-quota.ts new file mode 100644 index 00000000..ff8abfb2 --- /dev/null +++ b/src/commands/organization/handle-quota.ts @@ -0,0 +1,11 @@ +import { fetchQuota } from './fetch-quota' +import { outputQuota } from './output-quota' + +export async function handleQuota( + outputKind: 'text' | 'json' | 'markdown' = 'text' +): Promise { + const data = await fetchQuota() + if (!data) return + + await outputQuota(data, outputKind) +} diff --git a/src/commands/organization/handle-security-policy.ts b/src/commands/organization/handle-security-policy.ts new file mode 100644 index 00000000..b85f8cdf --- /dev/null +++ b/src/commands/organization/handle-security-policy.ts @@ -0,0 +1,12 @@ +import { fetchSecurityPolicy } from './fetch-security-policy' +import { getSecurityPolicy } from './output-security-policy' + +export async function handleSecurityPolicy( + orgSlug: string, + outputKind: 'text' | 'json' | 'markdown' +): Promise { + const data = await fetchSecurityPolicy(orgSlug) + if (!data) return + + await getSecurityPolicy(data, outputKind) +} diff --git a/src/commands/organization/get-organization.ts b/src/commands/organization/output-organization-list.ts similarity index 60% rename from src/commands/organization/get-organization.ts rename to src/commands/organization/output-organization-list.ts index 403394ac..48061afc 100644 --- a/src/commands/organization/get-organization.ts +++ b/src/commands/organization/output-organization-list.ts @@ -2,53 +2,20 @@ import colors from 'yoctocolors-cjs' import { logger } from '@socketsecurity/registry/lib/logger' -import constants from '../../constants' -import { - getLastFiveOfApiToken, - handleApiCall, - handleUnsuccessfulApiResponse -} from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { getDefaultToken, setupSdk } from '../../utils/sdk' +import { getLastFiveOfApiToken } from '../../utils/api' +import { getDefaultToken } from '../../utils/sdk' -export async function getOrganization( - format: 'text' | 'json' | 'markdown' = 'text' +import type { SocketSdkReturnType } from '@socketsecurity/sdk' + +export async function outputOrganizationList( + data: SocketSdkReturnType<'getOrganizations'>['data'], + outputKind: 'text' | 'json' | 'markdown' = 'text' ): Promise { + const organizations = Object.values(data.organizations) const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - await printOrganizationsFromToken(apiToken, format) -} - -async function printOrganizationsFromToken( - apiToken: string, - format: 'text' | 'json' | 'markdown' = 'text' -) { - // Lazily access constants.spinner. - const { spinner } = constants - - spinner.start('Fetching organizations...') - - const socketSdk = await setupSdk(apiToken) - const result = await handleApiCall( - socketSdk.getOrganizations(), - 'looking up organizations' - ) - - if (!result.success) { - handleUnsuccessfulApiResponse('getOrganizations', result) - return - } - - spinner.stop() - - const organizations = Object.values(result.data.organizations) - const lastFiveOfApiToken = getLastFiveOfApiToken(apiToken) + const lastFiveOfApiToken = getLastFiveOfApiToken(apiToken ?? '?????') - switch (format) { + switch (outputKind) { case 'json': { logger.log( JSON.stringify( diff --git a/src/commands/organization/output-quota.ts b/src/commands/organization/output-quota.ts new file mode 100644 index 00000000..d0a37ce0 --- /dev/null +++ b/src/commands/organization/output-quota.ts @@ -0,0 +1,35 @@ +import { logger } from '@socketsecurity/registry/lib/logger' + +import type { SocketSdkReturnType } from '@socketsecurity/sdk' + +export async function outputQuota( + data: SocketSdkReturnType<'getQuota'>['data'], + outputKind: 'text' | 'json' | 'markdown' = 'text' +): Promise { + if (outputKind === 'json') { + let json + try { + json = JSON.stringify(data, null, 2) + } catch { + console.error( + 'Failed to convert the server response to json, try running the same command without --json' + ) + return + } + + logger.log(json) + logger.log('') + return + } + + if (outputKind === 'markdown') { + logger.log('# Quota') + logger.log('') + logger.log(`Quota left on the current API token: ${data.quota}`) + logger.log('') + return + } + + logger.log(`Quota left on the current API token: ${data.quota}`) + logger.log('') +} diff --git a/src/commands/organization/output-security-policy.ts b/src/commands/organization/output-security-policy.ts new file mode 100644 index 00000000..1119580d --- /dev/null +++ b/src/commands/organization/output-security-policy.ts @@ -0,0 +1,49 @@ +import { logger } from '@socketsecurity/registry/lib/logger' + +import { mdTableOfPairs } from '../../utils/markdown' + +import type { SocketSdkReturnType } from '@socketsecurity/sdk' + +export async function getSecurityPolicy( + data: SocketSdkReturnType<'getOrgSecurityPolicy'>['data'], + outputKind: 'text' | 'json' | 'markdown' +): Promise { + if (outputKind === 'json') { + let json + try { + json = JSON.stringify(data, null, 2) + } catch { + console.error( + 'Failed to convert the server response to json, try running the same command without --json' + ) + return + } + + logger.log(json) + logger.log('') + return + } + + logger.log('# Security policy') + logger.log('') + logger.log( + `The default security policy setting is: "${data.securityPolicyDefault}"` + ) + logger.log('') + logger.log( + 'These are the security policies per setting for your organization:' + ) + logger.log('') + const rules = data.securityPolicyRules + const entries: Array< + [string, { action: 'defer' | 'error' | 'warn' | 'monitor' | 'ignore' }] + // @ts-ignore -- not sure why TS is complaining tbh but it does not like it + > = Object.entries(rules) + const mapped: Array<[string, string]> = entries.map(([key, value]) => [ + key, + value.action + ]) + mapped.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)) + logger.log(mdTableOfPairs(mapped, ['name', 'action'])) + logger.log('') +}