diff --git a/lib/commands/dependencies/index.js b/lib/commands/dependencies/index.js index 9c731136..c1401493 100644 --- a/lib/commands/dependencies/index.js +++ b/lib/commands/dependencies/index.js @@ -1,150 +1,24 @@ -/* eslint-disable no-console */ +import { search } from './search.js' +import { upload } from './upload.js' +import { meowWithSubcommands } from '../../utils/meow-with-subcommands.js' -import chalk from 'chalk' -// @ts-ignore -import chalkTable from 'chalk-table' -import meow from 'meow' -import ora from 'ora' - -import { outputFlags } from '../../flags/index.js' -import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js' -import { prepareFlags } from '../../utils/flags.js' -import { printFlagList } from '../../utils/formatting.js' -import { getDefaultKey, setupSdk } from '../../utils/sdk.js' +const description = 'Dependencies related commands' /** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */ export const dependencies = { - description: 'Search for any dependency that is being used in your organization', - async run (argv, importMeta, { parentName }) { - const name = parentName + ' dependencies' - - const input = setupCommand(name, dependencies.description, argv, importMeta) - if (input) { - const spinnerText = 'Searching dependencies...' - const spinner = ora(spinnerText).start() - await searchDeps(input, spinner) - } - } -} - -const dependenciesFlags = prepareFlags({ - limit: { - type: 'number', - shortFlag: 'l', - default: 50, - description: 'Maximum number of dependencies returned', - }, - offset: { - type: 'number', - shortFlag: 'o', - default: 0, - description: 'Page number', - } - }) - -// Internal functions - -/** - * @typedef Command - * @property {boolean} outputJson - * @property {boolean} outputMarkdown - * @property {number} limit - * @property {number} offset - */ - -/** - * @param {string} name - * @param {string} description - * @param {readonly string[]} argv - * @param {ImportMeta} importMeta - * @returns {void|Command} - */ -function setupCommand (name, description, argv, importMeta) { - const flags = { - ...outputFlags, - ...dependenciesFlags - } - - const cli = meow(` - Usage - $ ${name} - - Options - ${printFlagList(flags, 6)} - - Examples - $ ${name} - `, { - argv, - description, - importMeta, - flags - }) - - const { - json: outputJson, - markdown: outputMarkdown, - limit, - offset - } = cli.flags - - return { - outputJson, - outputMarkdown, - limit, - offset - } -} - -/** - * @typedef DependenciesData - * @property {import('@socketsecurity/sdk').SocketSdkReturnType<'searchDependencies'>["data"]} data - */ - -/** - * @param {Command} input - * @param {import('ora').Ora} spinner - * @returns {Promise} - */ -async function searchDeps ({ limit, offset, outputJson }, spinner) { - const socketSdk = await setupSdk(getDefaultKey()) - const result = await handleApiCall(socketSdk.searchDependencies({ limit, offset }), 'Searching dependencies') - - if (!result.success) { - return handleUnsuccessfulApiResponse('searchDependencies', result, spinner) - } - - spinner.stop() - - console.log('Organization dependencies: \n') - - if (outputJson) { - return console.log(result.data) - } - - const options = { - columns: [ - { field: 'namespace', name: chalk.cyan('Namespace') }, - { field: 'name', name: chalk.cyan('Name') }, - { field: 'version', name: chalk.cyan('Version') }, - { field: 'repository', name: chalk.cyan('Repository') }, - { field: 'branch', name: chalk.cyan('Branch') }, - { field: 'type', name: chalk.cyan('Type') }, - { field: 'direct', name: chalk.cyan('Direct') } - ] - } - - const formattedResults = result.data.rows.map((/** @type {{[key:string]: any}} */ d) => { - return { - ...d - } - }) - - const table = chalkTable(options, formattedResults) - - console.log(table, '\n') - - return { - data: result.data + description, + run: async (argv, importMeta, { parentName }) => { + await meowWithSubcommands( + { + search, + upload + }, + { + argv, + description, + importMeta, + name: parentName + ' dependencies', + } + ) } } diff --git a/lib/commands/dependencies/search.js b/lib/commands/dependencies/search.js new file mode 100644 index 00000000..39a14a98 --- /dev/null +++ b/lib/commands/dependencies/search.js @@ -0,0 +1,150 @@ +/* eslint-disable no-console */ + +import chalk from 'chalk' +// @ts-ignore +import chalkTable from 'chalk-table' +import meow from 'meow' +import ora from 'ora' + +import { outputFlags } from '../../flags/index.js' +import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js' +import { prepareFlags } from '../../utils/flags.js' +import { printFlagList } from '../../utils/formatting.js' +import { getDefaultKey, setupSdk } from '../../utils/sdk.js' + +/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */ +export const search = { + description: 'Search for any dependency that is being used in your organization', + async run (argv, importMeta, { parentName }) { + const name = parentName + ' search' + + const input = setupCommand(name, search.description, argv, importMeta) + if (input) { + const spinnerText = 'Searching dependencies...' + const spinner = ora(spinnerText).start() + await searchDeps(input, spinner) + } + } +} + +const dependenciesFlags = prepareFlags({ + limit: { + type: 'number', + shortFlag: 'l', + default: 50, + description: 'Maximum number of dependencies returned', + }, + offset: { + type: 'number', + shortFlag: 'o', + default: 0, + description: 'Page number', + } + }) + +// Internal functions + +/** + * @typedef Command + * @property {boolean} outputJson + * @property {boolean} outputMarkdown + * @property {number} limit + * @property {number} offset + */ + +/** + * @param {string} name + * @param {string} description + * @param {readonly string[]} argv + * @param {ImportMeta} importMeta + * @returns {void|Command} + */ +function setupCommand (name, description, argv, importMeta) { + const flags = { + ...outputFlags, + ...dependenciesFlags + } + + const cli = meow(` + Usage + $ ${name} + + Options + ${printFlagList(flags, 6)} + + Examples + $ ${name} + `, { + argv, + description, + importMeta, + flags + }) + + const { + json: outputJson, + markdown: outputMarkdown, + limit, + offset + } = cli.flags + + return { + outputJson, + outputMarkdown, + limit, + offset + } +} + +/** + * @typedef DependenciesData + * @property {import('@socketsecurity/sdk').SocketSdkReturnType<'searchDependencies'>["data"]} data + */ + +/** + * @param {Command} input + * @param {import('ora').Ora} spinner + * @returns {Promise} + */ +async function searchDeps ({ limit, offset, outputJson }, spinner) { + const socketSdk = await setupSdk(getDefaultKey()) + const result = await handleApiCall(socketSdk.searchDependencies({ limit, offset }), 'Searching dependencies') + + if (!result.success) { + return handleUnsuccessfulApiResponse('searchDependencies', result, spinner) + } + + spinner.stop() + + console.log('Organization dependencies: \n') + + if (outputJson) { + return console.log(result.data) + } + + const options = { + columns: [ + { field: 'namespace', name: chalk.cyan('Namespace') }, + { field: 'name', name: chalk.cyan('Name') }, + { field: 'version', name: chalk.cyan('Version') }, + { field: 'repository', name: chalk.cyan('Repository') }, + { field: 'branch', name: chalk.cyan('Branch') }, + { field: 'type', name: chalk.cyan('Type') }, + { field: 'direct', name: chalk.cyan('Direct') } + ] + } + + const formattedResults = result.data.rows.map((/** @type {{[key:string]: any}} */ d) => { + return { + ...d + } + }) + + const table = chalkTable(options, formattedResults) + + console.log(table, '\n') + + return { + data: result.data + } +} diff --git a/lib/commands/dependencies/upload.js b/lib/commands/dependencies/upload.js new file mode 100644 index 00000000..df19f907 --- /dev/null +++ b/lib/commands/dependencies/upload.js @@ -0,0 +1,136 @@ +/* eslint-disable no-console */ + +import chalk from 'chalk' +import meow from 'meow' +import ora from 'ora' + +import { outputFlags } from '../../flags/index.js' +import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js' +import { prepareFlags } from '../../utils/flags.js' +import { printFlagList } from '../../utils/formatting.js' +import { getDefaultKey, setupSdk } from '../../utils/sdk.js' + +/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */ +export const upload = { + description: 'Upload dependency that is being used in your organization', + async run (argv, importMeta, { parentName }) { + const name = parentName + ' upload' + + const input = setupCommand(name, upload.description, argv, importMeta) + if (input) { + const spinnerText = 'Uploading dependencies...' + const spinner = ora(spinnerText).start() + await uploadDeps(input, spinner) + } + } +} + +const dependenciesFlags = prepareFlags({ + repository: { + type: 'string', + shortFlag: 'r', + default: '', + description: 'Repository name', + }, + branch: { + type: 'string', + shortFlag: 'b', + default: '', + description: 'Branch name', + }, + }) + +// Internal functions + +/** + * @typedef Command + * @property {boolean} outputMarkdown + * @property {string} repository + * @property {string} branch + * @property {string[]} filePaths + */ + +/** + * @param {string} name + * @param {string} description + * @param {readonly string[]} argv + * @param {ImportMeta} importMeta + * @returns {void|Command} + */ +function setupCommand (name, description, argv, importMeta) { + const flags = { + ...outputFlags, + ...dependenciesFlags + } + + const cli = meow(` + Usage + $ ${name} + + Options + ${printFlagList(flags, 6)} + + Examples + $ ${name} + `, { + argv, + description, + importMeta, + flags + }) + + const { + markdown: outputMarkdown, + repository, + branch + } = cli.flags + + if (!repository || !branch) { + console.error(`${chalk.white.bgRed('Input error')}: Please provide a repository name and branch name.`) + cli.showHelp() + return + } + + const filePaths = cli.input + + if (!filePaths.length) { + console.error(`${chalk.white.bgRed('Input error')}: Please provide file paths.`) + cli.showHelp() + return + } + + return { + outputMarkdown, + repository, + branch, + filePaths + } +} + +/** + * @typedef DependenciesData + * @property {import('@socketsecurity/sdk').SocketSdkReturnType<'createDependenciesSnapshot'>["data"]} data + */ + +/** + * @param {Command} input + * @param {import('ora').Ora} spinner + * @returns {Promise} + */ +async function uploadDeps ({ repository, branch, filePaths }, spinner) { + const socketSdk = await setupSdk(getDefaultKey()) + + const result = await handleApiCall(socketSdk.createDependenciesSnapshot({ repository, branch }, filePaths), 'Uploading dependencies') + + if (!result.success) { + return handleUnsuccessfulApiResponse('createDependenciesSnapshot', result, spinner) + } + + spinner.stop() + + console.log('\n✅ Dependencies snapshot uploaded successfully \n') + + return { + data: result.data + } +} diff --git a/lib/commands/scan/create.js b/lib/commands/scan/create.js index 09093213..a2d4ffe0 100644 --- a/lib/commands/scan/create.js +++ b/lib/commands/scan/create.js @@ -125,7 +125,7 @@ async function setupCommand (name, description, argv, importMeta) { ${printFlagList(flags, 6)} Examples - $ ${name} --org=FakeOrg --repo=test-repo --branch=main ./package.json + $ ${name} FakeOrg --repo=test-repo --branch=main ./package.json `, { argv, description, diff --git a/package-lock.json b/package-lock.json index 0d45acd8..1994ec17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@cyclonedx/cdxgen": "^10.7.0", "@inquirer/select": "^2.3.5", "@socketsecurity/config": "^2.1.3", - "@socketsecurity/sdk": "^1.2.0", + "@socketsecurity/sdk": "^1.3.0", "chalk": "^5.3.0", "chalk-table": "^1.0.2", "execa": "^9.1.0", @@ -1625,9 +1625,10 @@ } }, "node_modules/@socketsecurity/sdk": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@socketsecurity/sdk/-/sdk-1.2.0.tgz", - "integrity": "sha512-XvOIJJsmzivaJWyUwNOcCAxcBBQtRLoE4mYbdrpgi1gagdgmau3dzSq/OC3vgrTV27iS9zfJLP8gqjrposuhGQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@socketsecurity/sdk/-/sdk-1.3.0.tgz", + "integrity": "sha512-AwztC4M3qGcsJgw4Y6EbzF97ETAOFf4s1Qj8T9k/nKGKQabhGX6PO0x90CTgoPhWhyrKffd9DnUA6ClwrV580Q==", + "license": "MIT", "dependencies": { "formdata-node": "^5.0.0", "got": "^12.5.3", diff --git a/package.json b/package.json index fdb434c0..68ff2578 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "@cyclonedx/cdxgen": "^10.7.0", "@inquirer/select": "^2.3.5", "@socketsecurity/config": "^2.1.3", - "@socketsecurity/sdk": "^1.2.0", + "@socketsecurity/sdk": "^1.3.0", "chalk": "^5.3.0", "chalk-table": "^1.0.2", "execa": "^9.1.0",