Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infer manifest types #505

Merged
merged 15 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/config/strings.ts
Original file line number Diff line number Diff line change
@@ -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.',
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
9 changes: 5 additions & 4 deletions src/lib/aggregate.ts
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}
Expand Down
15 changes: 4 additions & 11 deletions src/lib/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ContextTreeParams> => {
const safeManifest = await openYamlFileAsObject<Manifest>(inputPath);
const {name, description, tags, params, aggregation, initialize, tree} =
validateManifest(safeManifest);
export const load = async (inputPath: string, paramPath?: string) => {
const rawManifest = await openYamlFileAsObject<any>(inputPath);
const {tree, ...context} = validateManifest(rawManifest);
const parametersFromCli =
paramPath && (await readAndParseJson<Parameters>(paramPath)); // todo: validate json
const parameters = parametersFromCli || PARAMETERS;

return {
tree,
context: {name, description, tags, params, aggregation, initialize},
context,
parameters,
};
};
4 changes: 2 additions & 2 deletions src/lib/parameterize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);

Expand Down
2 changes: 0 additions & 2 deletions src/types/aggregation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
export type AggregationResult = Record<string, number>;

export const AGGREGATION_METHODS = ['horizontal', 'vertical', 'both'] as const;

export type AggregationMethodsNames = (typeof AGGREGATION_METHODS)[number];
8 changes: 0 additions & 8 deletions src/types/load.ts

This file was deleted.

51 changes: 9 additions & 42 deletions src/types/manifest.ts
Original file line number Diff line number Diff line change
@@ -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<string, any>;
method: string;
path: string;
};
type Manifest = z.infer<typeof manifestSchema>;

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<Manifest['aggregation'], {}>;

export type Manifest = Context & {
tree: {
children: any;
};
};
export type Context = Omit<Manifest, 'tree'>;

export type OutputManifest = Manifest & {
'aggregated-outputs'?: AggregationResult;
'if-version'?: string | null | undefined;
};
export type ManifestParameter = Extract<Manifest['params'], {}>[number];
17 changes: 3 additions & 14 deletions src/types/parameters.ts
Original file line number Diff line number Diff line change
@@ -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<ManifestParameter, 'name'>;

export type Parameters = Record<string, ParameterProps>;
2 changes: 1 addition & 1 deletion src/util/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
35 changes: 19 additions & 16 deletions src/util/validations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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(),
Expand All @@ -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`.
Expand All @@ -80,6 +79,9 @@ export const validate = <T>(
return validationResult.data;
};

/**
* Beautify error message from zod issue.
*/
const prettifyErrorMessage = (issues: string) => {
const issuesArray = JSON.parse(issues);

Expand All @@ -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}.`;
});
};
Loading