-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Paz Barda <[email protected]>
- Loading branch information
1 parent
4a2998a
commit b9e5d4f
Showing
6 changed files
with
321 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import {Exponent} from '../../../../lib'; | ||
|
||
import {ERRORS} from '../../../../util/errors'; | ||
|
||
const {InputValidationError} = ERRORS; | ||
|
||
describe('lib/exponent: ', () => { | ||
describe('Exponent: ', () => { | ||
const globalConfig = { | ||
'input-parameter': 'energy/base', | ||
exponent: 3, | ||
'output-parameter': 'energy', | ||
}; | ||
const exponent = Exponent(globalConfig); | ||
|
||
describe('init: ', () => { | ||
it('successfully initalized.', () => { | ||
expect(exponent).toHaveProperty('metadata'); | ||
expect(exponent).toHaveProperty('execute'); | ||
}); | ||
}); | ||
|
||
describe('execute(): ', () => { | ||
it('successfully applies Exponent strategy to given input.', async () => { | ||
expect.assertions(1); | ||
|
||
const expectedResult = [ | ||
{ | ||
duration: 3600, | ||
'energy/base': 2, | ||
energy: 8, | ||
timestamp: '2021-01-01T00:00:00Z', | ||
}, | ||
]; | ||
|
||
const result = await exponent.execute([ | ||
{ | ||
duration: 3600, | ||
'energy/base': 2, | ||
timestamp: '2021-01-01T00:00:00Z', | ||
}, | ||
]); | ||
|
||
expect(result).toStrictEqual(expectedResult); | ||
}); | ||
|
||
it('throws an error on missing params in input.', async () => { | ||
const expectedMessage = | ||
'Exponent: energy/base is missing from the input array.'; | ||
|
||
expect.assertions(1); | ||
|
||
try { | ||
await exponent.execute([ | ||
{ | ||
duration: 3600, | ||
timestamp: '2021-01-01T00:00:00Z', | ||
}, | ||
]); | ||
} catch (error) { | ||
expect(error).toStrictEqual( | ||
new InputValidationError(expectedMessage) | ||
); | ||
} | ||
}); | ||
|
||
it('throws an error on input param value not numeric.', async () => { | ||
const expectedMessage = 'Exponent: i-am-not-a-number is not numeric.'; | ||
|
||
expect.assertions(1); | ||
|
||
try { | ||
await exponent.execute([ | ||
{ | ||
duration: 3600, | ||
'energy/base': 'i-am-not-a-number', | ||
timestamp: '2021-01-01T00:00:00Z', | ||
}, | ||
]); | ||
} catch (error) { | ||
expect(error).toStrictEqual( | ||
new InputValidationError(expectedMessage) | ||
); | ||
} | ||
}); | ||
|
||
it('returns a result with input params not related to energy.', async () => { | ||
expect.assertions(1); | ||
const newConfig = { | ||
'input-parameter': 'carbon/base', | ||
exponent: 4, | ||
'output-parameter': 'carbon', | ||
}; | ||
const exponent = Exponent(newConfig); | ||
|
||
const data = [ | ||
{ | ||
duration: 3600, | ||
timestamp: '2021-01-01T00:00:00Z', | ||
'carbon/base': 2, | ||
}, | ||
]; | ||
const response = await exponent.execute(data); | ||
|
||
const expectedResult = [ | ||
{ | ||
duration: 3600, | ||
'carbon/base': 2, | ||
carbon: 16, | ||
timestamp: '2021-01-01T00:00:00Z', | ||
}, | ||
]; | ||
|
||
expect(response).toEqual(expectedResult); | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
# Exponent | ||
|
||
`exponent` is a generic plugin for calculating exponent of an input param (as base) and another (as the exponent) in an `input` array. | ||
|
||
You provide the names of the values you want to use for the exponent calculation, and a name to use to add the exponent result to the output array. | ||
|
||
For example, you use `cpu/energy` as base and `network/energy` as and name the result `energy`. `energy` would then be added to every observation in your input array as `cpu/energy` raised by the exponent `network/energy`. | ||
|
||
## Parameters | ||
|
||
### Plugin config | ||
|
||
Three parameters are required in global config: `input-parameter`, `exponent` and `output-parameter`. | ||
|
||
`input-parameter`: a string defining the base. Must match an existing key in the `inputs` array | ||
`exponent`: a number defining the exponent. | ||
`output-parameter`: a string defining the name to use to add the result of the exponent to the output array. | ||
|
||
### Inputs | ||
|
||
`input-parameter` and `exponent` must be available in the input array. | ||
|
||
## Returns | ||
|
||
- `output-parameter`: `input-parameter` raised by `exponent` with the parameter name defined by `output-parameter` in global config. | ||
|
||
## Calculation | ||
|
||
```pseudocode | ||
output = input ^ exponent | ||
``` | ||
|
||
## Implementation | ||
|
||
To run the plugin, you must first create an instance of `Exponent`. Then, you can call `execute()`. | ||
|
||
```typescript | ||
import {Exponent} from '@grnsft/if-plugins'; | ||
|
||
const config = { | ||
inputParameter: ['cpu/energy'], | ||
exponent: 2 | ||
outputParameter: 'energy', | ||
}; | ||
|
||
const exponent = Exponent(config); | ||
const result = await exponent.execute([ | ||
{ | ||
duration: 3600, | ||
timestamp: '2021-01-01T00:00:00Z', | ||
'cpu/energy': 0.1, | ||
'energy': 0.01, | ||
}, | ||
]); | ||
``` | ||
|
||
## Example manifest | ||
|
||
IF users will typically call the plugin as part of a pipeline defined in a manifest file. In this case, instantiating the plugin is handled by and does not have to be done explicitly by the user. The following is an example manifest that calls `exponent`: | ||
|
||
```yaml | ||
name: exponent demo | ||
description: | ||
tags: | ||
initialize: | ||
outputs: | ||
- yaml | ||
plugins: | ||
exponent: | ||
method: Exponent | ||
path: '@grnsft/if-plugins' | ||
global-config: | ||
input-parameter: 'cpu/energy' | ||
exponent: 2 | ||
output-parameter: 'energy' | ||
tree: | ||
children: | ||
child: | ||
pipeline: | ||
- exponent | ||
config: | ||
exponent: | ||
inputs: | ||
- timestamp: 2023-08-06T00:00 | ||
duration: 3600 | ||
cpu/energy: 0.001 | ||
network/energy: 0.001 | ||
``` | ||
You can run this example by saving it as `./manifests/examples/test/exponent.yml` and executing the following command from the project root: | ||
|
||
```sh | ||
npm i -g @grnsft/if | ||
npm i -g @grnsft/if-plugins | ||
ie --manifest ./manifests/examples/test/exponent.yml --output ./examples/outputs/exponent.yml | ||
``` | ||
|
||
The results will be saved to a new `yaml` file in `./examples/outputs`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import {z} from 'zod'; | ||
|
||
import {ERRORS} from '../../util/errors'; | ||
import {buildErrorMessage} from '../../util/helpers'; | ||
import {validate} from '../../util/validations'; | ||
|
||
import {PluginInterface} from '../../interfaces'; | ||
import {PluginParams} from '../../types/common'; | ||
import {ExponentConfig} from './types'; | ||
|
||
const {InputValidationError} = ERRORS; | ||
|
||
export const Exponent = (globalConfig: ExponentConfig): PluginInterface => { | ||
const errorBuilder = buildErrorMessage(Exponent.name); | ||
const metadata = { | ||
kind: 'execute', | ||
}; | ||
|
||
/** | ||
* Checks global config value are valid. | ||
*/ | ||
const validateGlobalConfig = () => { | ||
const globalConfigSchema = z.object({ | ||
'input-parameter': z.string().min(1), | ||
exponent: z.number().min(1), | ||
'output-parameter': z.string().min(1), | ||
}); | ||
|
||
return validate<z.infer<typeof globalConfigSchema>>( | ||
globalConfigSchema, | ||
globalConfig | ||
); | ||
}; | ||
|
||
/** | ||
* Checks for required fields in input. | ||
*/ | ||
const validateSingleInput = (input: PluginParams, inputParameter: string) => { | ||
validateParamExists(input, inputParameter); | ||
validateNumericString(input[inputParameter]); | ||
}; | ||
|
||
const validateParamExists = (input: PluginParams, param: string) => { | ||
if (input[param] === undefined) { | ||
throw new InputValidationError( | ||
errorBuilder({ | ||
message: `${param} is missing from the input array`, | ||
}) | ||
); | ||
} | ||
}; | ||
|
||
const validateNumericString = (str: string) => { | ||
if (typeof str !== 'number') { | ||
throw new InputValidationError( | ||
errorBuilder({ | ||
message: `${str} is not numeric`, | ||
}) | ||
); | ||
} | ||
}; | ||
|
||
/** | ||
* Calculate the input param raised by to the power of the given exponent. | ||
*/ | ||
const execute = async (inputs: PluginParams[]): Promise<PluginParams[]> => { | ||
const { | ||
'input-parameter': inputParameter, | ||
exponent: exponent, | ||
'output-parameter': outputParameter, | ||
} = validateGlobalConfig(); | ||
return inputs.map(input => { | ||
validateSingleInput(input, inputParameter); | ||
|
||
return { | ||
...input, | ||
[outputParameter]: calculateExponent(input, inputParameter, exponent), | ||
}; | ||
}); | ||
}; | ||
|
||
/** | ||
* Calculates the input param raised by the power of a given exponent. | ||
*/ | ||
const calculateExponent = ( | ||
input: PluginParams, | ||
inputParameter: string, | ||
exponent: number | ||
) => { | ||
const base = input[inputParameter]; | ||
return Math.pow(base, exponent); | ||
}; | ||
|
||
return { | ||
metadata, | ||
execute, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export type ExponentConfig = { | ||
'input-parameter': string; | ||
exponent: number; | ||
'output-parameter': string; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters