diff --git a/src/baseCommand.ts b/src/baseCommand.ts index 7010467..a419110 100644 --- a/src/baseCommand.ts +++ b/src/baseCommand.ts @@ -1,10 +1,9 @@ -import { color } from "@oclif/color"; import { Command, flags as oFlags } from "@oclif/command"; import { Client, configManager } from "@uns/crypto"; import { cli } from "cli-ux"; -import util from "util"; import { UNSCLIAPI } from "./api"; -import { CommandOutput, Formater, getFormatFlag, NestedCommandOutput, OUTPUT_FORMAT } from "./formater"; +import { Formater, getFormatFlag, OUTPUT_FORMAT } from "./formater"; +import * as LOGGER from "./logger"; import * as UTILS from "./utils"; export abstract class BaseCommand extends Command { @@ -42,8 +41,10 @@ export abstract class BaseCommand extends Command { this.formater = OUTPUT_FORMAT[flags.format]; this.verbose = flags.verbose; - if (!this.verbose) { - this.disableLogs(); + if (this.verbose) { + LOGGER.bindConsole(); + } else { + LOGGER.disableLogs(this); } /** @@ -70,29 +71,47 @@ export abstract class BaseCommand extends Command { this.client = new Client(networkPreset); + if (UTILS.isDevMode()) { + this.info("DEV MODE IS ACTIVATED"); + } try { - if (UTILS.isDevMode()) { - this.info("DEV MODE IS ACTIVATED"); - } this.info(`node: ${this.api.getCurrentNode()}`); const commandResult = await this.do(flags, args); - if (commandResult && Object.keys(commandResult).length > 0) { - // Keep super.log to force log - super.log(this.formater && this.formater.action ? this.formater.action(commandResult) : commandResult); + if (commandResult && typeof commandResult === "string") { + super.log(commandResult); + } else { + if (commandResult && Object.keys(commandResult).length > 0) { + // Keep super.log to force log + super.log( + this.formater && this.formater.action ? this.formater.action(commandResult) : commandResult, + ); + } } } catch (globalCatchException) { - this.promptErrAndExit(globalCatchException.message); + this.stop(globalCatchException.message); + this.exit(1); } } public info(message: string, ...args: any[]): void { if (this.verbose || this._helpOverride()) { - message = typeof message === "string" ? message : util.inspect(message); - const info = color.yellowBright(`:info: ${util.format(message, ...args)}\n`); - process.stdout.write(info); + LOGGER.logWithLevel("info", message, ...args); } } + public warn(message: string, ...args: any[]): void { + LOGGER.logWithLevel("warn", message, ...args); + } + + /** + * Always log and exit + * @param message + * @param args + */ + public stop(message: string, ...args: any[]): void { + LOGGER.logWithLevel("stop", message, ...args); + } + protected getAvailableFormats(): Formater[] { return [OUTPUT_FORMAT.json]; } @@ -100,12 +119,8 @@ export abstract class BaseCommand extends Command { protected getDefaultFormat(): Formater { return OUTPUT_FORMAT.json; } - protected abstract do( - flags: Record, - args?: Record, - ): Promise | Promise; + protected abstract do(flags: Record, args?: Record): Promise; protected abstract getCommand(): typeof BaseCommand; - protected abstract getCommandTechnicalName(): string; protected logAttribute(label: string, value: string) { this.log(`\t${label}: ${value}`); @@ -162,22 +177,4 @@ export abstract class BaseCommand extends Command { } return transactionFromNetwork; } - - /** - * - * @param errorMsg Prompt error and exit command. - */ - private promptErrAndExit(errorMsg: string): void { - this.error(`[${this.getCommandTechnicalName()}] ${errorMsg}`); - this.exit(1); - } - - private disableLogs(): void { - const disableFunction = (...args) => { - /*doNothing*/ - }; - ["debug", "log", "info", "warn"].forEach(level => (console[level] = disableFunction)); - ["log", "warn", "info"].forEach(level => (this[level] = disableFunction)); - // Do not override console.error and this.error - } } diff --git a/src/commands/create-unik.ts b/src/commands/create-unik.ts index 3389f9e..84e842f 100644 --- a/src/commands/create-unik.ts +++ b/src/commands/create-unik.ts @@ -41,10 +41,6 @@ export class CreateUnikCommand extends WriteCommand { return CreateUnikCommand; } - protected getCommandTechnicalName(): string { - return "create-unik"; - } - protected async do(flags: Record): Promise { if (flags.explicitValue.length > EXPLICIT_VALUE_MAX_LENGTH) { throw new Error( diff --git a/src/commands/create-wallet.ts b/src/commands/create-wallet.ts index d239560..2dcf346 100644 --- a/src/commands/create-wallet.ts +++ b/src/commands/create-wallet.ts @@ -1,5 +1,3 @@ -import { color } from "@oclif/color"; -import Command from "@oclif/command"; import { crypto } from "@uns/crypto"; import { generateMnemonic } from "bip39"; import { createHash, randomBytes } from "crypto"; @@ -28,10 +26,6 @@ export class CreateWalletCommand extends BaseCommand { return CreateWalletCommand; } - protected getCommandTechnicalName(): string { - return "create-wallet"; - } - protected async do(flags: Record): Promise { const passphrase = await this.randomMnemonicSeed(128); const keys = crypto.getKeys(passphrase); @@ -45,12 +39,7 @@ export class CreateWalletCommand extends BaseCommand { }; // Do not use this.error. It throws error and close. {exit: 0} option closes too. - console.error( - color.red( - "\n⚠️ WARNING: this information is not saved anywhere. You need to copy and save it by your own. ⚠️\n", - ), - ); - + this.warn("This information is not saved anywhere. You need to copy and save it by your own."); return wallet; } diff --git a/src/commands/did-resolve.ts b/src/commands/did-resolve.ts index 4f64f8e..1fc0f64 100644 --- a/src/commands/did-resolve.ts +++ b/src/commands/did-resolve.ts @@ -1,7 +1,7 @@ import { DidParserError, didResolve, ResourceWithChainMeta, UnikToken } from "@uns/ts-sdk"; import flatten from "flat"; import { BaseCommand } from "../baseCommand"; -import { CommandOutput, Formater, NestedCommandOutput, OUTPUT_FORMAT } from "../formater"; +import { Formater, OUTPUT_FORMAT } from "../formater"; import { confirmedFlag, getNetworksListListForDescription } from "../utils"; export class DidResolveCommand extends BaseCommand { @@ -37,39 +37,40 @@ export class DidResolveCommand extends BaseCommand { return DidResolveCommand; } - protected getCommandTechnicalName(): string { - return "did-resolve"; - } - - protected async do( - flags: Record, - args?: Record, - ): Promise { + protected async do(flags: Record, args?: Record): Promise { const didResolveNetwork = flags.network === "local" ? "TESTNET" : flags.network; - const resolved: ResourceWithChainMeta | DidParserError = await didResolve( - args.did, - didResolveNetwork, - ); - - if (resolved instanceof Error) { - throw resolved; + let resolved: ResourceWithChainMeta | DidParserError; + try { + resolved = await didResolve(args.did, didResolveNetwork); + } catch (error) { + if (error.response.status === 404) { + this.stop("DID does not exist"); + } else { + this.stop("An error occurred. Please see details below:\n", error); + } } - if (resolved.confirmations && resolved.confirmations < flags.confirmed) { - this.warn("DID has not reach the requested confirmation level."); - return undefined; - } else { - delete resolved.chainmeta; - delete resolved.confirmations; + if (resolved) { + if (resolved instanceof Error) { + // DidParserError + this.stop("DID does not match expected format"); + } else { + if (resolved.confirmations && resolved.confirmations < flags.confirmed) { + this.warn("DID has not reach the requested confirmation level."); + } else { + delete resolved.chainmeta; + delete resolved.confirmations; - if (flags.format === OUTPUT_FORMAT.raw.key && resolved.data instanceof Object) { - const flattenResult = flatten(resolved.data); - this.log("", flattenResult); - return flattenResult; + if (flags.format === OUTPUT_FORMAT.raw.key && resolved.data instanceof Object) { + const flattenResult = flatten(resolved.data); + this.log("", flattenResult); + return flattenResult; + } + return resolved; + } } - - return resolved; } + return "DID not resolved"; } } diff --git a/src/commands/get-properties.ts b/src/commands/get-properties.ts index a8b07eb..3e276cf 100644 --- a/src/commands/get-properties.ts +++ b/src/commands/get-properties.ts @@ -1,4 +1,3 @@ -import { flags } from "@oclif/command"; import { BaseCommand } from "../baseCommand"; import { CommandOutput, Formater, NestedCommandOutput, OUTPUT_FORMAT } from "../formater"; import { confirmedFlag, getNetworksListListForDescription, unikidFlag } from "../utils"; @@ -25,10 +24,6 @@ export class GetPropertiesCommand extends BaseCommand { return GetPropertiesCommand; } - protected getCommandTechnicalName(): string { - return "get-properties"; - } - protected async do(flags: Record): Promise { const unik = await this.api.getUnikById(flags.unikid); const lastTransaction = await this.api.getTransaction(unik.transactions.last.id); diff --git a/src/commands/read-unik.ts b/src/commands/read-unik.ts index 0ca913b..7fdba95 100644 --- a/src/commands/read-unik.ts +++ b/src/commands/read-unik.ts @@ -24,10 +24,6 @@ export class ReadUnikCommand extends ReadCommand { return ReadUnikCommand; } - protected getCommandTechnicalName(): string { - return "read-unik"; - } - protected async do(flags: Record): Promise { checkUnikIdFormat(flags.unikid); diff --git a/src/commands/read-wallet.ts b/src/commands/read-wallet.ts index 5f6cf4d..084d90e 100644 --- a/src/commands/read-wallet.ts +++ b/src/commands/read-wallet.ts @@ -32,10 +32,6 @@ export class ReadWalletCommand extends ReadCommand { return ReadWalletCommand; } - protected getCommandTechnicalName(): string { - return "read-wallet"; - } - protected async do(flags: Record, args?: Record): Promise { const walletId = args.walletId; const wallet: any = await this.api.getWallet(walletId); diff --git a/src/commands/set-properties.ts b/src/commands/set-properties.ts index 1831faa..2f7930e 100644 --- a/src/commands/set-properties.ts +++ b/src/commands/set-properties.ts @@ -32,10 +32,6 @@ export class SetPropertiesCommand extends UpdatePropertiesCommand { return SetPropertiesCommand; } - protected getCommandTechnicalName(): string { - return "set-properties"; - } - protected getProperties(flags: Record): { [_: string]: string } { const properties: { [_: string]: string } = {}; for (const prop of flags.properties) { diff --git a/src/commands/status.ts b/src/commands/status.ts index 92bf98c..ecd39b7 100644 --- a/src/commands/status.ts +++ b/src/commands/status.ts @@ -19,10 +19,6 @@ export class StatusCommand extends BaseCommand { return StatusCommand; } - protected getCommandTechnicalName(): string { - return "status"; - } - protected async do(flags: Record): Promise { const unsSupply: any = await this.api.getSupply(); diff --git a/src/commands/unset-properties.ts b/src/commands/unset-properties.ts index 175c309..acb0e96 100644 --- a/src/commands/unset-properties.ts +++ b/src/commands/unset-properties.ts @@ -30,10 +30,6 @@ export class UnsetProperties extends UpdateProperties { return UnsetProperties; } - protected getCommandTechnicalName(): string { - return "set-properties"; - } - protected getProperties(flags: Record): { [_: string]: string } { const properties: { [_: string]: string } = {}; diff --git a/src/logger.ts b/src/logger.ts new file mode 100644 index 0000000..62b4592 --- /dev/null +++ b/src/logger.ts @@ -0,0 +1,56 @@ +import { color } from "@oclif/color"; +import Command from "@oclif/command"; +import util from "util"; + +const LOGGER_COLOR_BY_LEVEL: any = { + stop: { + color: "#ff0000", + output: "stderr", + }, + warn: { + color: "#ffa407", + output: "stderr", + }, + info: { + color: "#6ecbfb", + output: "stdout", + }, + debug: { + color: "#fbfa6e", + output: "stdout", + }, +}; + +export const logWithLevel = (level: string, message: string, ...args: any[]): void => { + message = typeof message === "string" ? message : util.inspect(message); + const loggerConfig = LOGGER_COLOR_BY_LEVEL[level]; + const log = color.hex(loggerConfig.color)(`» :${level}: ${util.format(message, ...args)};\n`); + process[loggerConfig.output].write(log); +}; + +export const bindConsole = (): void => { + console.info = (message?: any, ...optionalParams: any[]) => { + logWithLevel("info", message, ...optionalParams); + }; + + console.warn = (message?: any, ...optionalParams: any[]) => { + logWithLevel("warn", message, ...optionalParams); + }; + + console.error = (message?: any, ...optionalParams: any[]) => { + logWithLevel("stop", message, ...optionalParams); + }; + + console.debug = (message?: any, ...optionalParams: any[]) => { + logWithLevel("debug", message, ...optionalParams); + }; +}; + +export const disableLogs = (command: Command): void => { + const disableFunction = (...args) => { + /*doNothing*/ + }; + ["debug", "log", "info"].forEach(level => (console[level] = disableFunction)); + ["log", "info"].forEach(level => (command[level] = disableFunction)); + // Do not override console.error, command.error, command.warn and command.stop +}; diff --git a/test/__fixtures__/commands/create-unik.ts b/test/__fixtures__/commands/create-unik.ts index 24f2602..2960389 100644 --- a/test/__fixtures__/commands/create-unik.ts +++ b/test/__fixtures__/commands/create-unik.ts @@ -40,7 +40,7 @@ export const meta = { }, }; -const verboseOutput = `:info: node: https://forger1.devnet.uns.network +const verboseOutput = `» :info: node: https://forger1.devnet.uns.network; unikid: ${UNIK_ID} Transaction id: ${TRANSACTION_ID} Transaction in explorer: https://explorer.devnet.uns.network/transaction/${TRANSACTION_ID} diff --git a/test/__fixtures__/commands/status.ts b/test/__fixtures__/commands/status.ts index e366b3d..6b369f9 100644 --- a/test/__fixtures__/commands/status.ts +++ b/test/__fixtures__/commands/status.ts @@ -21,7 +21,7 @@ lastBlockUrl: https://explorer.devnet.uns.network/block/100015 const statusResultTable = `height;network;totalTokenSupply;tokenSymbol;numberOfUniks;activeDelegates;lastBlockUrl 100015;devnet;21199994;DUNS;1;7;https://explorer.devnet.uns.network/block/100015 `; -const infoNode = ":info: node: https://forger1.devnet.uns.network\n"; +const infoNode = "» :info: node: https://forger1.devnet.uns.network;\n"; export const outputCases = [ {