diff --git a/package-lock.json b/package-lock.json index 3061f581e..1c1305b3c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@grnsft/if", - "version": "v0.2.0", + "version": "v0.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@grnsft/if", - "version": "v0.2.0", + "version": "v0.2.1", "license": "MIT", "dependencies": { "@commitlint/cli": "^18.6.0", diff --git a/src/config/strings.ts b/src/config/strings.ts index 4b959b043..3113b3e38 100644 --- a/src/config/strings.ts +++ b/src/config/strings.ts @@ -1,4 +1,4 @@ -import {ManifestParameter} from '../types/parameters'; +import {ManifestParameter} from '../types/manifest'; export const STRINGS = { FILE_IS_NOT_YAML: 'Provided manifest is not in yaml format.', diff --git a/src/index.ts b/src/index.ts index f5d7dd48d..83e0db500 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,6 +13,8 @@ import {logger} from './util/logger'; import {STRINGS} from './config'; +const packageJson = require('../package.json'); + const {CliInputError} = ERRORS; const {DISCLAIMER_MESSAGE, SOMETHING_WRONG} = STRINGS; @@ -29,6 +31,7 @@ const impactEngine = async () => { const plugins = await initalize(context.initialize.plugins); const computedTree = await compute(tree, {context, plugins}); const aggregatedTree = aggregate(computedTree, context.aggregation); + context['if-version'] = packageJson.version; exhaust(aggregatedTree, context, outputPath); return; diff --git a/src/lib/aggregate.ts b/src/lib/aggregate.ts index 1e3e508cf..a841e1830 100644 --- a/src/lib/aggregate.ts +++ b/src/lib/aggregate.ts @@ -1,7 +1,7 @@ import {aggregateInputsIntoOne} from '../util/aggregation-helper'; import {PluginParams} from '../types/interface'; -import {AggregationParams} from '../types/manifest'; +import {AggregationParams, AggregationParamsSure} from '../types/manifest'; /** * Gets `i`th element from all children outputs and collects them in single array. @@ -43,8 +43,9 @@ const temporalAggregation = (node: any, metrics: string[]) => { * The outputs of the grouping node are the aggregated time bucketed outputs of it's children. * 5. Now a grouping node has it's own outputs, it can horizotnally aggregate them. */ -const aggregateNode = (node: any, aggregationParams: AggregationParams) => { - const {metrics, type} = aggregationParams; +const aggregateNode = (node: any, aggregationParams: AggregationParamsSure) => { + const metrics = aggregationParams!.metrics; + const type = aggregationParams!.type; if (node.children) { for (const child in node.children) { @@ -69,7 +70,7 @@ const aggregateNode = (node: any, aggregationParams: AggregationParams) => { * If aggregation is disabled, then returns given `tree`. * Otherwise creates copy of the tree, then applies aggregation to it. */ -export const aggregate = (tree: any, aggregationParams?: AggregationParams) => { +export const aggregate = (tree: any, aggregationParams: AggregationParams) => { if (!aggregationParams || !aggregationParams.type) { return tree; } diff --git a/src/lib/load.ts b/src/lib/load.ts index 3f66438ed..aa2fc9da6 100644 --- a/src/lib/load.ts +++ b/src/lib/load.ts @@ -3,29 +3,22 @@ import {openYamlFileAsObject} from '../util/yaml'; import {readAndParseJson} from '../util/json'; import {PARAMETERS} from '../config'; - -import {Manifest} from '../types/manifest'; -import {ContextTreeParams} from '../types/load'; import {Parameters} from '../types/parameters'; /** * Parses manifest file as an object. Checks if parameter file is passed via CLI, then loads it too. * Returns context, tree and parameters (either the default one, or from CLI). */ -export const load = async ( - inputPath: string, - paramPath?: string -): Promise => { - const safeManifest = await openYamlFileAsObject(inputPath); - const {name, description, tags, params, aggregation, initialize, tree} = - validateManifest(safeManifest); +export const load = async (inputPath: string, paramPath?: string) => { + const rawManifest = await openYamlFileAsObject(inputPath); + const {tree, ...context} = validateManifest(rawManifest); const parametersFromCli = paramPath && (await readAndParseJson(paramPath)); // todo: validate json const parameters = parametersFromCli || PARAMETERS; return { tree, - context: {name, description, tags, params, aggregation, initialize}, + context, parameters, }; }; diff --git a/src/lib/parameterize.ts b/src/lib/parameterize.ts index c30dc6506..09ca1c154 100644 --- a/src/lib/parameterize.ts +++ b/src/lib/parameterize.ts @@ -2,7 +2,7 @@ import {logger} from '../util/logger'; import {STRINGS, PARAMETERS} from '../config'; -import {ManifestParameter} from '../types/parameters'; +import {ManifestParameter} from '../types/manifest'; const {REJECTING_OVERRIDE} = STRINGS; @@ -33,7 +33,7 @@ const Parametrize = () => { parameters: any ) => { if (contextParameters) { - contextParameters.forEach((param: any) => { + contextParameters.forEach(param => { if (`${param.name}` in parameters) { logger.warn(REJECTING_OVERRIDE); diff --git a/src/types/aggregation.ts b/src/types/aggregation.ts index eba8c9443..d15322d29 100644 --- a/src/types/aggregation.ts +++ b/src/types/aggregation.ts @@ -1,5 +1,3 @@ export type AggregationResult = Record; export const AGGREGATION_METHODS = ['horizontal', 'vertical', 'both'] as const; - -export type AggregationMethodsNames = (typeof AGGREGATION_METHODS)[number]; diff --git a/src/types/load.ts b/src/types/load.ts deleted file mode 100644 index 30c76d0af..000000000 --- a/src/types/load.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {Context} from './manifest'; -import {Parameters} from './parameters'; - -export type ContextTreeParams = { - tree: any; - context: Context; - parameters: Parameters; -}; diff --git a/src/types/manifest.ts b/src/types/manifest.ts index 4b9b1cf2a..0e43f4763 100644 --- a/src/types/manifest.ts +++ b/src/types/manifest.ts @@ -1,49 +1,16 @@ -import {AggregationMethodsNames, AggregationResult} from './aggregation'; -import {ManifestParameter} from './parameters'; +import {z} from 'zod'; -type Tags = - | { - kind?: string; - complexity?: string; - category?: string; - } - | null - | undefined; +import {manifestSchema} from '../util/validations'; -export type PluginOptions = { - 'global-config'?: Record; - method: string; - path: string; -}; +type Manifest = z.infer; -export type GlobalPlugins = { - [key: string]: PluginOptions; -}; +export type GlobalPlugins = Manifest['initialize']['plugins']; -export type AggregationParams = { - metrics: string[]; - type: AggregationMethodsNames; -}; +export type PluginOptions = GlobalPlugins[string]; -export type Context = { - name: string; - description: string | null | undefined; - tags: Tags; - params?: ManifestParameter[] | undefined | null; - initialize: { - plugins: GlobalPlugins; - outputs?: string[]; - }; - aggregation?: AggregationParams; -}; +export type AggregationParams = Manifest['aggregation']; +export type AggregationParamsSure = Extract; -export type Manifest = Context & { - tree: { - children: any; - }; -}; +export type Context = Omit; -export type OutputManifest = Manifest & { - 'aggregated-outputs'?: AggregationResult; - 'if-version'?: string | null | undefined; -}; +export type ManifestParameter = Extract[number]; diff --git a/src/types/parameters.ts b/src/types/parameters.ts index bd8b8fb4d..7434777ce 100644 --- a/src/types/parameters.ts +++ b/src/types/parameters.ts @@ -1,18 +1,7 @@ -export const AGGREGATION_TYPES = ['sum', 'none', 'avg'] as const; - -type AggregationTypes = (typeof AGGREGATION_TYPES)[number]; +import {ManifestParameter} from './manifest'; -type ParameterProps = { - unit: string; - description: string; - aggregation: AggregationTypes; -}; +export const AGGREGATION_TYPES = ['sum', 'none', 'avg'] as const; -export type ManifestParameter = { - name: string; - unit: string; - description: string; - aggregation: AggregationTypes; -}; +type ParameterProps = Omit; export type Parameters = Record; diff --git a/src/util/logger.ts b/src/util/logger.ts index b33233b8f..83126a954 100644 --- a/src/util/logger.ts +++ b/src/util/logger.ts @@ -3,7 +3,7 @@ import * as winston from 'winston'; const {combine, timestamp, printf, colorize, align} = winston.format; /** - * Winston logger + * Winston logger instance. */ export const logger = winston.createLogger({ format: combine( diff --git a/src/util/validations.ts b/src/util/validations.ts index 887a229bf..df947b589 100644 --- a/src/util/validations.ts +++ b/src/util/validations.ts @@ -4,23 +4,30 @@ import {ERRORS} from './errors'; import {AGGREGATION_METHODS} from '../types/aggregation'; import {AGGREGATION_TYPES} from '../types/parameters'; -import {Manifest} from '../types/manifest'; const {ManifestValidationError, InputValidationError} = ERRORS; /** * Validation schema for manifests. */ -const manifestValidation = z.object({ +export const manifestSchema = z.object({ name: z.string(), - 'if-version': z.string().optional().nullable(), - description: z.string().nullable().default(''), + description: z.string().optional().nullable(), + tags: z + .object({ + kind: z.string().optional().nullable(), + complexity: z.string().optional().nullable(), + category: z.string().optional().nullable(), + }) + .optional() + .nullable(), aggregation: z .object({ metrics: z.array(z.string()), type: z.enum(AGGREGATION_METHODS), }) - .optional(), + .optional() + .nullable(), params: z .array( z.object({ @@ -32,14 +39,6 @@ const manifestValidation = z.object({ ) .optional() .nullable(), - tags: z - .object({ - kind: z.string().optional(), - complexity: z.string().optional(), - category: z.string().optional(), - }) - .nullable() - .default({}), initialize: z.object({ plugins: z.record( z.string(), @@ -52,14 +51,14 @@ const manifestValidation = z.object({ outputs: z.array(z.string()).optional(), }), tree: z.record(z.string(), z.any()), + 'if-version': z.string().optional().nullable(), }); /** * Validates given `manifest` object to match pattern. */ -export const validateManifest = (manifest: Manifest) => { - return validate(manifestValidation, manifest, ManifestValidationError); -}; +export const validateManifest = (manifest: any) => + validate(manifestSchema, manifest, ManifestValidationError); /** * Validates given `object` with given `schema`. @@ -80,6 +79,9 @@ export const validate = ( return validationResult.data; }; +/** + * Beautify error message from zod issue. + */ const prettifyErrorMessage = (issues: string) => { const issuesArray = JSON.parse(issues); @@ -93,6 +95,7 @@ const prettifyErrorMessage = (issues: string) => { if (code === 'custom') { return `${message.toLowerCase()}. Error code: ${code}.`; } + return `"${fullPath}" parameter is ${message.toLowerCase()}. Error code: ${code}.`; }); };