From 9b15ee3662125aebdeb25b6ee7c0d672c1b03882 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 1 May 2024 12:17:11 +0400 Subject: [PATCH 01/20] feat(src): implement error writing strategy --- src/index.ts | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/index.ts b/src/index.ts index 0191f24f3..d63010573 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,7 @@ import {aggregate} from './lib/aggregate'; import {compute} from './lib/compute'; import {injectEnvironment} from './lib/environment'; import {exhaust} from './lib/exhaust'; -import {initalize} from './lib/initialize'; +import {initialize} from './lib/initialize'; import {load} from './lib/load'; import {parameterize} from './lib/parameterize'; @@ -27,13 +27,21 @@ const impactEngine = async () => { const {tree, rawContext, parameters} = await load(inputPath!, paramPath); const context = await injectEnvironment(rawContext); - parameterize.combine(context.params, parameters); - const pluginStorage = await initalize(context.initialize.plugins); - const computedTree = await compute(tree, {context, pluginStorage}); - const aggregatedTree = aggregate(computedTree, context.aggregation); - exhaust(aggregatedTree, context, outputOptions); - return; + try { + parameterize.combine(context.params, parameters); + const pluginStorage = await initialize(context.initialize.plugins); + const computedTree = await compute(tree, {context, pluginStorage}); + const aggregatedTree = aggregate(computedTree, context.aggregation); + exhaust(aggregatedTree, context, outputOptions); + } catch (error) { + if (error instanceof Error) { + context.execution.status = 'fail'; + context.execution.error = error.toString(); + logger.error(error); + exhaust(tree, context, outputOptions); + } + } }; impactEngine().catch(andHandle); From 2b66257461854b24ab7eb35f5fac852ab3422356 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 1 May 2024 12:19:33 +0400 Subject: [PATCH 02/20] test(lib): fix typo in initialize --- .../{initalize.test.ts => initialize.test.ts} | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) rename src/__tests__/unit/lib/{initalize.test.ts => initialize.test.ts} (92%) diff --git a/src/__tests__/unit/lib/initalize.test.ts b/src/__tests__/unit/lib/initialize.test.ts similarity index 92% rename from src/__tests__/unit/lib/initalize.test.ts rename to src/__tests__/unit/lib/initialize.test.ts index fcd4a10d0..ad5541684 100644 --- a/src/__tests__/unit/lib/initalize.test.ts +++ b/src/__tests__/unit/lib/initialize.test.ts @@ -10,7 +10,7 @@ jest.mock('../../../util/log-memoize', () => ({ memoizedLog: mockLog, })); -import {initalize} from '../../../lib/initialize'; +import {initialize} from '../../../lib/initialize'; import {ERRORS} from '../../../util/errors'; @@ -25,7 +25,7 @@ describe('lib/initalize: ', () => { describe('initalize(): ', () => { it('creates instance with get and set methods.', async () => { const plugins = {}; - const response = await initalize(plugins); + const response = await initialize(plugins); expect(response).toHaveProperty('get'); expect(response).toHaveProperty('set'); @@ -40,7 +40,7 @@ describe('lib/initalize: ', () => { method: 'Mockavizta', }, }; - const storage = await initalize(plugins); + const storage = await initialize(plugins); const pluginName = Object.keys(plugins)[0]; const module = storage.get(pluginName); @@ -59,7 +59,7 @@ describe('lib/initalize: ', () => { }, }, }; - const storage = await initalize(plugins); + const storage = await initialize(plugins); const pluginName = Object.keys(plugins)[0]; const module = storage.get(pluginName); @@ -79,7 +79,7 @@ describe('lib/initalize: ', () => { }; try { - await initalize(plugins); + await initialize(plugins); } catch (error) { expect(error).toBeInstanceOf(PluginCredentialError); @@ -101,7 +101,7 @@ describe('lib/initalize: ', () => { }; try { - await initalize(plugins); + await initialize(plugins); } catch (error) { expect(error).toBeInstanceOf(PluginCredentialError); @@ -121,7 +121,7 @@ describe('lib/initalize: ', () => { }, }, }; - const storage = await initalize(plugins); + const storage = await initialize(plugins); const pluginName = Object.keys(plugins)[0]; const module = storage.get(pluginName); @@ -139,7 +139,7 @@ describe('lib/initalize: ', () => { }, }, }; - const storage = await initalize(plugins); + const storage = await initialize(plugins); const pluginName = Object.keys(plugins)[0]; const module = storage.get(pluginName); @@ -159,7 +159,7 @@ describe('lib/initalize: ', () => { }; try { - await initalize(plugins); + await initialize(plugins); } catch (error) { expect(error).toBeInstanceOf(ModuleInitializationError); From 6f244b93e53bea691022c97f9b30b5aee19426bb Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 1 May 2024 14:24:08 +0400 Subject: [PATCH 03/20] feat(types): add status and error to execution --- src/types/manifest.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types/manifest.ts b/src/types/manifest.ts index ce096f779..7b1d265f7 100644 --- a/src/types/manifest.ts +++ b/src/types/manifest.ts @@ -23,6 +23,8 @@ export type ContextWithExec = Omit & { 'date-time': string; dependencies: string[]; }; + status: 'success' | 'fail'; + error?: string; }; }; From 1d5df3b6c6262ecdbd15c999b2be655c85549e99 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 1 May 2024 14:24:23 +0400 Subject: [PATCH 04/20] test(lib): fix typo in initialize --- src/lib/initialize.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/initialize.ts b/src/lib/initialize.ts index 5f603ac2a..d6bf7c184 100644 --- a/src/lib/initialize.ts +++ b/src/lib/initialize.ts @@ -84,9 +84,9 @@ const initPlugin = async ( }; /** - * Registers all plugins from `manifest`.`initalize` property. + * Registers all plugins from `manifest`.`initialize` property. */ -export const initalize = async ( +export const initialize = async ( plugins: GlobalPlugins ): Promise => { const storage = pluginStorage(); From cd1af79be87959f4e885ff9e8e66bebee46fe0b8 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 1 May 2024 14:25:39 +0400 Subject: [PATCH 05/20] feat(lib): add execution status to inject env --- src/lib/environment.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/environment.ts b/src/lib/environment.ts index 525b7b545..6b5b27b35 100644 --- a/src/lib/environment.ts +++ b/src/lib/environment.ts @@ -65,6 +65,7 @@ export const injectEnvironment = async (context: Context) => { const contextWithExec: ContextWithExec = { ...context, execution: { + status: 'success', command: process.argv.join(' '), environment: { 'if-version': packageJson.version, From 7e74afbc7fef881618123a5ff2d5ab79069a1282 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 8 May 2024 17:49:34 +0400 Subject: [PATCH 06/20] fix(util): tune manifest validation --- src/util/validations.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/util/validations.ts b/src/util/validations.ts index df947b589..538baeb55 100644 --- a/src/util/validations.ts +++ b/src/util/validations.ts @@ -50,8 +50,22 @@ export const manifestSchema = z.object({ ), outputs: z.array(z.string()).optional(), }), + execution: z + .object({ + command: z.string(), + environment: z.object({ + 'if-version': z.string(), + os: z.string(), + 'os-version': z.string(), + 'node-version': z.string(), + 'date-time': z.string(), + dependencies: z.array(z.string()), + }), + status: z.string(), + error: z.string().optional(), + }) + .optional(), tree: z.record(z.string(), z.any()), - 'if-version': z.string().optional().nullable(), }); /** From 638451b4f5733aa449cdcd2cbfee8fac77bbf5ac Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 8 May 2024 17:51:29 +0400 Subject: [PATCH 07/20] fix(types): remove manually declared types --- src/types/manifest.ts | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/types/manifest.ts b/src/types/manifest.ts index 7b1d265f7..2359a3433 100644 --- a/src/types/manifest.ts +++ b/src/types/manifest.ts @@ -12,20 +12,7 @@ export type AggregationParams = Manifest['aggregation']; export type AggregationParamsSure = Extract; export type Context = Omit; -export type ContextWithExec = Omit & { - execution: { - command: string; - environment: { - 'if-version': string; - os: string; - 'os-version': string; - 'node-version': string; - 'date-time': string; - dependencies: string[]; - }; - status: 'success' | 'fail'; - error?: string; - }; -}; + +export type ContextWithExec = Omit; export type ManifestParameter = Extract[number]; From 430fb331ae1e60524dec3c5ed27074f27fe607b4 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 8 May 2024 18:02:01 +0400 Subject: [PATCH 08/20] fix(util): tune manifest validation --- src/util/validations.ts | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/util/validations.ts b/src/util/validations.ts index 538baeb55..5b1aff0cf 100644 --- a/src/util/validations.ts +++ b/src/util/validations.ts @@ -50,21 +50,19 @@ export const manifestSchema = z.object({ ), outputs: z.array(z.string()).optional(), }), - execution: z - .object({ - command: z.string(), - environment: z.object({ - 'if-version': z.string(), - os: z.string(), - 'os-version': z.string(), - 'node-version': z.string(), - 'date-time': z.string(), - dependencies: z.array(z.string()), - }), - status: z.string(), - error: z.string().optional(), - }) - .optional(), + execution: z.object({ + command: z.string(), + environment: z.object({ + 'if-version': z.string(), + os: z.string(), + 'os-version': z.string(), + 'node-version': z.string(), + 'date-time': z.string(), + dependencies: z.array(z.string()), + }), + status: z.string(), + error: z.string().optional(), + }), tree: z.record(z.string(), z.any()), }); From 1fe6a3f0371fcb9a5d79b589b592d5018ef539db Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 8 May 2024 18:05:24 +0400 Subject: [PATCH 09/20] fix(lib): return raw manifest in response --- src/lib/load.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/lib/load.ts b/src/lib/load.ts index c3c3912ec..a024444df 100644 --- a/src/lib/load.ts +++ b/src/lib/load.ts @@ -1,4 +1,3 @@ -import {validateManifest} from '../util/validations'; import {openYamlFileAsObject} from '../util/yaml'; import {readAndParseJson} from '../util/json'; @@ -11,7 +10,6 @@ import {Parameters} from '../types/parameters'; */ export const load = async (inputPath: string, paramPath?: string) => { const rawManifest = await openYamlFileAsObject(inputPath); - const {tree, ...rawContext} = validateManifest(rawManifest); const parametersFromCli = paramPath && (await readAndParseJson(paramPath)); /** @todo validate json */ @@ -20,8 +18,7 @@ export const load = async (inputPath: string, paramPath?: string) => { PARAMETERS; /** @todo PARAMETERS should be specified in parameterize only */ return { - tree, - rawContext, + rawManifest, parameters, }; }; From 9376c549fddd18ccae694f6ebc7c145ad338b190 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 8 May 2024 18:06:34 +0400 Subject: [PATCH 10/20] fix(lib): env return type --- src/lib/environment.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/environment.ts b/src/lib/environment.ts index 6b5b27b35..1db3f5bda 100644 --- a/src/lib/environment.ts +++ b/src/lib/environment.ts @@ -2,7 +2,7 @@ import {DateTime} from 'luxon'; import {execPromise} from '../util/helpers'; -import {Context, ContextWithExec} from '../types/manifest'; +import {Manifest} from '../types/manifest'; import {NpmListResponse, PackageDependency} from '../types/environment'; import {osInfo} from '../util/os-checker'; @@ -56,14 +56,16 @@ const listDependencies = async () => { }; /** - * Injects execution information (command, environment) to existing context. + * Injects execution information (command, environment) to existing manifest. */ -export const injectEnvironment = async (context: Context) => { +export const injectEnvironment = async ( + manifest: Manifest +): Promise => { const dependencies = await listDependencies(); const info = await osInfo(); - const contextWithExec: ContextWithExec = { - ...context, + return { + ...manifest, execution: { status: 'success', command: process.argv.join(' '), @@ -77,6 +79,4 @@ export const injectEnvironment = async (context: Context) => { }, }, }; - - return contextWithExec; }; From 349d482df9d2b5b92fdfdb0a9667ae33686c49a3 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 8 May 2024 18:06:58 +0400 Subject: [PATCH 11/20] test(lib): fix load cases --- src/__tests__/unit/lib/load.test.ts | 131 ++++++++++++++-------------- 1 file changed, 66 insertions(+), 65 deletions(-) diff --git a/src/__tests__/unit/lib/load.test.ts b/src/__tests__/unit/lib/load.test.ts index 0e40636dc..bed5da780 100644 --- a/src/__tests__/unit/lib/load.test.ts +++ b/src/__tests__/unit/lib/load.test.ts @@ -28,32 +28,7 @@ describe('lib/load: ', () => { const result = await load(inputPath, paramPath); const expectedValue = { - tree: { - children: { - 'front-end': { - pipeline: ['boavizta-cpu'], - config: { - 'boavizta-cpu': { - 'core-units': 24, - processor: 'Intel® Core™ i7-1185G7', - }, - }, - inputs: [ - { - timestamp: '2023-07-06T00:00', - duration: 3600, - 'cpu/utilization': 18.392, - }, - { - timestamp: '2023-08-06T00:00', - duration: 3600, - 'cpu/utilization': 16, - }, - ], - }, - }, - }, - rawContext: { + rawManifest: { name: 'gsf-demo', description: 'Hello', tags: { @@ -69,10 +44,37 @@ describe('lib/load: ', () => { }, }, }, + tree: { + children: { + 'front-end': { + pipeline: ['boavizta-cpu'], + config: { + 'boavizta-cpu': { + 'core-units': 24, + processor: 'Intel® Core™ i7-1185G7', + }, + }, + inputs: [ + { + timestamp: '2023-07-06T00:00', + duration: 3600, + 'cpu/utilization': 18.392, + }, + { + timestamp: '2023-08-06T00:00', + duration: 3600, + 'cpu/utilization': 16, + }, + ], + }, + }, + }, }, parameters: PARAMETERS, }; + console.log(JSON.stringify(result)); + expect(result).toEqual(expectedValue); }); @@ -82,45 +84,8 @@ describe('lib/load: ', () => { const result = await load(inputPath, paramPath); - const expectedParameters = { - 'mock-carbon': { - description: 'an amount of carbon emitted into the atmosphere', - unit: 'gCO2e', - aggregation: 'sum', - }, - 'mock-cpu': { - description: 'number of cores available', - unit: 'cores', - aggregation: 'none', - }, - }; const expectedValue = { - tree: { - children: { - 'front-end': { - pipeline: ['boavizta-cpu'], - config: { - 'boavizta-cpu': { - 'core-units': 24, - processor: 'Intel® Core™ i7-1185G7', - }, - }, - inputs: [ - { - timestamp: '2023-07-06T00:00', - duration: 3600, - 'cpu/utilization': 18.392, - }, - { - timestamp: '2023-08-06T00:00', - duration: 3600, - 'cpu/utilization': 16, - }, - ], - }, - }, - }, - rawContext: { + rawManifest: { name: 'gsf-demo', description: 'Hello', tags: { @@ -136,8 +101,44 @@ describe('lib/load: ', () => { }, }, }, + tree: { + children: { + 'front-end': { + pipeline: ['boavizta-cpu'], + config: { + 'boavizta-cpu': { + 'core-units': 24, + processor: 'Intel® Core™ i7-1185G7', + }, + }, + inputs: [ + { + timestamp: '2023-07-06T00:00', + duration: 3600, + 'cpu/utilization': 18.392, + }, + { + timestamp: '2023-08-06T00:00', + duration: 3600, + 'cpu/utilization': 16, + }, + ], + }, + }, + }, + }, + parameters: { + 'mock-carbon': { + description: 'an amount of carbon emitted into the atmosphere', + unit: 'gCO2e', + aggregation: 'sum', + }, + 'mock-cpu': { + description: 'number of cores available', + unit: 'cores', + aggregation: 'none', + }, }, - parameters: expectedParameters, }; expect(result).toEqual(expectedValue); From 5d72e9929d5e2ec13ca1bf017b635f2043f329fb Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 8 May 2024 18:07:49 +0400 Subject: [PATCH 12/20] test(mocks): tune export csv --- src/__mocks__/builtins/export-csv.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/__mocks__/builtins/export-csv.ts b/src/__mocks__/builtins/export-csv.ts index 3d815f1ad..44170e619 100644 --- a/src/__mocks__/builtins/export-csv.ts +++ b/src/__mocks__/builtins/export-csv.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import {Context} from '../../types/manifest'; export const tree = { @@ -117,6 +118,7 @@ export const tree = { }, }; +// @ts-ignore export const context: Context = { name: 'demo', description: '', From 1edbed60d5f91f9d47b91c0a72a238daf57491fd Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 8 May 2024 18:09:28 +0400 Subject: [PATCH 13/20] fix(src): tune impact engine strategy --- src/index.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/index.ts b/src/index.ts index d63010573..156f07706 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,7 @@ import {parameterize} from './lib/parameterize'; import {parseArgs} from './util/args'; import {andHandle} from './util/helpers'; import {logger} from './util/logger'; +import {validateManifest} from './util/validations'; import {STRINGS} from './config'; @@ -25,10 +26,11 @@ const impactEngine = async () => { logger.info(DISCLAIMER_MESSAGE); const {inputPath, paramPath, outputOptions} = options; - const {tree, rawContext, parameters} = await load(inputPath!, paramPath); - const context = await injectEnvironment(rawContext); + const {rawManifest, parameters} = await load(inputPath!, paramPath); + const envManifest = await injectEnvironment(rawManifest); try { + const {tree, ...context} = validateManifest(envManifest); parameterize.combine(context.params, parameters); const pluginStorage = await initialize(context.initialize.plugins); const computedTree = await compute(tree, {context, pluginStorage}); @@ -36,9 +38,10 @@ const impactEngine = async () => { exhaust(aggregatedTree, context, outputOptions); } catch (error) { if (error instanceof Error) { - context.execution.status = 'fail'; - context.execution.error = error.toString(); + envManifest.execution.status = 'fail'; + envManifest.execution.error = error.toString(); logger.error(error); + const {tree, ...context} = envManifest; exhaust(tree, context, outputOptions); } } From 85190d3ad780bea73f3a1c67878f5ac0b8d91b7c Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 8 May 2024 23:40:02 +0400 Subject: [PATCH 14/20] feat(util): add exhaust error to errors --- src/util/errors.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/errors.ts b/src/util/errors.ts index 9781b1f5d..9c9714ed2 100644 --- a/src/util/errors.ts +++ b/src/util/errors.ts @@ -1,5 +1,6 @@ const CUSTOM_ERRORS = [ 'CliInputError', + 'ExhaustError', 'ManifestValidationError', 'ModuleInitializationError', 'InputValidationError', From 1780c40e206e54d5fb1b233e47c59d2493e053ab Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 8 May 2024 23:40:49 +0400 Subject: [PATCH 15/20] feat(lib): introduce exhaust error --- src/lib/exhaust.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/exhaust.ts b/src/lib/exhaust.ts index 5ee631d91..5c80b0a5a 100644 --- a/src/lib/exhaust.ts +++ b/src/lib/exhaust.ts @@ -14,7 +14,7 @@ import {ExhaustPluginInterface} from '../types/exhaust-plugin-interface'; import {Context} from '../types/manifest'; import {Options} from '../types/process-args'; -const {ModuleInitializationError} = ERRORS; +const {ExhaustError} = ERRORS; const {INVALID_EXHAUST_PLUGIN} = STRINGS; /** @@ -35,7 +35,7 @@ const initializeExhaustPlugin = (name: string): ExhaustPluginInterface => { case 'csv-raw': return ExportCSVRaw(); default: - throw new ModuleInitializationError(INVALID_EXHAUST_PLUGIN(name)); + throw new ExhaustError(INVALID_EXHAUST_PLUGIN(name)); } }; From c109ffe6966662e3579921b8e99b95a1b0e08b7e Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 8 May 2024 23:43:03 +0400 Subject: [PATCH 16/20] fix(builtins): introduce exhaust error to builtins --- src/builtins/export-csv-raw.ts | 8 +++----- src/builtins/export-csv.ts | 8 ++++---- src/builtins/export-yaml.ts | 5 ++--- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/builtins/export-csv-raw.ts b/src/builtins/export-csv-raw.ts index 3e2420ab5..429d99c85 100644 --- a/src/builtins/export-csv-raw.ts +++ b/src/builtins/export-csv-raw.ts @@ -5,7 +5,7 @@ import {ERRORS} from '../util/errors'; import {ExhaustPluginInterface} from '../types/exhaust-plugin-interface'; import {Context} from '../types/manifest'; -const {WriteFileError, CliInputError} = ERRORS; +const {ExhaustError} = ERRORS; export const ExportCSVRaw = (): ExhaustPluginInterface => { /** @@ -127,9 +127,7 @@ export const ExportCSVRaw = (): ExhaustPluginInterface => { try { await fs.writeFile(`${outputPath}.csv`, content); } catch (error) { - throw new WriteFileError( - `Failed to write CSV to ${outputPath}: ${error}` - ); + throw new ExhaustError(`Failed to write CSV to ${outputPath}: ${error}`); } }; @@ -138,7 +136,7 @@ export const ExportCSVRaw = (): ExhaustPluginInterface => { */ const execute = async (tree: any, _context: Context, outputPath: string) => { if (!outputPath) { - throw new CliInputError('Output path is required.'); + throw new ExhaustError('Output path is required.'); } const [extractredFlatMap, extractedHeaders] = diff --git a/src/builtins/export-csv.ts b/src/builtins/export-csv.ts index 9e48ad1b0..efd2cc3cb 100644 --- a/src/builtins/export-csv.ts +++ b/src/builtins/export-csv.ts @@ -6,7 +6,7 @@ import {ERRORS} from '../util/errors'; import {Context} from '../types/manifest'; import {PluginParams} from '../types/interface'; -const {CliInputError} = ERRORS; +const {ExhaustError} = ERRORS; /** * Extension to IF that outputs the tree in a CSV format. @@ -20,7 +20,7 @@ export const ExportCSV = () => { const criteria = paths[paths.length - 1]; if (paths.length <= 1 || !criteria) { - throw new CliInputError( + throw new ExhaustError( 'CSV export criteria is not found in output path. Please append it after --output #.' ); } @@ -36,11 +36,11 @@ export const ExportCSV = () => { */ const validateOutputPath = (outputPath: string) => { if (!outputPath) { - throw new CliInputError('Output path is required.'); + throw new ExhaustError('Output path is required.'); } if (!outputPath.includes('#')) { - throw new CliInputError('Output path should contains `#`.'); + throw new ExhaustError('Output path should contain `#`.'); } return outputPath; diff --git a/src/builtins/export-yaml.ts b/src/builtins/export-yaml.ts index bedab254e..e242bbcc2 100644 --- a/src/builtins/export-yaml.ts +++ b/src/builtins/export-yaml.ts @@ -1,10 +1,9 @@ import {saveYamlFileAs} from '../util/yaml'; - import {ERRORS} from '../util/errors'; import {Context} from '../types/manifest'; -const {CliInputError} = ERRORS; +const {ExhaustError} = ERRORS; export const ExportYaml = () => { /** Takes string before hashtag. */ @@ -15,7 +14,7 @@ export const ExportYaml = () => { */ const execute = async (tree: any, context: Context, outputPath: string) => { if (!outputPath) { - throw new CliInputError('Output path is required.'); + throw new ExhaustError('Output path is required.'); } const outputFile = { From 88338db114a01fe44aaa44775921668eb0fb0e78 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 8 May 2024 23:44:29 +0400 Subject: [PATCH 17/20] test(lib): use exhaust error --- src/__tests__/unit/lib/exhaust.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/__tests__/unit/lib/exhaust.test.ts b/src/__tests__/unit/lib/exhaust.test.ts index 371ae9c0a..97db0b9dc 100644 --- a/src/__tests__/unit/lib/exhaust.test.ts +++ b/src/__tests__/unit/lib/exhaust.test.ts @@ -7,7 +7,7 @@ import {ERRORS} from '../../../util/errors'; import {STRINGS} from '../../../config'; -const {CliInputError, ModuleInitializationError} = ERRORS; +const {ExhaustError} = ERRORS; const {INVALID_EXHAUST_PLUGIN} = STRINGS; describe('lib/exhaust: ', () => { @@ -58,9 +58,9 @@ describe('lib/exhaust: ', () => { // @ts-ignore await exhaust(tree, context, {}); } catch (error) { - expect(error).toBeInstanceOf(CliInputError); + expect(error).toBeInstanceOf(ExhaustError); - if (error instanceof CliInputError) { + if (error instanceof ExhaustError) { expect(error.message).toEqual(expectedMessage); } } @@ -83,9 +83,9 @@ describe('lib/exhaust: ', () => { // @ts-ignore await exhaust(tree, context, {}); } catch (error) { - expect(error).toBeInstanceOf(ModuleInitializationError); + expect(error).toBeInstanceOf(ExhaustError); - if (error instanceof ModuleInitializationError) { + if (error instanceof ExhaustError) { expect(error.message).toEqual(expectedMessage); } } From e1faafcfbae0fa6c8d85b93dcfe5ce7667f0a77c Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 8 May 2024 23:45:24 +0400 Subject: [PATCH 18/20] test(lib): remove console.log --- src/__tests__/unit/lib/load.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/__tests__/unit/lib/load.test.ts b/src/__tests__/unit/lib/load.test.ts index bed5da780..fed158b92 100644 --- a/src/__tests__/unit/lib/load.test.ts +++ b/src/__tests__/unit/lib/load.test.ts @@ -73,8 +73,6 @@ describe('lib/load: ', () => { parameters: PARAMETERS, }; - console.log(JSON.stringify(result)); - expect(result).toEqual(expectedValue); }); From 22ca3b63767f40b1c6e9c7752425551c9dd7e052 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 8 May 2024 23:46:12 +0400 Subject: [PATCH 19/20] test(builtins): change errors to exhaust --- src/__tests__/unit/builtins/export-csv-raw.test.ts | 8 ++++---- src/__tests__/unit/builtins/export-csv.test.ts | 14 +++++++------- src/__tests__/unit/builtins/export-yaml.test.ts | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/__tests__/unit/builtins/export-csv-raw.test.ts b/src/__tests__/unit/builtins/export-csv-raw.test.ts index c47959704..1ebf9e45e 100644 --- a/src/__tests__/unit/builtins/export-csv-raw.test.ts +++ b/src/__tests__/unit/builtins/export-csv-raw.test.ts @@ -6,7 +6,7 @@ import {ERRORS} from '../../../util/errors'; import {tree, context, outputs} from '../../../__mocks__/builtins/export-csv'; -const {CliInputError, WriteFileError} = ERRORS; +const {ExhaustError} = ERRORS; jest.mock('fs/promises', () => ({ __esModule: true, @@ -50,7 +50,7 @@ describe('builtins/export-csv-raw: ', () => { await expect( exportCSVRaw.execute(tree, context, outputPath) ).rejects.toThrow( - new WriteFileError( + new ExhaustError( 'Failed to write CSV to output#carbon: Could not write CSV file.' ) ); @@ -65,8 +65,8 @@ describe('builtins/export-csv-raw: ', () => { try { await exportCSVRaw.execute(tree, context, outputPath); } catch (error) { - expect(error).toBeInstanceOf(CliInputError); - expect(error).toEqual(new CliInputError('Output path is required.')); + expect(error).toBeInstanceOf(ExhaustError); + expect(error).toEqual(new ExhaustError('Output path is required.')); } }); }); diff --git a/src/__tests__/unit/builtins/export-csv.test.ts b/src/__tests__/unit/builtins/export-csv.test.ts index 26c1f194e..94414a970 100644 --- a/src/__tests__/unit/builtins/export-csv.test.ts +++ b/src/__tests__/unit/builtins/export-csv.test.ts @@ -13,7 +13,7 @@ import { aggregation, } from '../../../__mocks__/builtins/export-csv'; -const {CliInputError} = ERRORS; +const {ExhaustError} = ERRORS; jest.mock('fs/promises', () => ({ writeFile: jest.fn<() => Promise>().mockResolvedValue(), @@ -197,8 +197,8 @@ describe('builtins/export-csv: ', () => { try { await exportCSV.execute(tree, context, outputPath); } catch (error) { - expect(error).toBeInstanceOf(CliInputError); - expect(error).toEqual(new CliInputError('Output path is required.')); + expect(error).toBeInstanceOf(ExhaustError); + expect(error).toEqual(new ExhaustError('Output path is required.')); } }); @@ -210,9 +210,9 @@ describe('builtins/export-csv: ', () => { try { await exportCSV.execute(tree, context, outputPath); } catch (error) { - expect(error).toBeInstanceOf(CliInputError); + expect(error).toBeInstanceOf(ExhaustError); expect(error).toEqual( - new CliInputError('Output path should contains `#`.') + new ExhaustError('Output path should contain `#`.') ); } }); @@ -225,9 +225,9 @@ describe('builtins/export-csv: ', () => { try { await exportCSV.execute(tree, context, outputPath); } catch (error) { - expect(error).toBeInstanceOf(CliInputError); + expect(error).toBeInstanceOf(ExhaustError); expect(error).toEqual( - new CliInputError( + new ExhaustError( 'CSV export criteria is not found in output path. Please append it after --output #.' ) ); diff --git a/src/__tests__/unit/builtins/export-yaml.test.ts b/src/__tests__/unit/builtins/export-yaml.test.ts index e54c22972..a2157e4ef 100644 --- a/src/__tests__/unit/builtins/export-yaml.test.ts +++ b/src/__tests__/unit/builtins/export-yaml.test.ts @@ -8,7 +8,7 @@ jest.mock('../../../util/yaml', () => ({ saveYamlFileAs: jest.fn(), })); -const {CliInputError} = ERRORS; +const {ExhaustError} = ERRORS; describe('builtins/export-yaml: ', () => { describe('ExportYaml: ', () => { @@ -38,8 +38,8 @@ describe('builtins/export-yaml: ', () => { try { await exportYaml.execute({}, context, ''); } catch (error) { - expect(error).toBeInstanceOf(CliInputError); - expect(error).toEqual(new CliInputError('Output path is required.')); + expect(error).toBeInstanceOf(ExhaustError); + expect(error).toEqual(new ExhaustError('Output path is required.')); } }); }); From 6185ee0200741f3047020bd3d3d25ed03d42b5d1 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 8 May 2024 23:47:05 +0400 Subject: [PATCH 20/20] feat(src): handle exhaust errors --- src/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 0d52d048e..17eec0334 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,14 +31,17 @@ const impactEngine = async () => { const pluginStorage = await initialize(context.initialize.plugins); const computedTree = await compute(tree, {context, pluginStorage}); const aggregatedTree = aggregate(computedTree, context.aggregation); - exhaust(aggregatedTree, context, outputOptions); + await exhaust(aggregatedTree, context, outputOptions); } catch (error) { if (error instanceof Error) { envManifest.execution.status = 'fail'; envManifest.execution.error = error.toString(); logger.error(error); const {tree, ...context} = envManifest; - exhaust(tree, context, outputOptions); + + if (error.name !== 'ExhaustError') { + exhaust(tree, context, outputOptions); + } } } };