diff --git a/bin/cli.js b/bin/cli.js index 022ae67..e9a1a70 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -13,15 +13,19 @@ const devMode = require("../lib/cli/devMode"); const { firmCredentials } = require("../lib/api/firmCredentials"); const SF = require("../lib/api/sfApi"); const path = require("path"); +const { consola } = require("consola"); let firmIdDefault = cliUtils.loadDefaultFirmId(); cliUtils.handleUncaughtErrors(); // Name & Version program.name("silverfin"); -if (pkg.version) { - program.version(pkg.version); -} +pkg.version ? program.version(pkg.version) : undefined; +// Verbose Option +program.option("-v, --verbose", "Verbose output"); +program.on("option:verbose", () => { + consola.level = "debug"; // default: "info" +}); // READ reconciliations program @@ -482,21 +486,21 @@ program if (options.setFirm) { firmCredentials.setDefaultFirmId(options.setFirm); const currentDirectory = path.basename(process.cwd()); - console.log(`${currentDirectory}: firm id set to ${options.setFirm}`); + consola.success(`${currentDirectory}: firm id set to ${options.setFirm}`); } if (options.getFirm) { const storedFirmId = firmCredentials.getDefaultFirmId(); if (storedFirmId) { - console.log(`Firm id previously stored: ${storedFirmId}`); + consola.info(`Firm id previously stored: ${storedFirmId}`); } else { - console.log("There is no firm id previously stored"); + consola.info("There is no firm id previously stored"); } } if (options.listAll) { const ids = firmCredentials.listStoredIds() || []; if (ids) { - console.log("List of authorized firms"); - ids.forEach((element) => console.log("- " + element)); + const bullets = "\n - " + ids.join("\n - "); + consola.info("List of authorized firms:", bullets); } } }); @@ -637,5 +641,5 @@ if (pkg.repository && pkg.repository.url) { (async function () { // Check if there is a new version available await cliUpdates.checkVersions(); - program.parse(); + await program.parseAsync(); })(); diff --git a/index.js b/index.js index f6ec25d..7063947 100644 --- a/index.js +++ b/index.js @@ -6,13 +6,16 @@ const errorUtils = require("./lib/utils/errorUtils"); const { ReconciliationText } = require("./lib/reconciliationText"); const { SharedPart } = require("./lib/sharedPart"); const { ExportFile } = require("./lib/exportFile"); +const { consola } = require("consola"); async function fetchReconciliationByHandle(firmId, handle) { const template = await SF.findReconciliationTextByHandle(firmId, handle); if (!template) { - throw `Reconciliation ${handle} wasn't found`; + consola.error(`Reconciliation "${handle}" wasn't found`); + process.exit(1); } ReconciliationText.save(firmId, template); + consola.success(`Reconciliation "${handle}" imported`); } async function fetchReconciliationById(firmId, id) { @@ -29,7 +32,7 @@ async function fetchAllReconciliations(firmId, page = 1) { const templates = await SF.readReconciliationTexts(firmId, page); if (templates.length == 0) { if (page == 1) { - console.log("No reconciliations found"); + consola.error(`No reconciliations found in firm ${firmId}`); } return; } @@ -47,19 +50,28 @@ async function publishReconciliationByHandle( try { const templateConfig = fsUtils.readConfig("reconciliationText", handle); if (!templateConfig || !templateConfig.id[firmId]) { - console.log(`Reconciliation ${handle}: ID is missing. Aborted`); - console.log( - `Try running: ${chalk.bold( - `silverfin get-reconciliation-id --handle ${handle}` - )} or ${chalk.bold(`silverfin get-reconciliation-id --all`)}` + const updatedId = await getTemplateId( + firmId, + "reconciliationText", + handle ); - process.exit(1); + if (!updatedId) { + consola.error(`Reconciliation ${handle}: ID is missing. Aborted`); + process.exit(1); + } } let templateId = templateConfig.id[firmId]; - console.log(`Updating ${handle}...`); + consola.debug(`Updating ${handle}...`); const template = await ReconciliationText.read(handle); template.version_comment = message; - await SF.updateReconciliationText(firmId, templateId, template); + const response = await SF.updateReconciliationText( + firmId, + templateId, + template + ); + if (response.status == 201) { + consola.success(`Reconciliation "${handle}" updated`); + } } catch (error) { errorUtils.errorHandler(error); } @@ -87,8 +99,8 @@ async function newReconciliation( handle ); if (existingTemplate) { - console.log( - `Reconciliation ${handle} already exists. Skipping its creation` + consola.info( + `Reconciliation "${handle}" already exists. Skipping its creation` ); return; } @@ -99,6 +111,7 @@ async function newReconciliation( // Store new id if (response && response.status == 201) { ReconciliationText.updateTemplateId(firmId, handle, response.data.id); + consola.success(`Reconciliation "${handle}" created`); } } catch (error) { errorUtils.errorHandler(error); @@ -118,24 +131,28 @@ async function newAllReconciliations( async function fetchExportFileByHandle(firmId, name) { const template = await SF.findExportFileByName(firmId, name); if (!template) { - throw `Export file ${name} wasn't found`; + consola.error(`Export file "${name}" wasn't found`); + process.exit(1); } ExportFile.save(firmId, template); + consola.success(`Export file "${name}" imported`); } async function fetchExportFileById(firmId, id) { const template = await SF.readExportFileById(firmId, id); if (!template) { - throw `Export file with id ${id} wasn't found`; + consola.error(`Export file with id ${id} wasn't found`); + process.exit(1); } ExportFile.save(firmId, template); + consola.success(`Export file "${template.name}" imported`); } async function fetchAllExportFiles(firmId, page = 1) { const templates = await SF.readExportFiles(firmId, page); if (templates.length == 0) { if (page == 1) { - console.log("No export files found"); + console.error(`No export files found in firm ${firmId}`); } return; } @@ -153,14 +170,20 @@ async function publishExportFileByName( try { const templateConfig = fsUtils.readConfig("exportFile", name); if (!templateConfig || !templateConfig.id[firmId]) { - console.log(`Export file ${name}: ID is missing. Aborted`); - process.exit(1); + const updatedId = await getTemplateId(firmId, "exportfile", handle); + if (!updatedId) { + consola.error(`Export file ${name}: ID is missing. Aborted`); + process.exit(1); + } } let templateId = templateConfig.id[firmId]; - console.log(`Updating ${name}...`); + consola.debug(`Updating ${name}...`); const template = await ExportFile.read(name); template.version_comment = message; - await SF.updateExportFile(firmId, templateId, template); + const response = await SF.updateExportFile(firmId, templateId, template); + if (response.status == 201) { + consola.success(`Export file "${name}" updated`); + } } catch (error) { errorUtils.errorHandler(error); } @@ -185,8 +208,8 @@ async function newExportFile( try { const existingTemplate = await SF.findExportFileByName(firmId, name); if (existingTemplate) { - console.log( - `Reconciliation ${name} already exists. Skipping its creation` + consola.info( + `Export file "${name}" already exists. Skipping its creation` ); return; } @@ -197,6 +220,7 @@ async function newExportFile( // Store new id if (response && response.status == 201) { ExportFile.updateTemplateId(firmId, name, response.data.id); + consola.success(`Export file "${name}" created`); } } catch (error) { errorUtils.errorHandler(error); @@ -222,15 +246,17 @@ async function importExistingSharedPartById(firmId, id) { const sharedPart = await SF.readSharedPartById(firmId, id); if (!sharedPart) { - throw `Shared part ${id} wasn't found.`; + consola.error(`Shared part ${id} wasn't found.`); + process.exit(1); } await SharedPart.save(firmId, sharedPart.data); + consola.success(`Shared part "${sharedPart.data.name}" imported`); } async function fetchSharedPartByName(firmId, name) { const sharedPartByName = await SF.findSharedPartByName(firmId, name); if (!sharedPartByName) { - throw `Shared part with name ${name} wasn't found.`; + consola.error(`Shared part "${name}" wasn't found.`); } return importExistingSharedPartById(firmId, sharedPartByName.id); } @@ -240,7 +266,7 @@ async function fetchAllSharedParts(firmId, page = 1) { const sharedParts = response.data; if (sharedParts.length == 0) { if (page == 1) { - console.log(`No shared parts found`); + consola.error(`No shared parts found in firm ${firmId}`); } return; } @@ -258,18 +284,23 @@ async function publishSharedPartByName( try { const templateConfig = fsUtils.readConfig("sharedPart", name); if (!templateConfig || !templateConfig.id[firmId]) { - console.log(`Shared part ${name}: ID is missing. Aborted`); - console.log( - `Try running: ${chalk.bold( - `silverfin get-shared-part-id --shared-part ${name}` - )} or ${chalk.bold(`silverfin get-shared-part-id --all`)}` - ); - process.exit(1); + const updatedId = await getTemplateId(firmId, "sharedPart", handle); + if (!updatedId) { + consola.error(`Shared part ${name}: ID is missing. Aborted`); + process.exit(1); + } } - console.log(`Updating shared part ${name}...`); + consola.debug(`Updating shared part ${name}...`); const template = await SharedPart.read(name); template.version_comment = message; - await SF.updateSharedPart(firmId, templateConfig.id[firmId], template); + const response = await SF.updateSharedPart( + firmId, + templateConfig.id[firmId], + template + ); + if (response.status == 201) { + consola.success(`Shared part "${name}" updated`); + } } catch (error) { errorUtils.errorHandler(error); } @@ -294,7 +325,9 @@ async function newSharedPart( try { const existingSharedPart = await SF.findSharedPartByName(firmId, name); if (existingSharedPart) { - console.log(`Shared part ${name} already exists. Skipping its creation`); + consola.info( + `Shared part "${name}" already exists. Skipping its creation` + ); return; } const template = await SharedPart.read(name); @@ -304,6 +337,7 @@ async function newSharedPart( // Store new firm id if (response && response.status == 201) { SharedPart.updateTemplateId(firmId, name, response.data.id); + consola.success(`Shared part "${name}" created`); } } catch (error) { errorUtils.errorHandler(error); @@ -347,7 +381,7 @@ async function addSharedPartToReconciliation( reconciliationHandle ); if (!updated) { - console.error(`Reconciliation ${reconciliationHandle}: ID not found.`); + consola.error(`Reconciliation ${reconciliationHandle}: ID not found.`); return false; } configReconciliation = await fsUtils.readConfig( @@ -364,7 +398,7 @@ async function addSharedPartToReconciliation( sharedPartHandle ); if (!updated) { - console.error(`Shared part ${sharedPartHandle}: ID not found.`); + consola.error(`Shared part ${sharedPartHandle}: ID not found.`); return false; } configSharedPart = await fsUtils.readConfig( @@ -381,12 +415,12 @@ async function addSharedPartToReconciliation( ); if (!response || !response.status || !response.status === 201) { - console.log( + consola.warn( `Adding shared part "${sharedPartHandle}" to "${reconciliationHandle}" reconciliation text failed.` ); return false; } - console.log( + consola.success( `Shared part "${sharedPartHandle}" added to "${reconciliationHandle}" reconciliation text.` ); @@ -442,11 +476,11 @@ async function addAllSharedPartsToAllReconciliation(firmId) { let configSharedPart = fsUtils.readConfig("sharedPart", sharedPartName); for (let reconciliation of configSharedPart.used_in) { if (!reconciliation.handle) { - console.log(`Reconciliation has no handle. Skipping.`); + consola.warn(`Reconciliation has no handle. Skipping.`); continue; } if (!fs.existsSync(`./reconciliation_texts/${reconciliation.handle}`)) { - console.log( + consola.warn( `Reconciliation ${reconciliation.handle} not found. Skipping.` ); continue; @@ -478,7 +512,7 @@ async function removeSharedPartFromReconciliation( configReconciliation.id[firmId] ); if (response.status === 200) { - console.log( + consola.success( `Shared part "${sharedPartHandle}" removed from "${reconciliationHandle}" reconciliation text.` ); } @@ -505,6 +539,7 @@ async function removeSharedPartFromReconciliation( // Look for the template in Silverfin with the handle/name and get it's ID // Type has to be either "reconciliationText", "exportFile" or "sharedPart" async function getTemplateId(firmId, type, handle) { + consola.debug(`Getting ID for ${handle}...`); let templateText; switch (type) { case "reconciliationText": @@ -518,7 +553,7 @@ async function getTemplateId(firmId, type, handle) { break; } if (!templateText) { - console.log(`${handle} wasn't found`); + consola.debug(`${handle} wasn't found`); return false; } const config = fsUtils.readConfig(type, handle); @@ -527,7 +562,7 @@ async function getTemplateId(firmId, type, handle) { } config.id[firmId] = templateText.id; fsUtils.writeConfig(type, handle, config); - console.log(`${handle}: ID updated`); + consola.debug(`${handle}: ID updated`); return true; } @@ -541,7 +576,6 @@ async function getAllTemplatesId(firmId, type) { if (!handle) { continue; } - console.log(`Getting ID for ${handle}...`); await getTemplateId(firmId, type, handle); } } catch (error) { diff --git a/lib/api/firmCredentials.js b/lib/api/firmCredentials.js index c02829f..1ae1580 100644 --- a/lib/api/firmCredentials.js +++ b/lib/api/firmCredentials.js @@ -1,6 +1,7 @@ const fs = require("fs"); const path = require("path"); const homedir = require("os").homedir(); +const { consola } = require("consola"); /** * Class to manage the credentials for the firms @@ -39,7 +40,9 @@ class FirmCredentials { "utf8", (err) => { if (err) { - console.log(`Error while writing credentials file: ${err}`); + consola.error( + new Error(`Error while writing credentials file: ${err}`) + ); } } ); diff --git a/lib/api/sfApi.js b/lib/api/sfApi.js index 02b465d..62c2bcc 100644 --- a/lib/api/sfApi.js +++ b/lib/api/sfApi.js @@ -2,15 +2,16 @@ const axios = require("axios"); const open = require("open"); const prompt = require("prompt-sync")({ sigint: true }); const apiUtils = require("../utils/apiUtils"); +const { consola } = require("consola"); const missingVariables = ["SF_API_CLIENT_ID", "SF_API_SECRET"].filter( (key) => !process.env[key] ); if (missingVariables.length) { - console.log(`Error: Missing API credentials: [${missingVariables}]`); - console.log(`Credentials should be defined as environmental variables.`); - console.log(`Call export ${missingVariables[0]}=...`); - console.log( + consola.error(`Error: Missing API credentials: [${missingVariables}]`); + consola.log(`Credentials should be defined as environmental variables.`); + consola.log(`Call export ${missingVariables[0]}=...`); + consola.log( `If you don't have credentials yet, you need to register your app with Silverfin to get them` ); process.exit(1); @@ -18,8 +19,8 @@ if (missingVariables.length) { async function authorizeApp(firmId = undefined) { try { - console.log( - `Note: if you need to exit this process you can press "Ctrl/Cmmd + C"` + consola.info( + `NOTE: if you need to exit this process you can press "Ctrl/Cmmd + C"` ); const redirectUri = "urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob"; const scope = @@ -37,18 +38,18 @@ async function authorizeApp(firmId = undefined) { const url = `${apiUtils.BASE_URL}/f/${firmIdPrompt}/oauth/authorize?client_id=${process.env.SF_API_CLIENT_ID}&redirect_uri=${redirectUri}&response_type=code&scope=${scope}`; await open(url); - console.log(`You need to authorize your APP in the browser`); - console.log("Insert your credentials..."); + consola.info(`You need to authorize your APP in the browser`); + consola.log("Insert your credentials..."); const authCodePrompt = prompt("Enter your API authorization code: ", { echo: "*", }); // Get tokens const tokens = await apiUtils.getAccessToken(firmIdPrompt, authCodePrompt); if (tokens) { - console.log("Authentication successful"); + consola.success("Authentication successful"); } } catch (error) { - console.log(error); + consola.error(error); process.exit(1); } } @@ -131,7 +132,7 @@ async function findReconciliationTextByHandle(firmId, handle, page = 1) { const reconciliations = await readReconciliationTexts(firmId, page); // No data if (reconciliations.length == 0) { - console.log(`Reconciliation ${handle} not found`); + consola.warn(`Reconciliation "${handle}" not found`); return; } let reconciliationTexts = reconciliations.filter( @@ -335,7 +336,7 @@ async function findSharedPartByName(firmId, sharedPartName, page = 1) { const sharedParts = response.data; // No data if (sharedParts.length == 0) { - console.log(`Shared part ${sharedPartName} not found`); + console.warn(`Shared part "${sharedPartName}" not found`); return; } const sharedPart = sharedParts.find( @@ -569,7 +570,7 @@ async function findExportFileByName(firmId, exportFileName, page = 1) { const exportFiles = await readExportFiles(firmId, page); // No data if (exportFiles.length == 0) { - console.log(`Export file "${exportFileName}" not found`); + console.warn(`Export file "${exportFileName}" not found`); return; } const exportFile = exportFiles.find( @@ -844,7 +845,7 @@ async function findReconciliationInWorkflow( const workflowArray = response.data; // No data if (workflowArray.length == 0) { - console.log( + consola.log( `Reconciliation ${reconciliationHandle} not found in workflow id ${workflowId}` ); return; @@ -889,8 +890,8 @@ async function findReconciliationInWorkflows( } } // Not found - console.log( - `Reconciliation ${reconciliationHandle} not found in any workflow` + console.warn( + `Reconciliation "${reconciliationHandle}" not found in any workflow` ); } diff --git a/lib/cli/cliUpdates.js b/lib/cli/cliUpdates.js index f8d095e..c25dadb 100644 --- a/lib/cli/cliUpdates.js +++ b/lib/cli/cliUpdates.js @@ -3,6 +3,7 @@ const pkg = require("../../package.json"); const chalk = require("chalk"); const { promisify } = require("util"); const exec = promisify(require("child_process").exec); +const { consola } = require("consola"); const PACKAGE_URL = "https://raw.githubusercontent.com/silverfin/silverfin-cli/main/package.json"; @@ -24,37 +25,37 @@ async function checkVersions() { return; } if (latestVersion > currentVersion) { - console.log(`--------------`); - console.log( + consola.log(`--------------`); + consola.log( "There is a new version available of this CLI (" + chalk.red(`${currentVersion}`) + " -> " + chalk.green(`${latestVersion}`) + ")" ); - console.log( + consola.log( "Run " + chalk.italic.bold(`silverfin update`) + " to get the latest version" ); - console.log(`--------------`); + consola.log(`--------------`); } } async function performUpdate() { - console.log(`Updating npm package from GitHub repository...`); + consola.info(`Updating npm package from GitHub repository...`); try { // Exec output contains both stderr and stdout outputs const updateCommand = `sudo npm install -g ${pkg.repository.url}`; - console.log(`Running command: ${chalk.italic(updateCommand)}`); + consola.log(`Running command: ${chalk.italic(updateCommand)}`); const updateOutput = await exec(updateCommand); const updatedPkgVersion = await exec("silverfin --version"); - console.log(`--------------`); - console.log(updateOutput.stdout); - console.log(`--------------`); - console.log( + consola.log(`--------------`); + consola.log(updateOutput.stdout); + consola.log(`--------------`); + consola.success( chalk.bold( `Silverfin CLI succesfully updated to version ${updatedPkgVersion.stdout}` ) @@ -62,14 +63,14 @@ async function performUpdate() { return updateOutput; } catch (error) { - console.log(`--------------`); - console.error(`${chalk.red("ERROR.")} Update of Silverfin CLI failed`); - console.log( + consola.log(`--------------`); + consola.error(`${chalk.red("ERROR.")} Update of Silverfin CLI failed`); + consola.log( `You can try running the following command: ${chalk.bold( `npm install -g ${pkg.repository.url}` )}` ); - console.log(`If that still fails, try updating NPM first.`); + consola.log(`If that still fails, try updating NPM first.`); } } diff --git a/lib/cli/devMode.js b/lib/cli/devMode.js index 7fafefd..2b012aa 100644 --- a/lib/cli/devMode.js +++ b/lib/cli/devMode.js @@ -3,14 +3,15 @@ const path = require("path"); const toolkit = require("../../index"); const liquidTestRunner = require("../liquidTestRunner"); const fsUtils = require("../utils/fsUtils"); +const { consola } = require("consola"); // Watch for changes in an specific YAML and their related liquid files // Run a new test when file is saved async function watchLiquidTest(firmId, handle, testName, html_render) { - console.log( + consola.info( `Watching for changes related to reconciliation "${handle}" to run a new test...` ); - console.log( + consola.warn( `Don't forget to terminate this process when you don't need it anymore! (Ctrl + C)` ); const filePath = path.resolve( @@ -21,7 +22,7 @@ async function watchLiquidTest(firmId, handle, testName, html_render) { `${handle}_liquid_test.yml` ); if (!fs.existsSync(filePath)) { - console.log("YAML file not found: ", filePath); + consola.warn("YAML file not found: ", filePath); return; } const lastUpdates = {}; @@ -46,7 +47,6 @@ async function watchLiquidTest(firmId, handle, testName, html_render) { // Watch liquid files const liquidFiles = fsUtils.listExistingRelatedLiquidFiles(firmId, handle); - console.log(liquidFiles); liquidFiles.forEach((filePath) => { fs.watch(filePath, async (eventType, filename) => { if (eventType !== "change") return; @@ -71,10 +71,10 @@ async function watchLiquidTest(firmId, handle, testName, html_render) { // Identify if it's a reconciliation or shared part // Publish updates when file is saved function watchLiquidFiles(firmId) { - console.log( + consola.info( "Watching for changes in all liquid files to publish their updates..." ); - console.log( + consola.warn( `Don't forget to terminate this process when you don't need it anymore! (Ctrl + C)` ); const files = fsUtils.listExistingFiles(); @@ -96,11 +96,11 @@ function watchLiquidFiles(firmId) { // Update template if (details.type === "reconciliationText") { toolkit.publishReconciliationByHandle(firmId, details.handle); - console.log(`Reconciliation updated: ${details.handle}`); + consola.success(`Reconciliation updated: ${details.handle}`); } if (details.type === "sharedPart") { toolkit.publishSharedPartByName(firmId, details.handle); - console.log(`Shared part updated: ${details.handle}`); + consola.success(`Shared part updated: ${details.handle}`); } }); }); diff --git a/lib/cli/stats.js b/lib/cli/stats.js index 180687a..e6ac350 100644 --- a/lib/cli/stats.js +++ b/lib/cli/stats.js @@ -2,6 +2,7 @@ const exec = require("child_process"); const fs = require("fs"); const fsUtils = require("../utils/fsUtils"); const yaml = require("yaml"); +const { consola } = require("consola"); const YAML_EXPRESSION = `.*reconciliation_texts\/.*\/tests\/.*_liquid_test.*\.y(a)?ml`; const MAIN_EXPRESSION = `.*reconciliation_texts\/.*\/main\.liquid`; @@ -11,7 +12,7 @@ async function yamlFilesActivity(sinceDate) { `git whatchanged --since="${sinceDate}" --name-status --pretty="format:"` ); if (!filesChanged) { - console.log("No files were changed since the date provided"); + consola.info("No files were changed since the date provided"); process.exit(0); } @@ -19,7 +20,7 @@ async function yamlFilesActivity(sinceDate) { const nonEmptyRows = rows.filter(Boolean); if (!nonEmptyRows || nonEmptyRows.length === 0) { - console.log("No files were changed since the date provided"); + consola.info("No files were changed since the date provided"); process.exit(0); } @@ -116,7 +117,7 @@ async function countYamlFiles() { countTests += unitTestsCount; } catch (e) { // Error while parsing the YAML file - // console.log(e); + // consola.log(e); } } } @@ -143,22 +144,23 @@ async function generateStatsOverview(sinceDate) { const today = new Date().toJSON().toString().slice(0, 10); // Overview - console.log(`Summary ( ${sinceDate} - ${today} ):`); - console.log(""); + consola.log(`Summary ( ${sinceDate} - ${today} ):`); + consola.log("------------------------------------"); + consola.log(""); // Added - Deleted - console.log( + consola.log( `New YAML files created: ${ (countByType["A"] || 0) - (countByType["D"] || 0) }` ); // Modified - console.log(`Updates to existing YAML files: ${countByType["M"] || 0}`); + consola.log(`Updates to existing YAML files: ${countByType["M"] || 0}`); // Not showing any information about moved/renamed - console.log(""); - console.log(`Total Reconciliations stored: ${fileCount.reconciliations}`); - console.log(`Total YAML files stored: ${fileCount.yaml}`); - console.log(`Total Unit Tests stored: ${fileCount.tests}`); + consola.log(""); + consola.log(`Total Reconciliations stored: ${fileCount.reconciliations}`); + consola.log(`Total YAML files stored: ${fileCount.yaml}`); + consola.log(`Total Unit Tests stored: ${fileCount.tests}`); // Row to append constRowContent = `\r\n${sinceDate};${today};${ @@ -176,7 +178,7 @@ async function generateStatsOverview(sinceDate) { } if (!fs.existsSync(csvPath)) { fs.writeFileSync(csvPath, constRowHeader, (err) => { - console.log(err); + consola.error(err); }); } // Append content diff --git a/lib/cli/utils.js b/lib/cli/utils.js index 123ffaf..e68d000 100644 --- a/lib/cli/utils.js +++ b/lib/cli/utils.js @@ -1,6 +1,7 @@ const errorUtils = require("../utils/errorUtils"); const prompt = require("prompt-sync")({ sigint: true }); const { firmCredentials } = require("../api/firmCredentials"); +const { consola } = require("consola"); // Load default firm id from Config Object or ENV function loadDefaultFirmId() { @@ -18,7 +19,7 @@ function loadDefaultFirmId() { function checkDefaultFirm(firmUsed, firmIdDefault) { if (firmUsed === firmIdDefault) { - console.log(`Firm ID to be used: ${firmIdDefault}`); + consola.info(`Firm ID to be used: ${firmIdDefault}`); } } @@ -39,7 +40,7 @@ function promptConfirmation() { "This will overwrite existing templates. Do you want to proceed? (y/n): " ); if (confirm.toLocaleLowerCase() !== "yes" && confirm.toLowerCase() !== "y") { - console.log("Operation cancelled"); + consola.warn("Operation cancelled"); process.exit(1); } return true; @@ -70,7 +71,7 @@ function checkUniqueOption(uniqueParameters = [], options) { let formattedParameters = uniqueParameters.map((parameter) => formatOption(parameter) ); - console.log( + consola.error( "Only one of the following options must be used: " + formattedParameters.join(", ") ); @@ -92,7 +93,7 @@ function checkUsedTogether(parameters = [], options) { let formattedParameters = parameters.map((parameter) => formatOption(parameter) ); - console.log( + consola.error( "The following options must be used together: " + formattedParameters.join(", ") ); diff --git a/lib/liquidTestGenerator.js b/lib/liquidTestGenerator.js index c770995..ba5762b 100644 --- a/lib/liquidTestGenerator.js +++ b/lib/liquidTestGenerator.js @@ -1,6 +1,7 @@ const SF = require("./api/sfApi"); const { firmCredentials } = require("../lib/api/firmCredentials"); const Utils = require("./utils/liquidTestUtils"); +const { consola } = require("consola"); // MainProcess async function testGenerator(url, testName) { @@ -12,9 +13,10 @@ async function testGenerator(url, testName) { // Check if firm is authhorized if (!firmCredentials.data.hasOwnProperty(parameters.firmId)) { - console.error( + consola.error( `You have no authorization to access firm id ${parameters.firmId}` ); + process.exit(1); } firmId = parameters.firmId; @@ -207,7 +209,7 @@ async function testGenerator(url, testName) { } } } catch (err) { - console.error(err); + consola.error(err); } } } @@ -262,7 +264,7 @@ async function testGenerator(url, testName) { ].reconciliations[handle].custom = filteredCustomDrops; } } catch (err) { - console.log(err); + consola.error(err); } } } @@ -349,7 +351,7 @@ async function testGenerator(url, testName) { }; } } catch (error) { - console.log(error); + consola.error(error); // Previous Period // Should we include this ? } diff --git a/lib/liquidTestRunner.js b/lib/liquidTestRunner.js index 52998f9..95e6a99 100644 --- a/lib/liquidTestRunner.js +++ b/lib/liquidTestRunner.js @@ -12,6 +12,7 @@ const { spinner } = require("./cli/spinner"); const SF = require("./api/sfApi"); const fsUtils = require("./utils/fsUtils"); const { ReconciliationText } = require("../lib/reconciliationText"); +const { consola } = require("consola"); function findTestRows(testContent) { const options = { maxAliasCount: 10000 }; @@ -35,7 +36,7 @@ function buildTestParams(firmId, handle, testName = "", html_render = false) { // Empty YAML check if (testContent.split("\n").length <= 1) { - console.log(`${handle}: there are no tests stored in the YAML file`); + consola.info(`${handle}: there are no tests stored in the YAML file`); return false; } @@ -69,7 +70,7 @@ function buildTestParams(firmId, handle, testName = "", html_render = false) { if (testName) { const indexes = findTestRows(testContent); if (!Object.keys(indexes).includes(testName)) { - console.log(`Test ${testName} not found in YAML`); + consola.error(`Test ${testName} not found in YAML`); process.exit(1); } testParams.test_line = indexes[testName] + 1; @@ -94,7 +95,7 @@ async function fetchResult(firmId, testRunId) { pollingDelay *= 1.05; if (waitingTime >= waitingLimit) { spinner.stop(); - console.log("Timeout. Try to run your test again"); + consola.error("Timeout. Try to run your test again"); break; } } @@ -104,7 +105,7 @@ async function fetchResult(firmId, testRunId) { function listErrors(items, type) { const itemsKeys = Object.keys(items); - console.log( + consola.log( chalk.red( `${itemsKeys.length} ${type} expectation${ itemsKeys.length > 1 ? "s" : "" @@ -113,8 +114,8 @@ function listErrors(items, type) { ); itemsKeys.forEach((itemName) => { let itemDetails = items[itemName]; - console.log(`At line number ${itemDetails.line_number}`); - console.log( + consola.log(`At line number ${itemDetails.line_number}`); + consola.log( `For ${type} ${chalk.blue.bold(itemName)} got ${chalk.blue.bold( itemDetails.got )} (${chalk.italic( @@ -124,7 +125,7 @@ function listErrors(items, type) { )})` ); }); - console.log(""); + consola.log(""); } // Find at least one error in the all tests @@ -159,22 +160,21 @@ function processTestRunResponse(testRun) { // Possible status: started, completed, test_error, internal_error switch (testRun.status) { case "internal_error": - console.log( + consola.error( "Internal error. Try to run the test again or contact support if the issue persists." ); break; case "test_error": - console.log("Ran into an error an couldn't complete test run"); - console.log(chalk.red(testRun.error_message)); + consola.error("Ran into an error an couldn't complete test run"); + consola.log(chalk.red(testRun.error_message)); break; case "completed": const errorsPresent = checkAllTestsErrorsPresent(testRun.tests); if (errorsPresent === false) { - console.log(chalk.green("ALL TESTS HAVE PASSED")); + consola.success(chalk.green("ALL TESTS HAVE PASSED")); } else { - console.log(""); - - console.log( + consola.log(""); + consola.log( chalk.red( `${Object.keys(testRun.tests).length} TEST${ Object.keys(testRun.tests).length > 1 ? "S" : "" @@ -192,42 +192,42 @@ function processTestRunResponse(testRun) { if (!testErrorsPresent) { return; } - console.log( + consola.log( "---------------------------------------------------------------" ); - console.log(chalk.bold(testName)); + consola.log(chalk.bold(testName)); let testElements = testRun.tests[testName]; // Display success messages of test if (testElements.reconciled === null) { - console.log(chalk.green("Reconciliation expectation passed")); + consola.success(chalk.green("Reconciliation expectation passed")); } if (Object.keys(testElements.results).length === 0) { - console.log(chalk.green("All result expectations passed")); + consola.success(chalk.green("All result expectations passed")); } if (Object.keys(testElements.rollforwards).length === 0) { - console.log(chalk.green("All rollforward expectations passed")); + consola.success(chalk.green("All rollforward expectations passed")); } // Display error messages of test // Reconciled if (testElements.reconciled !== null) { - console.log(chalk.red("Reconciliation expectation failed")); - console.log( + consola.log(chalk.red("Reconciliation expectation failed")); + consola.log( `At line number ${testElements.reconciled.line_number}` ); - console.log( + consola.log( `got ${chalk.blue.bold( testElements.reconciled.got )} but expected ${chalk.blue.bold( testElements.reconciled.expected )}` ); - console.log(""); + consola.log(""); } // Results @@ -265,14 +265,14 @@ async function getHTML(url, testName, openBrowser = false) { if (commandExistsSync("wsl-open")) { exec(`wsl-open ${filePath}`); } else { - console.log( + consola.info( "In order to automatically open HTML files on WSL, we need to install the wsl-open script." ); - console.log( + consola.log( "You might be prompted for your password in order for us to install 'sudo npm install -g wsl-open'" ); execSync("sudo npm install -g wsl-open"); - console.log("Installed wsl-open script"); + consola.log("Installed wsl-open script"); exec(`wsl-open ${filePath}`); } } else { @@ -351,11 +351,11 @@ async function runTestsStatusOnly(firmId, handle, testName = "") { if (testRun && testRun.status === "completed") { const errorsPresent = checkAllTestsErrorsPresent(testRun.tests); if (errorsPresent === false) { - console.log("\r\nPASSED"); + consola.success("\r\nPASSED"); return "PASSED"; } } - console.log("\r\nFAILED"); + consola.log("\r\nFAILED"); return "FAILED"; } diff --git a/lib/reconciliationText.js b/lib/reconciliationText.js index fd36956..3a61d52 100644 --- a/lib/reconciliationText.js +++ b/lib/reconciliationText.js @@ -1,6 +1,7 @@ const fs = require("fs"); const fsUtils = require("./utils/fsUtils"); const templateUtils = require("./utils/templateUtils"); +const { consola } = require("consola"); class ReconciliationText { static CONFIG_ITEMS = [ @@ -101,7 +102,7 @@ class ReconciliationText { static #missingHandle(template) { if (!template.handle) { - console.log( + consola.warn( `Template has no handle, add a handle before importing it from Silverfin. Skipped` ); return true; @@ -137,7 +138,7 @@ class ReconciliationText { if ( !this.RECONCILIATION_TYPE_OPTIONS.includes(attributes.reconciliation_type) ) { - console.log( + consola.warn( `Wrong reconciliation type. It must be one of the following: ${this.RECONCILIATION_TYPE_OPTIONS.join( ", " )}. Skipping it's definition.` diff --git a/lib/utils/apiUtils.js b/lib/utils/apiUtils.js index 7c1c875..cb20f48 100644 --- a/lib/utils/apiUtils.js +++ b/lib/utils/apiUtils.js @@ -1,6 +1,7 @@ const { firmCredentials } = require("../api/firmCredentials"); const pkg = require("../../package.json"); const axios = require("axios"); +const { consola } = require("consola"); const BASE_URL = process.env.SF_HOST || "https://live.getsilverfin.com"; @@ -17,10 +18,10 @@ async function getAccessToken(firmId, authCode) { firmCredentials.storeNewTokenPair(firmId, response.data); return true; } catch (error) { - console.log( + consola.error( `Response Status: ${error.response.status} (${error.response.statusText})` ); - console.log( + consola.error( `Error description: ${JSON.stringify( error.response.data.error_description )}` @@ -32,7 +33,7 @@ async function getAccessToken(firmId, authCode) { // Get a new pair of tokens async function refreshTokens(firmId, accessToken, refreshToken) { try { - console.log(`Requesting new pair of tokens`); + consola.debug(`Requesting new pair of tokens`); let data = { client_id: process.env.SF_API_CLIENT_ID, client_secret: process.env.SF_API_SECRET, @@ -47,15 +48,12 @@ async function refreshTokens(firmId, accessToken, refreshToken) { ); firmCredentials.storeNewTokenPair(firmId, response.data); } catch (error) { - console.log( - `Response Status: ${error.response.status} (${error.response.statusText})` - ); - console.log( - `Error description: ${JSON.stringify( - error.response.data.error_description - )}` - ); - console.log( + const description = JSON.stringify(error.response.data.error_description); + consola.error( + `Response Status: ${error.response.status} (${error.response.statusText})`, + "\n", + `Error description: ${description}`, + "\n", `Error refreshing the tokens. Try running the authentication process again` ); process.exit(1); @@ -71,13 +69,13 @@ function setAxiosDefaults(firmId) { "Authorization" ] = `Bearer ${firmTokens.accessToken}`; } else { - console.log(`Missing authorization for firm id: ${firmId}`); + consola.error(`Missing authorization for firm id: ${firmId}`); process.exit(1); } } function responseSuccessHandler(response) { - console.log( + consola.debug( `Response Status: ${response.status} (${response.statusText}) - method: ${response.config.method} - url: ${response.config.url}` ); } @@ -90,27 +88,27 @@ async function responseErrorHandler( callbackParameters ) { if (error && error.response) { - console.log( + consola.error( `Response Status: ${error.response.status} (${error.response.statusText}) - method: ${error.response.config.method} - url: ${error.response.config.url}` ); } // Valid Request. Not Found if (error.response.status === 404) { - console.log( + consola.error( `Response Error (404): ${JSON.stringify(error.response.data.error)}` ); return; } // Bad Request if (error.response.status === 400) { - console.log( + consola.error( `Response Error (400): ${JSON.stringify(error.response.data.error)}` ); return; } // No access credentials if (error.response.status === 401) { - console.log( + consola.error( `Response Error (401): ${JSON.stringify(error.response.data.error)}` ); if (refreshToken) { @@ -124,7 +122,7 @@ async function responseErrorHandler( // Call the original function again return callbackFunction(...Object.values(callbackParameters)); } else { - console.log( + consola.error( `Error 401: API calls failed, try to run the authorization process again` ); process.exit(1); @@ -132,13 +130,16 @@ async function responseErrorHandler( } // Unprocessable Entity if (error.response.status === 422) { - console.log(`Response Error (422): ${JSON.stringify(error.response.data)}`); - console.log(`You don't have the rights to update the previous parameters`); + consola.error( + `Response Error (422): ${JSON.stringify(error.response.data)}`, + "\n", + `You don't have the rights to update the previous parameters` + ); process.exit(1); } // Forbidden if (error.response.status === 403) { - console.log("Error (403): Forbidden access. Terminating process"); + consola.error("Error (403): Forbidden access. Terminating process"); process.exit(1); } // Not handled diff --git a/lib/utils/errorUtils.js b/lib/utils/errorUtils.js index 6c4c66c..28eda06 100644 --- a/lib/utils/errorUtils.js +++ b/lib/utils/errorUtils.js @@ -1,9 +1,10 @@ const pkg = require("../../package.json"); +const { consola } = require("consola"); // Uncaught Errors. Open Issue in GitHub function uncaughtErrors(error) { if (error.stack) { - console.error(""); + console.error("----------------------------"); console.error( `!!! Please open an issue including this log on ${pkg.bugs.url}` ); @@ -12,16 +13,17 @@ function uncaughtErrors(error) { console.error(`silverfin: v${pkg.version}, node: ${process.version}`); console.error(""); console.error(error.stack); + console.error("----------------------------"); } process.exit(1); } function errorHandler(error) { if (error.code == "ENOENT") { - console.log( + consola.log( `The path ${error.path} was not found, please ensure you've imported or created all required files` ); - process.exit(); + process.exit(1); } else { uncaughtErrors(error); } diff --git a/lib/utils/fsUtils.js b/lib/utils/fsUtils.js index 0999155..8478b91 100644 --- a/lib/utils/fsUtils.js +++ b/lib/utils/fsUtils.js @@ -1,5 +1,6 @@ const fs = require("fs"); const path = require("path"); +const { consola } = require("consola"); const FOLDERS = { reconciliationText: "reconciliation_texts", @@ -40,8 +41,11 @@ function createSharedPartFolders(handle) { const errorCallbackLiquidTest = (error) => { if (error) { - console.log("An error occurred when creating the liquid testing file"); - console.log(error); + consola.error( + "An error occurred when creating the liquid testing file", + "\n", + error + ); } }; @@ -60,7 +64,7 @@ async function createLiquidTestYaml(templateType, handle, testContent) { ); const existingFile = fs.existsSync(liquidTestPath); if (existingFile) { - console.log( + consola.debug( `Liquid testing file ${handle}_liquid_test.yml already exists, so the file content was not overwritten` ); return; @@ -69,7 +73,7 @@ async function createLiquidTestYaml(templateType, handle, testContent) { if (error) { errorCallbackLiquidTest(error); } else { - console.log(`Liquid testing YAML file created for ${handle}`); + consola.debug(`Liquid testing YAML file created for ${handle}`); } }); } @@ -117,7 +121,7 @@ async function createTemplateFiles(templateType, handle, textMain, textParts) { ); fs.writeFile(partPath, textParts[textPartName], emptyCallback); }); - console.log(`Liquid template file(s) created for ${handle}`); + consola.debug(`Liquid template file(s) created for ${handle}`); } async function createLiquidFile(relativePath, fileName, textContent) { @@ -127,7 +131,7 @@ async function createLiquidFile(relativePath, fileName, textContent) { textContent, emptyCallback ); - console.log(`${fileName} file created`); + consola.debug(`${fileName} file created`); } function writeConfig(templateType, handle, config) { diff --git a/lib/utils/liquidTestUtils.js b/lib/utils/liquidTestUtils.js index a346384..93f0211 100644 --- a/lib/utils/liquidTestUtils.js +++ b/lib/utils/liquidTestUtils.js @@ -1,6 +1,7 @@ const YAML = require("yaml"); const fs = require("fs"); const fsUtils = require("./fsUtils"); +const { consola } = require("consola"); // Create base Liquid Test object function createBaseLiquidTest(testName) { @@ -35,7 +36,7 @@ function extractURL(url) { } else if (parts.indexOf("account_entry") !== -1) { type = "accountId"; } else { - console.log( + consola.error( "Not possible to identify if it's a reconciliation text or account entry." ); process.exit(1); @@ -48,7 +49,7 @@ function extractURL(url) { [type]: parts[7], }; } catch (err) { - console.log( + consola.error( "The URL provided is not correct. Double check it and run the command again." ); process.exit(1); @@ -83,11 +84,12 @@ function exportYAML(handle, liquidTestObject) { lineWidth: 0, }, }), - (err, data) => { + (err, _) => { if (err) { - console.error(err); + consola.error(err); + process.exit(1); } else { - console.log(`File saved: ${filePath}`); + consola.info(`File saved: ${filePath}`); } } ); @@ -114,7 +116,7 @@ function getCompanyDependencies(reconcilationObject, reconciliationHandle) { // No main part ? if (!reconcilationObject || !reconcilationObject.text) { - console.log( + consola.warn( `Reconciliation "${reconciliationHandle}": no liquid code found` ); return { standardDropElements: [], customDropElements: [] }; @@ -163,7 +165,7 @@ function searchForResultsFromDependenciesInLiquid( // No main part ? if (!reconcilationObject || !reconcilationObject.text) { - console.log( + consola.warn( `Reconciliation "${reconciliationHandle}": no liquid code found` ); return resultsCollection; @@ -260,7 +262,7 @@ function searchForCustomsFromDependenciesInLiquid( // No main part ? if (!reconcilationObject || !reconcilationObject.text) { - console.log( + consola.warn( `Reconciliation "${reconciliationHandle}": no liquid code found` ); return customCollection; @@ -311,7 +313,7 @@ function lookForSharedPartsInLiquid(reconcilationObject, reconciliationHandle) { // No main part ? if (!reconcilationObject || !reconcilationObject.text) { - console.log( + consola.warn( `Reconciliation "${reconciliationHandle}": no liquid code found` ); return; @@ -355,7 +357,7 @@ function lookForAccountsIDs(obj) { function lookForDefaultAccounts(reconcilationObject) { // No main part ? if (!reconcilationObject.text) { - console.log(`Reconciliation "${reconciliationHandle}": no liquid code found`); + consola.warn(`Reconciliation "${reconciliationHandle}": no liquid code found`); return; }; // Main diff --git a/lib/utils/liquidUtils.js b/lib/utils/liquidUtils.js index 9dcd5fa..f83b9ce 100644 --- a/lib/utils/liquidUtils.js +++ b/lib/utils/liquidUtils.js @@ -1,3 +1,5 @@ +const { consola } = require("consola"); + // Search for all "input" tags in Liquid function lookForInputTags(liquidCode, input_type = "") { const input_types = [ @@ -13,7 +15,7 @@ function lookForInputTags(liquidCode, input_type = "") { "", ]; if (!input_types.includes(input_type)) { - console.log("Input type defined not supported"); + consola.error("Input type defined not supported"); process.exit(1); } let reInput; diff --git a/lib/utils/templateUtils.js b/lib/utils/templateUtils.js index 478fc3d..7add5a4 100644 --- a/lib/utils/templateUtils.js +++ b/lib/utils/templateUtils.js @@ -1,11 +1,10 @@ -const fsUtils = require("./fsUtils"); -const fs = require("fs"); +const { consola } = require("consola"); /** Check if the name is valid. If not, log it and return false. Valid names are alphanumeric and underscore */ function checkValidName(name) { const nameCheck = /^[a-zA-Z0-9_]*$/.test(name); if (!nameCheck) { - console.log( + consola.warn( `Template name contains invalid characters. Skipping. Valid template names only include alphanumeric characters and underscores. Current name: ${name}` ); return false; @@ -24,7 +23,7 @@ function filterParts(template) { function missingLiquidCode(template) { if (!template.text) { - console.log( + consola.warn( `Template ${ template.handle || template.name || "" }: this template's liquid code was empty or hidden so it was not imported.` diff --git a/package-lock.json b/package-lock.json index f0070fa..1abdbb6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,19 @@ { "name": "silverfin-cli", - "version": "1.14.1", + "version": "1.17.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "silverfin-cli", - "version": "1.14.1", + "version": "1.17.0", "license": "MIT", "dependencies": { "axios": "^0.26.1", "chalk": "4.1.2", "command-exists": "^1.2.9", "commander": "^9.4.0", + "consola": "^3.2.3", "is-wsl": "^2.2.0", "open": "^8.4.0", "prompt-sync": "^4.2.0", @@ -363,6 +364,14 @@ "dev": true, "license": "MIT" }, + "node_modules/consola": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz", + "integrity": "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", diff --git a/package.json b/package.json index 23affcf..c796a13 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "chalk": "4.1.2", "command-exists": "^1.2.9", "commander": "^9.4.0", + "consola": "^3.2.3", "is-wsl": "^2.2.0", "open": "^8.4.0", "prompt-sync": "^4.2.0", diff --git a/yarn.lock b/yarn.lock index dce1f41..0ead70b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -161,6 +161,11 @@ concat-map@0.0.1: resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +consola@^3.2.3: + version "3.2.3" + resolved "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz" + integrity sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ== + cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz"