Skip to content

Commit

Permalink
Merge pull request #996 from Green-Software-Foundation/explain-rework
Browse files Browse the repository at this point in the history
Update `explain` feature
  • Loading branch information
jmcook1186 committed Aug 29, 2024
2 parents 8dfcd90 + ee33f3e commit 79aad35
Show file tree
Hide file tree
Showing 4 changed files with 243 additions and 40 deletions.
196 changes: 175 additions & 21 deletions src/__tests__/if-run/lib/explain.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import {ERRORS} from '@grnsft/if-core/utils';

import {STRINGS} from '../../../common/config';

import {explain, addExplainData} from '../../../if-run/lib/explain';

const {ManifestValidationError} = ERRORS;
const {AGGREGATION_UNITS_NOT_MATCH, AGGREGATION_METHODS_NOT_MATCH} = STRINGS;

describe('lib/explain: ', () => {
it('successfully adds explain data if `inputs` and `outputs` of `metadata` are `undefined`.', () => {
it('missing explain data if `inputs` and `outputs` of `metadata` are `undefined`.', () => {
const mockData = {
pluginName: 'divide',
metadata: {kind: 'execute', inputs: undefined, outputs: undefined},
Expand All @@ -11,19 +18,11 @@ describe('lib/explain: ', () => {
method: 'Divide',
},
};
const expectedResult = {
divide: {
method: 'Divide',
path: 'builtin',
inputs: 'undefined',
outputs: 'undefined',
},
};

addExplainData(mockData);
const result = explain();
expect.assertions(1);
expect(result).toEqual(expectedResult);
expect(result).toEqual({});
});

it('successfully adds explain data if `inputs` and `outputs` of `metadata` are valid data.', () => {
Expand Down Expand Up @@ -56,36 +55,99 @@ describe('lib/explain: ', () => {
method: 'Sum',
},
};

const expectedResult = {
divide: {
method: 'Divide',
path: 'builtin',
inputs: 'undefined',
outputs: 'undefined',
'cpu/energy': {
plugins: ['sum'],
unit: 'kWh',
description: 'energy consumed by the cpu',
'aggregation-method': 'sum',
},
sum: {
method: 'Sum',
path: 'builtin',
'network/energy': {
plugins: ['sum'],
unit: 'kWh',
description: 'energy consumed by data ingress and egress',
'aggregation-method': 'sum',
},
'energy-sum': {
plugins: ['sum'],
unit: 'kWh',
description: 'sum of energy components',
'aggregation-method': 'sum',
},
};

// @ts-ignore
addExplainData(mockData);

const result = explain();

expect.assertions(1);
expect(result).toEqual(expectedResult);
});

it('successfully adds explain data if the parameter is using more than one plugin.', () => {
const mockData = {
pluginName: 'sum-energy',
metadata: {
kind: 'execute',
inputs: {
'cpu/energy': {
unit: 'kWh',
description: 'energy consumed by the cpu',
'aggregation-method': 'sum',
},
'network/energy': {
'memory/energy': {
unit: 'kWh',
description: 'energy consumed by data ingress and egress',
description: 'energy consumed by data from memory',
'aggregation-method': 'sum',
},
},
outputs: {
'energy-sum': {
'total/energy': {
unit: 'kWh',
description: 'sum of energy components',
'aggregation-method': 'sum',
},
},
},
pluginData: {
path: 'builtin',
method: 'Sum',
},
};

const expectedResult = {
'cpu/energy': {
plugins: ['sum', 'sum-energy'],
unit: 'kWh',
description: 'energy consumed by the cpu',
'aggregation-method': 'sum',
},
'network/energy': {
plugins: ['sum'],
unit: 'kWh',
description: 'energy consumed by data ingress and egress',
'aggregation-method': 'sum',
},
'energy-sum': {
plugins: ['sum'],
unit: 'kWh',
description: 'sum of energy components',
'aggregation-method': 'sum',
},
'memory/energy': {
plugins: ['sum-energy'],
unit: 'kWh',
description: 'energy consumed by data from memory',
'aggregation-method': 'sum',
},
'total/energy': {
plugins: ['sum-energy'],
unit: 'kWh',
description: 'sum of energy components',
'aggregation-method': 'sum',
},
};

// @ts-ignore
Expand All @@ -96,4 +158,96 @@ describe('lib/explain: ', () => {
expect.assertions(1);
expect(result).toEqual(expectedResult);
});

it('throws an error if `unit` of the parameter is not matched.', () => {
const mockData = {
pluginName: 'sum-of-energy',
metadata: {
kind: 'execute',
inputs: {
'cpu/energy': {
unit: 'co2q',
description: 'energy consumed by the cpu',
'aggregation-method': 'sum',
},
'memory/energy': {
unit: 'kWh',
description: 'energy consumed by data from memory',
'aggregation-method': 'sum',
},
},
outputs: {
'total/energy': {
unit: 'kWh',
description: 'sum of energy components',
'aggregation-method': 'sum',
},
},
},
pluginData: {
path: 'builtin',
method: 'Sum',
},
};

expect.assertions(2);
try {
// @ts-ignore
addExplainData(mockData);
explain();
} catch (error) {
if (error instanceof Error) {
expect(error).toBeInstanceOf(ManifestValidationError);
expect(error.message).toEqual(
AGGREGATION_UNITS_NOT_MATCH('cpu/energy')
);
}
}
});

it('throws an error if `aggregation-method` of the parameter is not matched.', () => {
const mockData = {
pluginName: 'sum-of-energy',
metadata: {
kind: 'execute',
inputs: {
'cpu/energy': {
unit: 'kWh',
description: 'energy consumed by the cpu',
'aggregation-method': 'avg',
},
'memory/energy': {
unit: 'kWh',
description: 'energy consumed by data from memory',
'aggregation-method': 'sum',
},
},
outputs: {
'total/energy': {
unit: 'kWh',
description: 'sum of energy components',
'aggregation-method': 'sum',
},
},
},
pluginData: {
path: 'builtin',
method: 'Sum',
},
};

expect.assertions(2);
try {
// @ts-ignore
addExplainData(mockData);
explain();
} catch (error) {
if (error instanceof Error) {
expect(error).toBeInstanceOf(ManifestValidationError);
expect(error.message).toEqual(
AGGREGATION_METHODS_NOT_MATCH('cpu/energy')
);
}
}
});
});
4 changes: 4 additions & 0 deletions src/common/config/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ Incubation projects are experimental, offer no support guarantee, have minimal g
SUCCESS_MESSAGE: 'The environment is successfully setup!',
MANIFEST_IS_MISSING: 'Manifest is missing.',
DIRECTORY_NOT_FOUND: 'Directory not found.',
AGGREGATION_UNITS_NOT_MATCH: (param: string) =>
`Your manifest uses two instances of ${param} with different units. Please check that you are using consistent units for ${param} throughout your manifest.`,
AGGREGATION_METHODS_NOT_MATCH: (param: string) =>
`Your manifest uses two instances of ${param} with different 'aggregation-method'. Please check that you are using right 'aggregation-method' for ${param} throughout your manifest.`,
};
73 changes: 54 additions & 19 deletions src/if-run/lib/explain.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,79 @@
import {ExplainParams} from '../types/explain';
import {ERRORS} from '@grnsft/if-core/utils';

import {STRINGS} from '../../common/config';

import {ExplainParams, ExplainStorageType} from '../types/explain';

const {ManifestValidationError} = ERRORS;
const {AGGREGATION_UNITS_NOT_MATCH, AGGREGATION_METHODS_NOT_MATCH} = STRINGS;

/**
* Retrieves stored explain data.
*/
export const explain = () => storeExplainData.plugins;
export const explain = () => storeExplainData.parameters;

/**
* Manages the storage of explain data.
*/
const storeExplainData = (() => {
let plugin = {};
let parameter: ExplainStorageType = {};

const pluginManager = {
get plugins() {
return plugin;
const parameterManager = {
get parameters() {
return parameter;
},
set plugins(value: object) {
plugin = value;
set parameters(value: ExplainStorageType) {
parameter = value;
},
};

return pluginManager;
return parameterManager;
})();

/**
* Adds new explain data to the stored explain data.
*/
export const addExplainData = (params: ExplainParams) => {
const {pluginName, pluginData, metadata} = params;
const plugin = {
[pluginName]: {
method: pluginData!.method,
path: pluginData!.path,
inputs: metadata?.inputs || 'undefined',
outputs: metadata?.outputs || 'undefined',
},
const parameterMetadata = pluginData?.['parameter-metadata'] || metadata;
const parameters = storeExplainData.parameters;
const allParameters = {
...parameterMetadata?.inputs,
...parameterMetadata?.outputs,
};

storeExplainData.plugins = {
...storeExplainData.plugins,
...plugin,
Object.entries(allParameters).forEach(([name, meta]) => {
const existingParameter = parameters[name];

if (parameters[name]?.plugins?.includes(pluginName)) {
return;
}

if (existingParameter) {
if (meta.unit !== existingParameter.unit) {
throw new ManifestValidationError(AGGREGATION_UNITS_NOT_MATCH(name));
}

if (
meta['aggregation-method'] !== existingParameter['aggregation-method']
) {
throw new ManifestValidationError(AGGREGATION_METHODS_NOT_MATCH(name));
}

existingParameter.plugins.push(pluginName);
existingParameter.description =
meta.description || existingParameter.description;
} else {
parameters[name] = {
plugins: [pluginName],
unit: meta.unit,
description: meta.description,
'aggregation-method': meta['aggregation-method'],
};
}
});

storeExplainData.parameters = {
...parameters,
};
};
10 changes: 10 additions & 0 deletions src/if-run/types/explain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,13 @@ export type ExplainParams = {
pluginData: PluginOptions;
metadata: {inputs?: ParameterMetadata; outputs?: ParameterMetadata};
};

export type ExplainStorageType = Record<
string,
{
plugins: string[];
unit: string;
description: string;
'aggregation-method': string;
}
>;

0 comments on commit 79aad35

Please sign in to comment.