From d343f2ec01c88848417d446af92d0dbe1627eea6 Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Thu, 8 May 2025 16:56:16 +0200 Subject: [PATCH 1/5] feat: enhance CLIUtils with JSON flag support for various methods --- src/utils/cli.utils.ts | 77 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/src/utils/cli.utils.ts b/src/utils/cli.utils.ts index 4034cabb..0a810b1f 100644 --- a/src/utils/cli.utils.ts +++ b/src/utils/cli.utils.ts @@ -3,11 +3,14 @@ import cliProgress from 'cli-progress'; import Table, { Header } from 'tty-table'; import { PromptOptions } from '../types/command.types'; import { InquirerUtils } from './inquirer.utils'; +import { ErrorUtils } from './errors.utils'; export class CLIUtils { - static readonly clearPreviousLine = () => { - process.stdout.write('\x1b[1A'); - process.stdout.clearLine(0); + static readonly clearPreviousLine = (jsonFlag?: boolean) => { + if (!jsonFlag) { + process.stdout?.write?.('\x1b[1A'); + process.stdout?.clearLine?.(0); + } }; static readonly warning = (reporter: (message: string) => void, message: string) => { @@ -18,10 +21,6 @@ export class CLIUtils { reporter(ux.colorize('red', `⚠ Error: ${message}`)); }; - static readonly doing = (message: string) => { - ux.action.start(message, undefined, {}); - }; - static readonly success = (reporter: (message: string) => void, message: string) => { reporter(ux.colorize('green', `✓ ${message}`)); }; @@ -30,15 +29,36 @@ export class CLIUtils { reporter(`${message}`); }; - static readonly done = () => { - ux.action.stop(ux.colorize('green', 'done ✓')); + static readonly consoleLog = (message: string) => { + // eslint-disable-next-line no-console + console.log(message); + }; + + static readonly doing = (message: string, jsonFlag?: boolean) => { + if (!jsonFlag) { + ux.action.start(message, undefined, {}); + } + }; + + static readonly done = (jsonFlag?: boolean) => { + if (!jsonFlag && ux.action.running) { + ux.action.stop(ux.colorize('green', 'done ✓')); + } + }; + + static readonly failed = (jsonFlag?: boolean) => { + if (!jsonFlag && ux.action.running) { + ux.action.stop(ux.colorize('red', 'failed ✕')); + } }; - static readonly progress = (opts: cliProgress.Options) => { - return new cliProgress.SingleBar( - { noTTYOutput: Boolean(!process.stdin.isTTY), ...opts }, - cliProgress.Presets.shades_classic, - ); + static readonly progress = (opts: cliProgress.Options, jsonFlag?: boolean) => { + if (!jsonFlag) { + return new cliProgress.SingleBar( + { noTTYOutput: Boolean(!process.stdin.isTTY), ...opts }, + cliProgress.Presets.shades_classic, + ); + } }; static readonly table = (reporter: (message: string) => void, header: Header[], rows: object[]) => { @@ -152,6 +172,35 @@ export class CLIUtils { }; }; + static readonly catchError = ({ + error, + logReporter, + errorReporter, + command, + jsonFlag, + }: { + error: Error; + command?: string; + logReporter: (message: string) => void; + errorReporter: (message: string) => void; + jsonFlag?: boolean; + }) => { + let message; + if ('message' in error && error.message.trim().length > 0) { + message = error.message; + } else { + message = JSON.stringify(error); + } + + CLIUtils.failed(jsonFlag); + if (jsonFlag) { + CLIUtils.consoleLog(JSON.stringify({ success: false, message })); + } else { + ErrorUtils.report(errorReporter, error, { command }); + CLIUtils.error(logReporter, message); + } + }; + static readonly parseEmpty = async (input: string) => (input.trim().length === 0 ? ' ' : input); } From 0ea4886a34b94504612c9d9225fd4d719b5dff99 Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Thu, 8 May 2025 16:56:57 +0200 Subject: [PATCH 2/5] feat: add JSON flag support for credential checks in prerun hook --- src/hooks/prerun/auth_check.ts | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/hooks/prerun/auth_check.ts b/src/hooks/prerun/auth_check.ts index 52ef81e5..f9768208 100644 --- a/src/hooks/prerun/auth_check.ts +++ b/src/hooks/prerun/auth_check.ts @@ -11,21 +11,28 @@ import WebDAVConfig from '../../commands/webdav-config'; const CommandsToSkip = [Whoami, Login, Logout, Logs, Webdav, WebDAVConfig]; const hook: Hook<'prerun'> = async function (opts) { - if (!CommandsToSkip.map((command) => command.name).includes(opts.Command.name)) { - CLIUtils.doing('Checking credentials'); + const { Command, argv } = opts; + const jsonFlag = argv.includes('--json'); + + if (!CommandsToSkip.map((command) => command.name).includes(Command.name)) { + CLIUtils.doing('Checking credentials', jsonFlag); try { const { token, newToken } = await AuthService.instance.getAuthDetails(); SdkManager.init({ token, newToken, }); - CLIUtils.done(); - CLIUtils.clearPreviousLine(); + CLIUtils.done(jsonFlag); + CLIUtils.clearPreviousLine(jsonFlag); } catch (error) { const err = error as Error; - CLIUtils.done(); - CLIUtils.clearPreviousLine(); - CLIUtils.error(this.log.bind(this), err.message); + CLIUtils.catchError({ + error: err, + command: Command.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag, + }); opts.context.exit(1); } } From 05d70f7954c5c7e28bdba4dd95bcbb0fef7fb411 Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Thu, 8 May 2025 17:01:36 +0200 Subject: [PATCH 3/5] refactor: replace ErrorUtils with CLIUtils.catchError for improved error handling in command catch blocks --- src/commands/add-cert.ts | 47 ++++++++++++----------- src/commands/config.ts | 11 ++++-- src/commands/create-folder.ts | 15 +++++--- src/commands/delete-permanently-file.ts | 11 ++++-- src/commands/delete-permanently-folder.ts | 11 ++++-- src/commands/download-file.ts | 46 +++++++++++++--------- src/commands/list.ts | 11 ++++-- src/commands/login.ts | 11 ++++-- src/commands/logout.ts | 11 ++++-- src/commands/logs.ts | 11 ++++-- src/commands/move-file.ts | 11 ++++-- src/commands/move-folder.ts | 11 ++++-- src/commands/rename-file.ts | 11 ++++-- src/commands/rename-folder.ts | 11 ++++-- src/commands/trash-clear.ts | 11 ++++-- src/commands/trash-file.ts | 11 ++++-- src/commands/trash-folder.ts | 11 ++++-- src/commands/trash-list.ts | 11 ++++-- src/commands/trash-restore-file.ts | 11 ++++-- src/commands/trash-restore-folder.ts | 11 ++++-- src/commands/upload-file.ts | 33 ++++++++++------ src/commands/webdav-config.ts | 11 ++++-- src/commands/webdav.ts | 43 +++++++++++---------- src/commands/whoami.ts | 11 ++++-- 24 files changed, 256 insertions(+), 137 deletions(-) diff --git a/src/commands/add-cert.ts b/src/commands/add-cert.ts index 4fe54cad..23c6dc73 100644 --- a/src/commands/add-cert.ts +++ b/src/commands/add-cert.ts @@ -4,7 +4,6 @@ import { ConfigService } from '../services/config.service'; import os from 'node:os'; import path from 'node:path'; import { CLIUtils } from '../utils/cli.utils'; -import { ErrorUtils } from '../utils/errors.utils'; export default class AddCert extends Command { static readonly args = {}; @@ -15,34 +14,36 @@ export default class AddCert extends Command { static readonly enableJsonFlag = true; public run = async () => { - try { - const certPath = path.join(ConfigService.WEBDAV_SSL_CERTS_DIR, 'cert.crt'); - const platform = os.platform(); + const certPath = path.join(ConfigService.WEBDAV_SSL_CERTS_DIR, 'cert.crt'); + const platform = os.platform(); - const scriptBasePath = path.join(__dirname, '../../scripts'); - let command = ''; + const scriptBasePath = path.join(__dirname, '../../scripts'); + let command = ''; - if (platform === 'win32') { - // eslint-disable-next-line max-len - command = `powershell -ExecutionPolicy Bypass -File "${path.join(scriptBasePath, 'add-cert.ps1')}" -certPath "${certPath}"`; - } else if (platform === 'darwin' || platform === 'linux') { - command = `bash "${path.join(scriptBasePath, 'add-cert.sh')}" "${certPath}"`; - } else { - throw new Error(`Unsupported OS: ${platform}`); - } - - await this.executeCommand(command); - const message = 'Certificate successfully added to the trusted store.'; - CLIUtils.success(this.log.bind(this), message); - return { success: true, message }; - } catch (error) { - await this.catch(error as Error); + if (platform === 'win32') { + // eslint-disable-next-line max-len + command = `powershell -ExecutionPolicy Bypass -File "${path.join(scriptBasePath, 'add-cert.ps1')}" -certPath "${certPath}"`; + } else if (platform === 'darwin' || platform === 'linux') { + command = `bash "${path.join(scriptBasePath, 'add-cert.sh')}" "${certPath}"`; + } else { + throw new Error(`Unsupported OS: ${platform}`); } + + await this.executeCommand(command); + const message = 'Certificate successfully added to the trusted store.'; + CLIUtils.success(this.log.bind(this), message); + return { success: true, message }; }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(AddCert); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; diff --git a/src/commands/config.ts b/src/commands/config.ts index e4f249fa..07fd59e1 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -1,7 +1,6 @@ import { Command } from '@oclif/core'; import { ConfigService } from '../services/config.service'; import { CLIUtils } from '../utils/cli.utils'; -import { ErrorUtils } from '../utils/errors.utils'; import { UsageService } from '../services/usage.service'; import { FormatUtils } from '../utils/format.utils'; import { Header } from 'tty-table'; @@ -41,8 +40,14 @@ export default class Config extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(Config); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; } diff --git a/src/commands/create-folder.ts b/src/commands/create-folder.ts index e0c6878b..27e5c774 100644 --- a/src/commands/create-folder.ts +++ b/src/commands/create-folder.ts @@ -1,5 +1,4 @@ import { Command, Flags } from '@oclif/core'; -import { ErrorUtils } from '../utils/errors.utils'; import { CLIUtils } from '../utils/cli.utils'; import { DriveFolderService } from '../services/drive/drive-folder.service'; import { ConfigService } from '../services/config.service'; @@ -42,7 +41,7 @@ export default class CreateFolder extends Command { folderUuid = userCredentials.user.rootFolderId; } - CLIUtils.doing('Creating folder...'); + CLIUtils.doing('Creating folder...', flags['json']); const [createNewFolder, requestCanceler] = DriveFolderService.instance.createFolder({ plainName: folderName, parentFolderUuid: folderUuid, @@ -54,7 +53,7 @@ export default class CreateFolder extends Command { }); const newFolder = await createNewFolder; - CLIUtils.done(); + CLIUtils.done(flags['json']); // eslint-disable-next-line max-len const message = `Folder ${newFolder.plainName} created successfully, view it at ${ConfigService.instance.get('DRIVE_URL')}/folder/${newFolder.uuid}`; CLIUtils.success(this.log.bind(this), message); @@ -62,8 +61,14 @@ export default class CreateFolder extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(CreateFolder); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; diff --git a/src/commands/delete-permanently-file.ts b/src/commands/delete-permanently-file.ts index 530506b0..a2837406 100644 --- a/src/commands/delete-permanently-file.ts +++ b/src/commands/delete-permanently-file.ts @@ -4,7 +4,6 @@ import { CLIUtils } from '../utils/cli.utils'; import { MissingCredentialsError, NotValidFileUuidError } from '../types/command.types'; import { ValidationService } from '../services/validation.service'; import { DriveFileService } from '../services/drive/drive-file.service'; -import { ErrorUtils } from '../utils/errors.utils'; import { TrashService } from '../services/drive/trash.service'; export default class DeletePermanentlyFile extends Command { @@ -43,8 +42,14 @@ export default class DeletePermanentlyFile extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(DeletePermanentlyFile); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; diff --git a/src/commands/delete-permanently-folder.ts b/src/commands/delete-permanently-folder.ts index 7aaa25a9..0fc8cc6b 100644 --- a/src/commands/delete-permanently-folder.ts +++ b/src/commands/delete-permanently-folder.ts @@ -3,7 +3,6 @@ import { ConfigService } from '../services/config.service'; import { CLIUtils } from '../utils/cli.utils'; import { MissingCredentialsError, NotValidFolderUuidError } from '../types/command.types'; import { ValidationService } from '../services/validation.service'; -import { ErrorUtils } from '../utils/errors.utils'; import { TrashService } from '../services/drive/trash.service'; import { DriveFolderService } from '../services/drive/drive-folder.service'; @@ -43,8 +42,14 @@ export default class DeletePermanentlyFolder extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(DeletePermanentlyFolder); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; diff --git a/src/commands/download-file.ts b/src/commands/download-file.ts index b4f6c7d0..936b294c 100644 --- a/src/commands/download-file.ts +++ b/src/commands/download-file.ts @@ -12,7 +12,6 @@ import { DriveFileItem } from '../types/drive.types'; import fs from 'node:fs/promises'; import path from 'node:path'; import { StreamUtils } from '../utils/stream.utils'; -import { ErrorUtils } from '../utils/errors.utils'; import { NotValidDirectoryError, NotValidFileUuidError } from '../types/command.types'; import { ValidationService } from '../services/validation.service'; import { Environment } from '@internxt/inxt-js'; @@ -60,22 +59,25 @@ export default class DownloadFile extends Command { const fileUuid = await this.getFileUuid(flags['id'], nonInteractive); // 1. Get file metadata - const driveFile = await this.getFileMetadata(fileUuid); + const driveFile = await this.getFileMetadata(fileUuid, flags['json']); const downloadPath = await this.getDownloadPath(downloadDirectory, driveFile, overwrite); // 2. Prepare the network const { user } = await AuthService.instance.getAuthDetails(); - const networkFacade = await this.prepareNetwork(user); + const networkFacade = await this.prepareNetwork(user, flags['json']); // 3. Download the file const fileWriteStream = createWriteStream(downloadPath); - const progressBar = CLIUtils.progress({ - format: 'Downloading file [{bar}] {percentage}%', - linewrap: true, - }); + const progressBar = CLIUtils.progress( + { + format: 'Downloading file [{bar}] {percentage}%', + linewrap: true, + }, + flags['json'], + ); - progressBar.start(100, 0); + progressBar?.start(100, 0); const [executeDownload, abortable] = await networkFacade.downloadToStream( user.bucket, user.mnemonic, @@ -86,7 +88,7 @@ export default class DownloadFile extends Command { { abortController: new AbortController(), progressCallback: (progress) => { - progressBar.update(progress * 0.99); + progressBar?.update(progress * 0.99); }, }, ); @@ -98,16 +100,22 @@ export default class DownloadFile extends Command { await executeDownload; - progressBar.update(100); - progressBar.stop(); + progressBar?.update(100); + progressBar?.stop(); const message = `File downloaded successfully to ${downloadPath}`; CLIUtils.success(this.log.bind(this), message); return { success: true, message, file: driveFile }; }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(DownloadFile); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; @@ -156,10 +164,10 @@ export default class DownloadFile extends Command { return directory; }; - private getFileMetadata = async (uuid: string) => { - CLIUtils.doing('Getting file metadata'); + private getFileMetadata = async (uuid: string, jsonFlag?: boolean) => { + CLIUtils.doing('Getting file metadata', jsonFlag); const driveFile = await DriveFileService.instance.getFileMetadata(uuid); - CLIUtils.done(); + CLIUtils.done(jsonFlag); if (!driveFile) { throw new Error('File not found'); } @@ -192,8 +200,8 @@ export default class DownloadFile extends Command { return downloadPath; }; - private prepareNetwork = async (user: UserSettings) => { - CLIUtils.doing('Preparing Network'); + private prepareNetwork = async (user: UserSettings, jsonFlag?: boolean) => { + CLIUtils.doing('Preparing Network', jsonFlag); const networkModule = SdkManager.instance.getNetwork({ user: user.bridgeUser, @@ -211,7 +219,7 @@ export default class DownloadFile extends Command { DownloadService.instance, CryptoService.instance, ); - CLIUtils.done(); + CLIUtils.done(jsonFlag); return networkFacade; }; diff --git a/src/commands/list.ts b/src/commands/list.ts index ac0442e3..4a6c46a3 100644 --- a/src/commands/list.ts +++ b/src/commands/list.ts @@ -5,7 +5,6 @@ import { CLIUtils } from '../utils/cli.utils'; import { MissingCredentialsError, NotValidFolderUuidError, PaginatedItem } from '../types/command.types'; import { ValidationService } from '../services/validation.service'; import { FormatUtils } from '../utils/format.utils'; -import { ErrorUtils } from '../utils/errors.utils'; import { Header } from 'tty-table'; export default class List extends Command { @@ -78,8 +77,14 @@ export default class List extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(List); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; diff --git a/src/commands/login.ts b/src/commands/login.ts index 4b7e2cb7..33324a2f 100644 --- a/src/commands/login.ts +++ b/src/commands/login.ts @@ -4,7 +4,6 @@ import { AuthService } from '../services/auth.service'; import { ConfigService } from '../services/config.service'; import { ValidationService } from '../services/validation.service'; import { CLIUtils } from '../utils/cli.utils'; -import { ErrorUtils } from '../utils/errors.utils'; import { SdkManager } from '../services/sdk-manager.service'; export default class Login extends Command { @@ -71,8 +70,14 @@ export default class Login extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(Login); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; diff --git a/src/commands/logout.ts b/src/commands/logout.ts index 26d13c67..3d3ec770 100644 --- a/src/commands/logout.ts +++ b/src/commands/logout.ts @@ -1,7 +1,6 @@ import { Command } from '@oclif/core'; import { ConfigService } from '../services/config.service'; import { CLIUtils } from '../utils/cli.utils'; -import { ErrorUtils } from '../utils/errors.utils'; export default class Logout extends Command { static readonly args = {}; @@ -26,8 +25,14 @@ export default class Logout extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(Logout); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; } diff --git a/src/commands/logs.ts b/src/commands/logs.ts index 636becfc..902f55dc 100644 --- a/src/commands/logs.ts +++ b/src/commands/logs.ts @@ -1,7 +1,6 @@ import { Command } from '@oclif/core'; import { ConfigService } from '../services/config.service'; import { CLIUtils } from '../utils/cli.utils'; -import { ErrorUtils } from '../utils/errors.utils'; export default class Logs extends Command { static readonly args = {}; @@ -18,8 +17,14 @@ export default class Logs extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(Logs); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; } diff --git a/src/commands/move-file.ts b/src/commands/move-file.ts index a54d3780..8f7b3d0a 100644 --- a/src/commands/move-file.ts +++ b/src/commands/move-file.ts @@ -4,7 +4,6 @@ import { CLIUtils } from '../utils/cli.utils'; import { MissingCredentialsError, NotValidFileUuidError, NotValidFolderUuidError } from '../types/command.types'; import { ValidationService } from '../services/validation.service'; import { DriveFileService } from '../services/drive/drive-file.service'; -import { ErrorUtils } from '../utils/errors.utils'; export default class MoveFile extends Command { static readonly args = {}; @@ -48,8 +47,14 @@ export default class MoveFile extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(MoveFile); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; diff --git a/src/commands/move-folder.ts b/src/commands/move-folder.ts index bdbf0ee1..f12bb46f 100644 --- a/src/commands/move-folder.ts +++ b/src/commands/move-folder.ts @@ -4,7 +4,6 @@ import { DriveFolderService } from '../services/drive/drive-folder.service'; import { CLIUtils } from '../utils/cli.utils'; import { MissingCredentialsError, NotValidFolderUuidError } from '../types/command.types'; import { ValidationService } from '../services/validation.service'; -import { ErrorUtils } from '../utils/errors.utils'; export default class MoveFolder extends Command { static readonly args = {}; @@ -48,8 +47,14 @@ export default class MoveFolder extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(MoveFolder); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; diff --git a/src/commands/rename-file.ts b/src/commands/rename-file.ts index 2564d005..ff8d3d38 100644 --- a/src/commands/rename-file.ts +++ b/src/commands/rename-file.ts @@ -4,7 +4,6 @@ import { CLIUtils } from '../utils/cli.utils'; import { EmptyFileNameError, MissingCredentialsError, NotValidFileUuidError } from '../types/command.types'; import { ValidationService } from '../services/validation.service'; import { DriveFileService } from '../services/drive/drive-file.service'; -import { ErrorUtils } from '../utils/errors.utils'; export default class RenameFile extends Command { static readonly args = {}; @@ -43,8 +42,14 @@ export default class RenameFile extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(RenameFile); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; diff --git a/src/commands/rename-folder.ts b/src/commands/rename-folder.ts index 1328ad05..b15714b6 100644 --- a/src/commands/rename-folder.ts +++ b/src/commands/rename-folder.ts @@ -4,7 +4,6 @@ import { CLIUtils } from '../utils/cli.utils'; import { EmptyFolderNameError, MissingCredentialsError, NotValidFolderUuidError } from '../types/command.types'; import { ValidationService } from '../services/validation.service'; import { DriveFolderService } from '../services/drive/drive-folder.service'; -import { ErrorUtils } from '../utils/errors.utils'; export default class RenameFolder extends Command { static readonly args = {}; @@ -43,8 +42,14 @@ export default class RenameFolder extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(RenameFolder); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; diff --git a/src/commands/trash-clear.ts b/src/commands/trash-clear.ts index aabdc4ab..39bf22e8 100644 --- a/src/commands/trash-clear.ts +++ b/src/commands/trash-clear.ts @@ -2,7 +2,6 @@ import { Command, Flags } from '@oclif/core'; import { ConfigService } from '../services/config.service'; import { CLIUtils } from '../utils/cli.utils'; import { MissingCredentialsError } from '../types/command.types'; -import { ErrorUtils } from '../utils/errors.utils'; import { TrashService } from '../services/drive/trash.service'; import { InquirerUtils } from '../utils/inquirer.utils'; @@ -50,8 +49,14 @@ export default class TrashClear extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(TrashClear); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; diff --git a/src/commands/trash-file.ts b/src/commands/trash-file.ts index c9594b0a..991c1c98 100644 --- a/src/commands/trash-file.ts +++ b/src/commands/trash-file.ts @@ -3,7 +3,6 @@ import { ConfigService } from '../services/config.service'; import { CLIUtils } from '../utils/cli.utils'; import { MissingCredentialsError, NotValidFileUuidError } from '../types/command.types'; import { ValidationService } from '../services/validation.service'; -import { ErrorUtils } from '../utils/errors.utils'; import { TrashService } from '../services/drive/trash.service'; export default class TrashFile extends Command { @@ -38,8 +37,14 @@ export default class TrashFile extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(TrashFile); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; diff --git a/src/commands/trash-folder.ts b/src/commands/trash-folder.ts index ccee06ae..87512377 100644 --- a/src/commands/trash-folder.ts +++ b/src/commands/trash-folder.ts @@ -3,7 +3,6 @@ import { ConfigService } from '../services/config.service'; import { CLIUtils } from '../utils/cli.utils'; import { MissingCredentialsError, NotValidFolderUuidError } from '../types/command.types'; import { ValidationService } from '../services/validation.service'; -import { ErrorUtils } from '../utils/errors.utils'; import { TrashService } from '../services/drive/trash.service'; export default class TrashFolder extends Command { @@ -38,8 +37,14 @@ export default class TrashFolder extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(TrashFolder); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; diff --git a/src/commands/trash-list.ts b/src/commands/trash-list.ts index 104891ac..1d62af0f 100644 --- a/src/commands/trash-list.ts +++ b/src/commands/trash-list.ts @@ -3,7 +3,6 @@ import { ConfigService } from '../services/config.service'; import { CLIUtils } from '../utils/cli.utils'; import { MissingCredentialsError, PaginatedItem } from '../types/command.types'; import { FormatUtils } from '../utils/format.utils'; -import { ErrorUtils } from '../utils/errors.utils'; import { TrashService } from '../services/drive/trash.service'; import { Header } from 'tty-table'; @@ -63,8 +62,14 @@ export default class TrashList extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(TrashList); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; } diff --git a/src/commands/trash-restore-file.ts b/src/commands/trash-restore-file.ts index 11cac966..1dfe2971 100644 --- a/src/commands/trash-restore-file.ts +++ b/src/commands/trash-restore-file.ts @@ -4,7 +4,6 @@ import { CLIUtils } from '../utils/cli.utils'; import { MissingCredentialsError, NotValidFileUuidError, NotValidFolderUuidError } from '../types/command.types'; import { ValidationService } from '../services/validation.service'; import { DriveFileService } from '../services/drive/drive-file.service'; -import { ErrorUtils } from '../utils/errors.utils'; export default class TrashRestoreFile extends Command { static readonly args = {}; @@ -50,8 +49,14 @@ export default class TrashRestoreFile extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(TrashRestoreFile); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; diff --git a/src/commands/trash-restore-folder.ts b/src/commands/trash-restore-folder.ts index 1bfbf6de..e041b9a3 100644 --- a/src/commands/trash-restore-folder.ts +++ b/src/commands/trash-restore-folder.ts @@ -4,7 +4,6 @@ import { DriveFolderService } from '../services/drive/drive-folder.service'; import { CLIUtils } from '../utils/cli.utils'; import { MissingCredentialsError, NotValidFolderUuidError } from '../types/command.types'; import { ValidationService } from '../services/validation.service'; -import { ErrorUtils } from '../utils/errors.utils'; export default class TrashRestoreFolder extends Command { static readonly args = {}; @@ -50,8 +49,14 @@ export default class TrashRestoreFolder extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(TrashRestoreFolder); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; diff --git a/src/commands/upload-file.ts b/src/commands/upload-file.ts index 60033297..05d98e50 100644 --- a/src/commands/upload-file.ts +++ b/src/commands/upload-file.ts @@ -66,7 +66,7 @@ export default class UploadFile extends Command { } // 1. Prepare the network - CLIUtils.doing('Preparing Network'); + CLIUtils.doing('Preparing Network', flags['json']); const { user } = await AuthService.instance.getAuthDetails(); const networkModule = SdkManager.instance.getNetwork({ user: user.bridgeUser, @@ -85,16 +85,19 @@ export default class UploadFile extends Command { CryptoService.instance, ); - CLIUtils.done(); + CLIUtils.done(flags['json']); // 2. Upload file to the Network const readStream = createReadStream(filePath); const timer = CLIUtils.timer(); - const progressBar = CLIUtils.progress({ - format: 'Uploading file [{bar}] {percentage}%', - linewrap: true, - }); - progressBar.start(100, 0); + const progressBar = CLIUtils.progress( + { + format: 'Uploading file [{bar}] {percentage}%', + linewrap: true, + }, + flags['json'], + ); + progressBar?.start(100, 0); let bufferStream: BufferStream | undefined; let fileStream: Readable = readStream; @@ -105,7 +108,7 @@ export default class UploadFile extends Command { } const progressCallback = (progress: number) => { - progressBar.update(progress * 100 * 0.99); + progressBar?.update(progress * 100 * 0.99); }; const fileId = await new Promise((resolve: (fileId: string) => void, reject) => { @@ -157,8 +160,8 @@ export default class UploadFile extends Command { ErrorUtils.report(this.error.bind(this), error, { command: this.id }); } - progressBar.update(100); - progressBar.stop(); + progressBar?.update(100); + progressBar?.stop(); const uploadTime = timer.stop(); this.log('\n'); @@ -169,8 +172,14 @@ export default class UploadFile extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(UploadFile); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; diff --git a/src/commands/webdav-config.ts b/src/commands/webdav-config.ts index 90760d34..9d6d74f3 100644 --- a/src/commands/webdav-config.ts +++ b/src/commands/webdav-config.ts @@ -1,7 +1,6 @@ import { Command, Flags } from '@oclif/core'; import { ConfigService } from '../services/config.service'; import { CLIUtils } from '../utils/cli.utils'; -import { ErrorUtils } from '../utils/errors.utils'; import { NotValidPortError } from '../types/command.types'; import { ValidationService } from '../services/validation.service'; @@ -72,8 +71,14 @@ export default class WebDAVConfig extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(WebDAVConfig); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; } diff --git a/src/commands/webdav.ts b/src/commands/webdav.ts index ea842f9e..3528eef7 100644 --- a/src/commands/webdav.ts +++ b/src/commands/webdav.ts @@ -2,7 +2,6 @@ import { Args, Command, ux } from '@oclif/core'; import { PM2Utils } from '../utils/pm2.utils'; import { CLIUtils } from '../utils/cli.utils'; import { ConfigService } from '../services/config.service'; -import { ErrorUtils } from '../utils/errors.utils'; import { AuthService } from '../services/auth.service'; export default class Webdav extends Command { @@ -24,7 +23,7 @@ export default class Webdav extends Command { static readonly enableJsonFlag = true; public run = async () => { - const { args } = await this.parse(Webdav); + const { args, flags } = await this.parse(Webdav); let message = ''; let success = true; @@ -32,18 +31,18 @@ export default class Webdav extends Command { switch (args.action) { case 'enable': { await AuthService.instance.getAuthDetails(); - message = await this.enableWebDav(); + message = await this.enableWebDav(flags['json']); break; } case 'disable': { - message = await this.disableWebDav(); + message = await this.disableWebDav(flags['json']); break; } case 'restart': { await AuthService.instance.getAuthDetails(); - message = await this.restartWebDav(); + message = await this.restartWebDav(flags['json']); break; } @@ -64,20 +63,22 @@ export default class Webdav extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - if (error instanceof Error) { - CLIUtils.error(this.log.bind(this), error.message); - } else { - CLIUtils.error(this.log.bind(this), JSON.stringify(error)); - } + const { flags } = await this.parse(Webdav); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; - private enableWebDav = async (): Promise => { - CLIUtils.doing('Starting Internxt WebDav server...'); + private enableWebDav = async (jsonFlag?: boolean): Promise => { + CLIUtils.doing('Starting Internxt WebDav server...', jsonFlag); await PM2Utils.killWebDavServer(); await PM2Utils.startWebDavServer(); - CLIUtils.done(); + CLIUtils.done(jsonFlag); const { status } = await PM2Utils.webdavServerStatus(); const webdavConfigs = await ConfigService.instance.readWebdavConfig(); @@ -99,27 +100,27 @@ export default class Webdav extends Command { } }; - private disableWebDav = async (): Promise => { - CLIUtils.doing('Stopping Internxt WebDav server...'); + private disableWebDav = async (jsonFlag?: boolean): Promise => { + CLIUtils.doing('Stopping Internxt WebDav server...', jsonFlag); await PM2Utils.killWebDavServer(); - CLIUtils.done(); + CLIUtils.done(jsonFlag); const message = 'Internxt WebDav server stopped successfully'; CLIUtils.success(this.log.bind(this), message); return message; }; - private restartWebDav = async (): Promise => { - CLIUtils.doing('Restarting Internxt WebDav server...'); + private restartWebDav = async (jsonFlag?: boolean): Promise => { + CLIUtils.doing('Restarting Internxt WebDav server...', jsonFlag); const { status } = await PM2Utils.webdavServerStatus(); if (status === 'online') { await PM2Utils.killWebDavServer(); await PM2Utils.startWebDavServer(); - CLIUtils.done(); + CLIUtils.done(jsonFlag); const message = 'Internxt WebDav server restarted successfully'; CLIUtils.success(this.log.bind(this), message); return message; } else { - CLIUtils.done(); + CLIUtils.done(jsonFlag); const message = 'Internxt WebDav server is not running, it wont be restarted'; CLIUtils.warning(this.log.bind(this), message); return message; diff --git a/src/commands/whoami.ts b/src/commands/whoami.ts index 7456fded..61585ca8 100644 --- a/src/commands/whoami.ts +++ b/src/commands/whoami.ts @@ -1,6 +1,5 @@ import { Command } from '@oclif/core'; import { CLIUtils } from '../utils/cli.utils'; -import { ErrorUtils } from '../utils/errors.utils'; import { ConfigService } from '../services/config.service'; import { ValidationService } from '../services/validation.service'; import { LoginCredentials } from '../types/command.types'; @@ -35,8 +34,14 @@ export default class Whoami extends Command { }; public catch = async (error: Error) => { - ErrorUtils.report(this.error.bind(this), error, { command: this.id }); - CLIUtils.error(this.log.bind(this), error.message); + const { flags } = await this.parse(Whoami); + CLIUtils.catchError({ + error, + command: this.id, + logReporter: this.log.bind(this), + errorReporter: this.error.bind(this), + jsonFlag: flags['json'], + }); this.exit(1); }; From 8964dceb0fdba0f6a14da9953f9214c0493fca68 Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Thu, 8 May 2025 17:03:17 +0200 Subject: [PATCH 4/5] chore: update version to 1.5.3 --- README.md | 50 +++++++++++++++++++++++++------------------------- package.json | 2 +- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 8acf44b1..8f3ae950 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ $ npm install -g @internxt/cli $ internxt COMMAND running command... $ internxt (--version) -@internxt/cli/1.5.2 win32-x64 node-v23.7.0 +@internxt/cli/1.5.3 win32-x64 node-v23.7.0 $ internxt --help [COMMAND] USAGE $ internxt COMMAND @@ -99,7 +99,7 @@ EXAMPLES $ internxt add-cert ``` -_See code: [src/commands/add-cert.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/add-cert.ts)_ +_See code: [src/commands/add-cert.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/add-cert.ts)_ ## `internxt config` @@ -119,7 +119,7 @@ EXAMPLES $ internxt config ``` -_See code: [src/commands/config.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/config.ts)_ +_See code: [src/commands/config.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/config.ts)_ ## `internxt create-folder` @@ -148,7 +148,7 @@ EXAMPLES $ internxt create-folder ``` -_See code: [src/commands/create-folder.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/create-folder.ts)_ +_See code: [src/commands/create-folder.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/create-folder.ts)_ ## `internxt delete-permanently-file` @@ -178,7 +178,7 @@ EXAMPLES $ internxt delete-permanently-file ``` -_See code: [src/commands/delete-permanently-file.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/delete-permanently-file.ts)_ +_See code: [src/commands/delete-permanently-file.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/delete-permanently-file.ts)_ ## `internxt delete-permanently-folder` @@ -208,7 +208,7 @@ EXAMPLES $ internxt delete-permanently-folder ``` -_See code: [src/commands/delete-permanently-folder.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/delete-permanently-folder.ts)_ +_See code: [src/commands/delete-permanently-folder.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/delete-permanently-folder.ts)_ ## `internxt delete permanently file` @@ -297,7 +297,7 @@ EXAMPLES $ internxt download-file ``` -_See code: [src/commands/download-file.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/download-file.ts)_ +_See code: [src/commands/download-file.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/download-file.ts)_ ## `internxt download file` @@ -356,7 +356,7 @@ EXAMPLES $ internxt list ``` -_See code: [src/commands/list.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/list.ts)_ +_See code: [src/commands/list.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/list.ts)_ ## `internxt login` @@ -385,7 +385,7 @@ EXAMPLES $ internxt login ``` -_See code: [src/commands/login.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/login.ts)_ +_See code: [src/commands/login.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/login.ts)_ ## `internxt logout` @@ -405,7 +405,7 @@ EXAMPLES $ internxt logout ``` -_See code: [src/commands/logout.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/logout.ts)_ +_See code: [src/commands/logout.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/logout.ts)_ ## `internxt logs` @@ -425,7 +425,7 @@ EXAMPLES $ internxt logs ``` -_See code: [src/commands/logs.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/logs.ts)_ +_See code: [src/commands/logs.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/logs.ts)_ ## `internxt move-file` @@ -457,7 +457,7 @@ EXAMPLES $ internxt move-file ``` -_See code: [src/commands/move-file.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/move-file.ts)_ +_See code: [src/commands/move-file.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/move-file.ts)_ ## `internxt move-folder` @@ -489,7 +489,7 @@ EXAMPLES $ internxt move-folder ``` -_See code: [src/commands/move-folder.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/move-folder.ts)_ +_See code: [src/commands/move-folder.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/move-folder.ts)_ ## `internxt move file` @@ -580,7 +580,7 @@ EXAMPLES $ internxt rename-file ``` -_See code: [src/commands/rename-file.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/rename-file.ts)_ +_See code: [src/commands/rename-file.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/rename-file.ts)_ ## `internxt rename-folder` @@ -611,7 +611,7 @@ EXAMPLES $ internxt rename-folder ``` -_See code: [src/commands/rename-folder.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/rename-folder.ts)_ +_See code: [src/commands/rename-folder.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/rename-folder.ts)_ ## `internxt rename file` @@ -699,7 +699,7 @@ EXAMPLES $ internxt trash-clear ``` -_See code: [src/commands/trash-clear.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/trash-clear.ts)_ +_See code: [src/commands/trash-clear.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/trash-clear.ts)_ ## `internxt trash-file` @@ -729,7 +729,7 @@ EXAMPLES $ internxt trash-file ``` -_See code: [src/commands/trash-file.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/trash-file.ts)_ +_See code: [src/commands/trash-file.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/trash-file.ts)_ ## `internxt trash-folder` @@ -759,7 +759,7 @@ EXAMPLES $ internxt trash-folder ``` -_See code: [src/commands/trash-folder.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/trash-folder.ts)_ +_See code: [src/commands/trash-folder.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/trash-folder.ts)_ ## `internxt trash-list` @@ -785,7 +785,7 @@ EXAMPLES $ internxt trash-list ``` -_See code: [src/commands/trash-list.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/trash-list.ts)_ +_See code: [src/commands/trash-list.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/trash-list.ts)_ ## `internxt trash-restore-file` @@ -816,7 +816,7 @@ EXAMPLES $ internxt trash-restore-file ``` -_See code: [src/commands/trash-restore-file.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/trash-restore-file.ts)_ +_See code: [src/commands/trash-restore-file.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/trash-restore-file.ts)_ ## `internxt trash-restore-folder` @@ -847,7 +847,7 @@ EXAMPLES $ internxt trash-restore-folder ``` -_See code: [src/commands/trash-restore-folder.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/trash-restore-folder.ts)_ +_See code: [src/commands/trash-restore-folder.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/trash-restore-folder.ts)_ ## `internxt trash clear` @@ -1044,7 +1044,7 @@ EXAMPLES $ internxt upload-file ``` -_See code: [src/commands/upload-file.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/upload-file.ts)_ +_See code: [src/commands/upload-file.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/upload-file.ts)_ ## `internxt upload file` @@ -1099,7 +1099,7 @@ EXAMPLES $ internxt webdav status ``` -_See code: [src/commands/webdav.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/webdav.ts)_ +_See code: [src/commands/webdav.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/webdav.ts)_ ## `internxt webdav-config` @@ -1125,7 +1125,7 @@ EXAMPLES $ internxt webdav-config ``` -_See code: [src/commands/webdav-config.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/webdav-config.ts)_ +_See code: [src/commands/webdav-config.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/webdav-config.ts)_ ## `internxt whoami` @@ -1145,7 +1145,7 @@ EXAMPLES $ internxt whoami ``` -_See code: [src/commands/whoami.ts](https://github.com/internxt/cli/blob/v1.5.2/src/commands/whoami.ts)_ +_See code: [src/commands/whoami.ts](https://github.com/internxt/cli/blob/v1.5.3/src/commands/whoami.ts)_ # Current Limitations diff --git a/package.json b/package.json index e6b1bc80..6841bb72 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "author": "Internxt ", - "version": "1.5.2", + "version": "1.5.3", "description": "Internxt CLI to manage your encrypted storage", "scripts": { "build": "yarn clean && tsc", From 3dbf69cefb23eb0492f540119d17bd6a5f0623e3 Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Thu, 8 May 2025 17:46:12 +0200 Subject: [PATCH 5/5] test: add unit tests for CLIUtils --- test/utils/cli.utils.test.ts | 148 +++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 test/utils/cli.utils.test.ts diff --git a/test/utils/cli.utils.test.ts b/test/utils/cli.utils.test.ts new file mode 100644 index 00000000..6f73d2da --- /dev/null +++ b/test/utils/cli.utils.test.ts @@ -0,0 +1,148 @@ +import { describe, it, expect, vi, beforeEach, afterEach, MockInstance } from 'vitest'; +import { ux } from '@oclif/core'; +import { CLIUtils } from '../../src/utils/cli.utils'; +import { Direction } from 'node:readline'; +import { Options } from '@oclif/core/lib/ux/action/types'; + +vi.mock('ux', () => { + return { + action: { + start: vi.fn(), + stop: vi.fn(), + running: false, + }, + colorize: vi.fn((color: string, text: string) => text), + }; +}); + +describe('CliUtils', () => { + let stdoutWrite: MockInstance<{ + (buffer: Uint8Array | string, cb?: (err?: Error) => void): boolean; + (str: Uint8Array | string, encoding?: BufferEncoding, cb?: (err?: Error) => void): boolean; + }>; + let stdoutClear: MockInstance<(dir: Direction, callback?: () => void) => boolean>; + let reporter: (message: string) => void; + + beforeEach(() => { + vi.clearAllMocks(); + process.stdout.write = vi.fn(); + process.stdout.clearLine = vi.fn(); + stdoutWrite = vi.spyOn(process.stdout, 'write').mockImplementation(() => true); + stdoutClear = vi.spyOn(process.stdout, 'clearLine').mockImplementation(() => true); + + reporter = vi.fn(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe('clearPreviousLine', () => { + it('should move cursor up and clear line when jsonFlag is false', () => { + CLIUtils.clearPreviousLine(false); + expect(stdoutWrite).toHaveBeenCalledWith('\x1b[1A'); + expect(stdoutClear).toHaveBeenCalledWith(0); + }); + + it('should not call stdout methods when jsonFlag is true', () => { + CLIUtils.clearPreviousLine(true); + expect(stdoutWrite).not.toHaveBeenCalled(); + expect(stdoutClear).not.toHaveBeenCalled(); + }); + + it('should not throw when no flags provided', () => { + expect(() => CLIUtils.clearPreviousLine()).not.toThrow(); + expect(stdoutWrite).toHaveBeenCalled(); + }); + }); + + describe('warning, error, success, log', () => { + it('warning should call reporter with colored warning', () => { + vi.spyOn(ux, 'colorize').mockImplementation(vi.fn((color: string | undefined, text: string) => text)); + CLIUtils.warning(reporter, 'Test'); + expect(ux.colorize).toHaveBeenCalledWith('#a67805', '⚠ Warning: Test'); + expect(reporter).toHaveBeenCalledWith('⚠ Warning: Test'); + }); + + it('error should call reporter with colored error', () => { + vi.spyOn(ux, 'colorize').mockImplementation(vi.fn((color: string | undefined, text: string) => text)); + CLIUtils.error(reporter, 'Test'); + expect(ux.colorize).toHaveBeenCalledWith('red', '⚠ Error: Test'); + expect(reporter).toHaveBeenCalledWith('⚠ Error: Test'); + }); + + it('success should call reporter with colored success', () => { + vi.spyOn(ux, 'colorize').mockImplementation(vi.fn((color: string | undefined, text: string) => text)); + CLIUtils.success(reporter, 'Test'); + expect(ux.colorize).toHaveBeenCalledWith('green', '✓ Test'); + expect(reporter).toHaveBeenCalledWith('✓ Test'); + }); + + it('log should call reporter with message', () => { + vi.spyOn(ux, 'colorize').mockImplementation(vi.fn((color: string | undefined, text: string) => text)); + CLIUtils.log(reporter, 'Hello'); + expect(reporter).toHaveBeenCalledWith('Hello'); + }); + }); + + describe('consoleLog', () => { + it('consoleLog should print to console', () => { + const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); + CLIUtils.consoleLog('Hi'); + expect(consoleSpy).toHaveBeenCalledWith('Hi'); + consoleSpy.mockRestore(); + }); + }); + + describe('doing, done, failed', () => { + it('doing should start ux action when jsonFlag is false', () => { + vi.spyOn(ux.action, 'start').mockImplementation( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + vi.fn((action: string, status?: string, opts?: Options) => {}), + ); + CLIUtils.doing('Working', false); + expect(ux.action.start).toHaveBeenCalledWith('Working', undefined, {}); + }); + + it('doing should not call when jsonFlag is true', () => { + vi.spyOn(ux.action, 'start').mockImplementation( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + vi.fn((action: string, status?: string, opts?: Options) => {}), + ); + CLIUtils.doing('Anything', true); + expect(ux.action.start).not.toHaveBeenCalled(); + }); + + it('done should stop ux action with colored done when running', () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + vi.spyOn(ux.action, 'stop').mockImplementation(vi.fn((msg?: string) => {})); + vi.spyOn(ux.action, 'running', 'get').mockReturnValue(true); + CLIUtils.done(false); + expect(ux.action.stop).toHaveBeenCalledWith('done ✓'); + }); + + it('done should not stop action when jsonFlag is true', () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + vi.spyOn(ux.action, 'stop').mockImplementation(vi.fn((msg?: string) => {})); + vi.spyOn(ux.action, 'running', 'get').mockReturnValue(true); + CLIUtils.done(true); + expect(ux.action.stop).not.toHaveBeenCalled(); + }); + + it('failed should stop ux action with colored failed when running', () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + vi.spyOn(ux.action, 'stop').mockImplementation(vi.fn((msg?: string) => {})); + vi.spyOn(ux.action, 'running', 'get').mockReturnValue(true); + CLIUtils.failed(false); + expect(ux.action.stop).toHaveBeenCalledWith('failed ✕'); + }); + + it('failed should not stop when jsonFlag is true', () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + vi.spyOn(ux.action, 'stop').mockImplementation(vi.fn((msg?: string) => {})); + vi.spyOn(ux.action, 'running', 'get').mockReturnValue(true); + CLIUtils.failed(true); + expect(ux.action.stop).not.toHaveBeenCalled(); + }); + }); +});