diff --git a/README.md b/README.md index d8eed586f..466d5191b 100644 --- a/README.md +++ b/README.md @@ -56,21 +56,21 @@ Then installing some models: npm install -g "@grnsft/if-models" ``` -Then create an `impl` file that describes your application (see our docs for a detailed explanation). +Then create an `manifest` file that describes your application (see our docs for a detailed explanation). -Then, run `impact-engine` using the following command: +Then, run `if` using the following command: ```sh -impact-engine --impl +if --manifest ``` You can also add an optional savepath for your output yaml (if you do not provide one, the output will be printed to the console): ```sh -impact-engine --impl --ompl +if --manifest --output ``` -The `impact-engine` CLI tool will configure and run the models defined in your input `yaml` (`impl`) and return the results as an output `yaml` (`ompl`). +The `if` CLI tool will configure and run the models defined in your input `yaml` (`manifest`) and return the results as an output `yaml` (`output`). ## Documentation @@ -79,7 +79,7 @@ Please read our documentation at [if.greensoftware.foundation](https://if.greens ## Video walk-through -Watch this video to learn how to create and run an `impl`. +Watch this video to learn how to create and run a `manifest`. [![Watch the walk-through video](https://i3.ytimg.com/vi/R-6eDM8AsvY/maxresdefault.jpg)](https://youtu.be/GW37Qd4AQbU) diff --git a/jest.config.js b/jest.config.js index f1e6d39b1..ed24da708 100644 --- a/jest.config.js +++ b/jest.config.js @@ -12,7 +12,7 @@ module.exports = { }, modulePathIgnorePatterns: [ './build', - './src/__tests__/unit/lib/impls', + './src/__tests__/unit/lib/manifest', './src/__tests__/integration/helpers', './src/__tests__/integration/test-data', ], diff --git a/scripts/impact-test.sh b/scripts/impact-test.sh index 98ffccd5a..b152f1438 100755 --- a/scripts/impact-test.sh +++ b/scripts/impact-test.sh @@ -7,6 +7,6 @@ do echo "" echo executing $file, outfile is ${file#"$prefix"} echo "" -npx ts-node impact-engine.ts --impl $file --ompl examples/ompls/${file#"$prefix"} +npx ts-node ./src --manifest $file --output examples/ompls/${file#"$prefix"} done exit 0 diff --git a/scripts/run-yamls.sh b/scripts/run-yamls.sh index ea878fa23..8ab79914c 100644 --- a/scripts/run-yamls.sh +++ b/scripts/run-yamls.sh @@ -1,8 +1,8 @@ #!/bin/bash -echo 'Running all impls' +echo 'Running all manifests' for f in ./examples/impls/test/*.yml; do echo "Processing $f file..."; - npm run impact-engine -- --impl $f + npm run if -- --manifest $f done diff --git a/src/__mocks__/model-universe/index.ts b/src/__mocks__/model-universe/index.ts deleted file mode 100644 index 4f44b33b1..000000000 --- a/src/__mocks__/model-universe/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -import {STRINGS} from '../../config'; - -import { - ImplInitializeModel, - InitalizedModels, -} from '../../types/models-universe'; -import {ModelPluginInterface} from '../../types/model-interface'; - -const {NOT_INITIALIZED_MODEL} = STRINGS; - -export class MockModel implements ModelPluginInterface { - configure(): Promise { - return Promise.resolve(this); - } - execute(inputs: any): Promise { - if (inputs[0].carbon) { - return Promise.resolve(inputs); - } - - return Promise.resolve([{data: 'mock-data'}]); - } -} - -/** - * Models Initialization Lifecycle. - */ -export class ModelsUniverse { - /** - * Models list. - */ - public initalizedModels: InitalizedModels = {}; - - /** - * Gets model class by provided `name` param. - */ - private handBuiltinModel() { - return MockModel; - } - - /** - * Gets model based on `name` and `kind` params. - */ - private handModelByCriteria() { - return this.handBuiltinModel(); - } - - /** - * Initializes and registers model. - */ - public bulkWriteDown(models: ImplInitializeModel[]) { - const {name} = models[0]; - - const Model = this.handModelByCriteria(); - - const callback = async () => { - const initalizedModel = await new Model().configure(); - - return initalizedModel; - }; - - this.initalizedModels = { - ...this.initalizedModels, - [name]: callback, - }; - - return this.initalizedModels; - } - - /** - * Returns existing model by `name`. - */ - public async getInitializedModel(modelName: string, config: any) { - if (this.initalizedModels[modelName]) { - return await this.initalizedModels[modelName](config); - } - - throw new Error(NOT_INITIALIZED_MODEL(modelName)); - } -} diff --git a/src/__tests__/unit/lib/impls/basic.ts b/src/__tests__/unit/lib/manifest/basic.ts similarity index 82% rename from src/__tests__/unit/lib/impls/basic.ts rename to src/__tests__/unit/lib/manifest/basic.ts index 9d814da1f..5729b069d 100644 --- a/src/__tests__/unit/lib/impls/basic.ts +++ b/src/__tests__/unit/lib/manifest/basic.ts @@ -1,6 +1,6 @@ -import {Impl} from '../../../../types/impl'; +import {Manifest} from '../../../../types/manifest'; -export const impl: Impl = { +export const manifest: Manifest = { name: 'gsf-demo', description: 'Hello', tags: { @@ -9,19 +9,18 @@ export const impl: Impl = { category: 'cloud', }, initialize: { - models: [ - { - name: 'mock-name', + plugins: { + 'mock-name': { model: 'MockaviztaModel', path: 'mock-path', - config: { + 'global-config': { allocation: 'LINEAR', verbose: true, }, }, - ], + }, }, - graph: { + tree: { children: { 'front-end': { pipeline: ['mock-name'], diff --git a/src/__tests__/unit/lib/impls/nested.ts b/src/__tests__/unit/lib/manifest/nested.ts similarity index 91% rename from src/__tests__/unit/lib/impls/nested.ts rename to src/__tests__/unit/lib/manifest/nested.ts index 32e7abd1a..fa1d00f0c 100644 --- a/src/__tests__/unit/lib/impls/nested.ts +++ b/src/__tests__/unit/lib/manifest/nested.ts @@ -1,6 +1,6 @@ -import {Impl} from '../../../../types/impl'; +import {Manifest} from '../../../../types/manifest'; -export const implNested: Impl = { +export const manifestNested: Manifest = { name: 'nesting-demo', description: null, tags: { @@ -9,19 +9,18 @@ export const implNested: Impl = { category: 'on-premise', }, initialize: { - models: [ - { - name: 'mockavizta', + plugins: { + mockavizta: { model: 'MockaviztaModel', path: 'mock-path', - config: { + 'global-config': { allocation: 'LINEAR', verbose: true, }, }, - ], + }, }, - graph: { + tree: { children: { 'child-0': { config: { @@ -76,7 +75,7 @@ export const implNested: Impl = { }, }; -export const implNestedNoConfig: Impl = { +export const manifestNestedNoConfig: Manifest = { name: 'nesting-demo', description: null, tags: { @@ -85,19 +84,18 @@ export const implNestedNoConfig: Impl = { category: 'on-premise', }, initialize: { - models: [ - { - name: 'mockavizta', + plugins: { + mockavizta: { model: 'MockaviztaModel', path: 'mock-path', - config: { + 'global-config': { allocation: 'LINEAR', verbose: true, }, }, - ], + }, }, - graph: { + tree: { children: { 'child-0': { config: { diff --git a/src/__tests__/unit/util/args.test.ts b/src/__tests__/unit/util/args.test.ts index a54f07733..38084adc4 100644 --- a/src/__tests__/unit/util/args.test.ts +++ b/src/__tests__/unit/util/args.test.ts @@ -4,14 +4,14 @@ jest.mock('ts-command-line-args', () => ({ switch (process.env.result) { case 'error': return {}; - case 'impl': + case 'manifest': return { - impl: 'impl-mock.yml', + manifest: 'manifest-mock.yml', }; - case 'impl-ompl': + case 'manifest-output': return { - impl: 'impl-mock.yml', - ompl: 'ompl-mock.yml', + manifest: 'manifest-mock.yml', + output: 'output-mock.yml', }; case 'help': return { @@ -19,12 +19,12 @@ jest.mock('ts-command-line-args', () => ({ }; case 'not-yaml': return { - impl: 'mock.notyaml', + manifest: 'mock.notyaml', }; default: return { - impl: 'mock-impl.yaml', - ompl: 'mock-ompl', + manifest: 'mock-manifest.yaml', + output: 'mock-output', }; } }, @@ -39,7 +39,7 @@ import {STRINGS} from '../../../config'; const {CliInputError} = ERRORS; -const {IMPL_IS_MISSING, FILE_IS_NOT_YAML} = STRINGS; +const {MANIFEST_IS_MISSING, FILE_IS_NOT_YAML} = STRINGS; describe('util/args: ', () => { const originalEnv = process.env; @@ -55,40 +55,40 @@ describe('util/args: ', () => { } catch (error) { if (error instanceof Error) { expect(error).toBeInstanceOf(CliInputError); - expect(error.message).toEqual(IMPL_IS_MISSING); + expect(error.message).toEqual(MANIFEST_IS_MISSING); } } }); - it('returns impl path.', () => { + it('returns manifest path.', () => { expect.assertions(1); - process.env.result = 'impl'; + process.env.result = 'manifest'; const result = parseArgs(); const processRunningPath = process.cwd(); - const implPath = 'impl-mock.yml'; + const manifestPath = 'manifest-mock.yml'; const expectedResult = { - inputPath: path.normalize(`${processRunningPath}/${implPath}`), + inputPath: path.normalize(`${processRunningPath}/${manifestPath}`), }; expect(result).toEqual(expectedResult); }); - it('returns impl and ompl path.', () => { + it('returns manifest and output path.', () => { expect.assertions(1); - process.env.result = 'impl-ompl'; + process.env.result = 'manifest-output'; const result = parseArgs(); const processRunningPath = process.cwd(); - const implPath = 'impl-mock.yml'; - const omplPath = 'ompl-mock.yml'; + const manifestPath = 'manifest-mock.yml'; + const outputPath = 'output-mock.yml'; const expectedResult = { - inputPath: path.normalize(`${processRunningPath}/${implPath}`), - outputPath: path.normalize(`${processRunningPath}/${omplPath}`), + inputPath: path.normalize(`${processRunningPath}/${manifestPath}`), + outputPath: path.normalize(`${processRunningPath}/${outputPath}`), }; expect(result).toEqual(expectedResult); diff --git a/src/config/config.ts b/src/config/config.ts index 61c252e1d..3ed2ad463 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -1,21 +1,21 @@ import {ArgumentConfig} from 'ts-command-line-args'; -import {impactProcessArgs} from '../types/process-args'; +import {ManifestProcessArgs} from '../types/process-args'; export const CONFIG = { impact: { ARGS: { - impl: { + manifest: { type: String, optional: true, alias: 'i', - description: 'Path to an input IMPL file.', + description: 'Path to an input manifest file.', }, - ompl: { + output: { type: String, optional: true, description: - 'Path to the output IMPL file where the results as saved, if none is provided it prints to stdout.', + 'Path to the output file where the results as saved, if none is provided it prints to stdout.', }, 'override-params': { type: String, @@ -41,15 +41,15 @@ export const CONFIG = { alias: 'h', description: 'Prints this usage guide.', }, - } as ArgumentConfig, + } as ArgumentConfig, HELP: `impact - -impl [path to the input impl file] - -ompl [path to the output impl file] + -manifest [path to the input file] + -output [path to the output file] -format [yaml|csv] -verbose -help - impl: path to an input IMPL file - ompl: path to the output IMPL file where the results as saved, if none is provided it prints to stdout. + manifest: path to an input manifest + output: path to the output file where the results as saved, if none is provided it prints to stdout. format: the output file format. default to yaml but if csv is specified then it formats the outputs as a csv file for loading into another program. verbose: how much information to output about the calculation to aid investigation and debugging. help: prints out the above help instruction. diff --git a/src/config/strings.ts b/src/config/strings.ts index ab8700d8a..8849badab 100644 --- a/src/config/strings.ts +++ b/src/config/strings.ts @@ -1,6 +1,6 @@ export const STRINGS = { - FILE_IS_NOT_YAML: 'Provided impl file is not in yaml format.', - IMPL_IS_MISSING: 'Impl file is missing.', + FILE_IS_NOT_YAML: 'Provided manifest is not in yaml format.', + MANIFEST_IS_MISSING: 'Manifest is missing.', MISSING_CLASSNAME: "Initalization param 'model' is missing.", MISSING_PATH: "Initalization param 'path' is missing.", OVERRIDE_WARNING: diff --git a/src/lib/load.ts b/src/lib/load.ts index f8c0d3fd2..ba6e4ccf7 100644 --- a/src/lib/load.ts +++ b/src/lib/load.ts @@ -10,7 +10,7 @@ import {ContextTree} from '../types/load'; export const load = async (inputPath: string): Promise => { const safeManifest = await openYamlFileAsObject(inputPath); const {initialize, aggregation, params, tags, description, name, tree} = - validateManifest(safeManifest); // TODO: should be moved outside as separate call + validateManifest(safeManifest); return { tree, diff --git a/src/models/README.md b/src/models/README.md index 0d40b2676..e03cd3d64 100644 --- a/src/models/README.md +++ b/src/models/README.md @@ -201,8 +201,8 @@ const results = timeSyncModel.execute([ IEF users will typically call the model as part of a pipeline defined in an `impl` file. In this case, instantiating and configuring the model is handled by -`impact-engine` and does not have to be done explicitly by the user. -The following is an example `impl` that calls `time-sync`: +`if` and does not have to be done explicitly by the user. +The following is an example `manifest` that calls `time-sync`: ```yaml name: time-sync-demo diff --git a/src/types/process-args.ts b/src/types/process-args.ts index 9a90f7fe1..15af39202 100644 --- a/src/types/process-args.ts +++ b/src/types/process-args.ts @@ -1,6 +1,6 @@ -export interface impactProcessArgs { - impl?: string; - ompl?: string; +export interface ManifestProcessArgs { + manifest?: string; + output?: string; 'override-params'?: string; format?: string; verbose?: boolean; diff --git a/src/util/args.ts b/src/util/args.ts index 08cad95b2..b2fcb07ef 100644 --- a/src/util/args.ts +++ b/src/util/args.ts @@ -6,22 +6,21 @@ import {ERRORS} from './errors'; import {CONFIG, STRINGS} from '../config'; -import {impactProcessArgs} from '../types/process-args'; +import {ManifestProcessArgs} from '../types/process-args'; const {CliInputError} = ERRORS; const {impact} = CONFIG; const {ARGS, HELP} = impact; -const {FILE_IS_NOT_YAML, IMPL_IS_MISSING} = STRINGS; +const {FILE_IS_NOT_YAML, MANIFEST_IS_MISSING} = STRINGS; /** * Validates process arguments - * @private */ const validateAndParseProcessArgs = () => { try { - return parse(ARGS); + return parse(ARGS); } catch (error) { if (error instanceof Error) { throw new CliInputError(error.message); @@ -32,23 +31,29 @@ const validateAndParseProcessArgs = () => { }; /** - * Prepends process path to fiven `filePath`. - * @private + * Prepends process path to given `filePath`. */ const prependFullFilePath = (filePath: string) => { const processRunningPath = process.cwd(); + if (path.isAbsolute(filePath)) { + return filePath; + } + return path.normalize(`${processRunningPath}/${filePath}`); }; /** - * Parses process argument, if it's `yaml` file, then returns it. - * Otherwise throws error. + * 1. Parses process arguments like `manifest`, `output`, `override-params` and `help`. + * 2. Checks if `help` param is provided, then logs help message and exits. + * 3. Otherwise checks if `manifest` param is there, then processes with checking if it's a yaml file. + * If it is, then returns object containing full path. + * 4. If params are missing or invalid, then rejects with `CliInputError`. */ export const parseArgs = () => { const { - impl, - ompl, + manifest, + output, 'override-params': overrideParams, help, } = validateAndParseProcessArgs(); @@ -58,11 +63,11 @@ export const parseArgs = () => { return; } - if (impl) { - if (checkIfFileIsYaml(impl)) { + if (manifest) { + if (checkIfFileIsYaml(manifest)) { return { - inputPath: prependFullFilePath(impl), - ...(ompl && {outputPath: prependFullFilePath(ompl)}), + inputPath: prependFullFilePath(manifest), + ...(output && {outputPath: prependFullFilePath(output)}), ...(overrideParams && {paramPath: overrideParams}), }; } @@ -70,5 +75,5 @@ export const parseArgs = () => { throw new CliInputError(FILE_IS_NOT_YAML); } - throw new CliInputError(IMPL_IS_MISSING); + throw new CliInputError(MANIFEST_IS_MISSING); }; diff --git a/src/util/errors.ts b/src/util/errors.ts index 07278cde0..d5631018e 100644 --- a/src/util/errors.ts +++ b/src/util/errors.ts @@ -1,7 +1,7 @@ const CUSTOM_ERRORS = [ 'CliInputError', 'FileNotFoundError', - 'ImplValidationError', + 'ManifestValidationError', 'InputValidationError', 'InvalidAggregationParams', 'InvalidGrouping', diff --git a/src/util/validations.ts b/src/util/validations.ts index 93930f008..a7faa060b 100644 --- a/src/util/validations.ts +++ b/src/util/validations.ts @@ -6,12 +6,12 @@ import {AGGREGATION_METHODS} from '../types/aggregation'; import {AGGREGATION_TYPES} from '../types/parameters'; import {Manifest} from '../types/manifest'; -const {ImplValidationError} = ERRORS; +const {ManifestValidationError} = ERRORS; /** - * Validation schema for impl files. + * Validation schema for manifests. */ -const implValidation = z.object({ +const manifestValidation = z.object({ name: z.string(), 'if-version': z.string().optional().nullable(), description: z.string().nullable().default(''), @@ -57,9 +57,9 @@ const implValidation = z.object({ * Validates given `manifest` object to match pattern. */ export const validateManifest = (manifest: Manifest) => { - const validatedImpl = implValidation.safeParse(manifest); + const safeManifest = manifestValidation.safeParse(manifest); - if (!validatedImpl.success) { + if (!safeManifest.success) { const prettifyErrorMessage = (issues: string) => { const issuesArray = JSON.parse(issues); @@ -74,10 +74,10 @@ export const validateManifest = (manifest: Manifest) => { }); }; - throw new ImplValidationError( - prettifyErrorMessage(validatedImpl.error.message) + throw new ManifestValidationError( + prettifyErrorMessage(safeManifest.error.message) ); } - return validatedImpl.data; + return safeManifest.data; };