Skip to content

Commit

Permalink
Merge pull request #1032 from Green-Software-Foundation/refactor-plugins
Browse files Browse the repository at this point in the history
Refactor plugins
  • Loading branch information
narekhovhannisyan authored Oct 1, 2024
2 parents 9e08d52 + b889285 commit cf83951
Show file tree
Hide file tree
Showing 47 changed files with 1,869 additions and 2,682 deletions.
115 changes: 63 additions & 52 deletions Refactor-migration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,65 +199,76 @@ Details tbc...
## Plugins
The plugins themselves require some changes to keep them compatible with the refactored IF.
Plugins require some modifications to remain compatible with the refactored IF interface.
Instead of the old class-based model, plugins are now functions. They conform to the following interface:
Each plugin follows the `PluginFactory` interface, which is a higher-order function that accepts a `params` object of type `PluginFactoryParams`. This function returns another function (the inner function), which handles the plugin’s `config`, `parametersMetadata`, and `mapping`.
```ts
export type PluginInterface = {
execute: (inputs: PluginParams[]) => PluginParams[];
metadata: {
kind: string;
};
[key: string]: any;
};
export const PluginFactory =
(params: PluginFactoryParams) =>
(
config: ConfigParams = {},
parametersMetadata: PluginParametersMetadata,
mapping: MappingParams
) => ({
metadata: {
kind: 'execute',
inputs: {...params.metadata.inputs, ...parametersMetadata?.inputs},
outputs: parametersMetadata?.outputs || params.metadata.outputs,
},
execute: async (inputs: PluginParams[]) => {
// Generic plugin functionality goes here
// E.g., mapping, arithmetic operations, validation
// Process inputs and mapping logic
});
})
```
The plugin still requires an execute function. This is where you implement the plugin logic.
Inner Function Parameters:
- `config`: This is of type `ConfigParams` and has a default value of an empty object ({}). This might hold configuration settings for the plugin.
- `parametersMetadata`: A `PluginParametersMetadata` object that describes the metadata for the plugin’s parameters.
- `mapping`: A `MappingParams` object, describing parameters are mapped.
Implementation Function:
Here's a minimal example for a plugin that sums some inputs defined in config - see inline comments for some important notes:
The plugin requires an `implementation` function, where the actual plugin logic is defined.
Here’s a minimal example of a plugin that sums inputs as defined in the config. See the inline comments for further clarification.
```ts
// Here's the function definition - notice that config is passed in here!
export const Sum = (config: SumConfig): PluginInterface => {
const inputParameters = config['input-parameters'] || [];
const outputParameter = config['output-parameter'];
// we also return metadata now too - you can add more or just use this default
const metadata = {
kind: 'execute',
};
/**
* Calculate the sum of the input metrics for each timestamp.
*/
const execute = async (inputs: PluginParams[]): Promise<PluginParams[]> => {
inputs.map(input => {
return calculateSum(input, inputParameters, outputParameter);
// Here's the function definition!
export const Sum = PluginFactory({
configValidation: z.object({
'input-parameters': z.array(z.string()),
'output-parameter': z.string().min(1),
}),
inputValidation: (input: PluginParams, config: ConfigParams) => {
return validate(validationSchema, inputData);
},
implementation: async (inputs: PluginParams[], config: ConfigParams) => {
const {
'input-parameters': inputParameters,
'output-parameter': outputParameter,
} = config;
return inputs.map(input => {
const calculatedResult = calculateSum(input, inputParameters);
return {
...input,
[outputParameter]: calculatedResult,
};
});
return inputs;
};
/**
* Calculates the sum of the energy components.
*/
const calculateSum = (
input: PluginParams,
inputParameters: string[],
outputParameter: string
) => {
input[outputParameter] = inputParameters.reduce(
(accumulator, metricToSum) => {
return accumulator + input[metricToSum];
},
0
);
};
// return the metadata and the execute function
return {
metadata,
execute,
};
};
},
allowArithmeticExpressions: [],
});
/**
* Calculates the sum of the energy components.
*/
const calculateSum = (input: PluginParams, inputParameters: string[]) =>
inputParameters.reduce(
(accumulator, metricToSum) => accumulator + input[metricToSum],
0
);
```
8 changes: 4 additions & 4 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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"dependencies": {
"@commitlint/cli": "^18.6.0",
"@commitlint/config-conventional": "^18.6.0",
"@grnsft/if-core": "^0.0.23",
"@grnsft/if-core": "^0.0.24",
"axios": "^1.7.2",
"csv-parse": "^5.5.6",
"csv-stringify": "^6.4.6",
Expand Down
39 changes: 17 additions & 22 deletions src/__tests__/if-run/builtins/coefficient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {ERRORS} from '@grnsft/if-core/utils';
import {Coefficient} from '../../../if-run/builtins/coefficient';

import {STRINGS} from '../../../if-run/config';
import {CoefficientConfig} from '@grnsft/if-core/types';

const {InputValidationError, ConfigError} = ERRORS;
const {MISSING_CONFIG} = STRINGS;
Expand All @@ -29,7 +28,7 @@ describe('builtins/coefficient: ', () => {
});

describe('execute(): ', () => {
it('successfully applies coefficient strategy to given input.', () => {
it('successfully applies coefficient strategy to given input.', async () => {
expect.assertions(1);

const expectedResult = [
Expand All @@ -41,7 +40,7 @@ describe('builtins/coefficient: ', () => {
},
];

const result = coefficient.execute([
const result = await coefficient.execute([
{
duration: 3600,
carbon: 3,
Expand All @@ -54,7 +53,7 @@ describe('builtins/coefficient: ', () => {
expect(result).toStrictEqual(expectedResult);
});

it('succcessfully executes when the mapping has data.', () => {
it('succcessfully executes when the mapping has data.', async () => {
const mapping = {
carbon: 'carbon-for-production',
};
Expand All @@ -71,7 +70,7 @@ describe('builtins/coefficient: ', () => {
},
];

const result = coefficient.execute([
const result = await coefficient.execute([
{
duration: 3600,
'carbon-for-production': 3,
Expand All @@ -84,7 +83,7 @@ describe('builtins/coefficient: ', () => {
expect(result).toStrictEqual(expectedResult);
});

it('succcessfully executes when the mapping map output parameter.', () => {
it('succcessfully executes when the mapping map output parameter.', async () => {
const mapping = {
'carbon-product': 'carbon-result',
};
Expand All @@ -101,7 +100,7 @@ describe('builtins/coefficient: ', () => {
},
];

const result = coefficient.execute([
const result = await coefficient.execute([
{
duration: 3600,
carbon: 3,
Expand All @@ -114,7 +113,7 @@ describe('builtins/coefficient: ', () => {
expect(result).toStrictEqual(expectedResult);
});

it('successfully executes when a parameter has an arithmetic expression.', () => {
it('successfully executes when a parameter has an arithmetic expression.', async () => {
expect.assertions(1);
const config = {
'input-parameter': '=3*carbon',
Expand All @@ -136,7 +135,7 @@ describe('builtins/coefficient: ', () => {
},
];

const result = coefficient.execute([
const result = await coefficient.execute([
{
duration: 3600,
carbon: 3,
Expand All @@ -149,7 +148,7 @@ describe('builtins/coefficient: ', () => {
expect(result).toStrictEqual(expectedResult);
});

it('throws an error when the `coefficient` has wrong arithmetic expression.', () => {
it('throws an error when the `coefficient` has wrong arithmetic expression.', async () => {
const config = {
'input-parameter': 'carbon',
coefficient: 'mock-param',
Expand All @@ -159,16 +158,12 @@ describe('builtins/coefficient: ', () => {
inputs: {},
outputs: {},
};
const coefficient = Coefficient(
config as any as CoefficientConfig,
parametersMetadata,
{}
);
const coefficient = Coefficient(config, parametersMetadata, {});

expect.assertions(2);

try {
coefficient.execute([
await coefficient.execute([
{
duration: 3600,
carbon: 'some-param',
Expand All @@ -185,14 +180,14 @@ describe('builtins/coefficient: ', () => {
}
});

it('throws an error when config is not provided.', () => {
it('throws an error when config is not provided.', async () => {
const config = undefined;
const coefficient = Coefficient(config!, parametersMetadata, {});

expect.assertions(1);

try {
coefficient.execute([
await coefficient.execute([
{
duration: 3600,
timestamp: '2021-01-01T00:00:00Z',
Expand All @@ -204,7 +199,7 @@ describe('builtins/coefficient: ', () => {
}
});

it('throws an error on missing `input-parameter` param in input.', () => {
it('throws an error on missing `input-parameter` param in input.', async () => {
const invalidConfig = {
'input-parameter': '',
coefficient: 3,
Expand All @@ -217,7 +212,7 @@ describe('builtins/coefficient: ', () => {
expect.assertions(1);

try {
coefficient.execute([
await coefficient.execute([
{
duration: 3600,
timestamp: '2021-01-01T00:00:00Z',
Expand All @@ -231,7 +226,7 @@ describe('builtins/coefficient: ', () => {
}
});

it('throws an error on missing `output-parameter` param in input.', () => {
it('throws an error on missing `output-parameter` param in input.', async () => {
const invalidConfig = {
'input-parameter': 'carbon',
coefficient: 10,
Expand All @@ -243,7 +238,7 @@ describe('builtins/coefficient: ', () => {

expect.assertions(1);
try {
coefficient.execute([
await coefficient.execute([
{
duration: 3600,
timestamp: '2021-01-01T00:00:00Z',
Expand Down
Loading

0 comments on commit cf83951

Please sign in to comment.