-
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.
Merge branch 'main' into regex-plugin
Signed-off-by: Manushak Keramyan <[email protected]>
- Loading branch information
Showing
5 changed files
with
334 additions
and
12 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,131 @@ | ||
import {Divide} from '../../../../lib'; | ||
|
||
import {ERRORS} from '../../../../util/errors'; | ||
|
||
const {InputValidationError} = ERRORS; | ||
|
||
describe('lib/divide: ', () => { | ||
describe('Divide: ', () => { | ||
const globalConfig = { | ||
numerator: 'vcpus-allocated', | ||
denominator: 2, | ||
output: 'cpu/number-cores', | ||
}; | ||
const divide = Divide(globalConfig); | ||
|
||
describe('init: ', () => { | ||
it('successfully initalized.', () => { | ||
expect(divide).toHaveProperty('metadata'); | ||
expect(divide).toHaveProperty('execute'); | ||
}); | ||
}); | ||
|
||
describe('execute(): ', () => { | ||
it('successfully applies Divide strategy to given input.', async () => { | ||
expect.assertions(1); | ||
|
||
const expectedResult = [ | ||
{ | ||
duration: 3600, | ||
'vcpus-allocated': 24, | ||
'cpu/number-cores': 12, | ||
timestamp: '2021-01-01T00:00:00Z', | ||
}, | ||
]; | ||
|
||
const result = await divide.execute([ | ||
{ | ||
duration: 3600, | ||
'vcpus-allocated': 24, | ||
timestamp: '2021-01-01T00:00:00Z', | ||
}, | ||
]); | ||
|
||
expect(result).toStrictEqual(expectedResult); | ||
}); | ||
|
||
it('returns a result when `denominator` is provded in input.', async () => { | ||
expect.assertions(1); | ||
const globalConfig = { | ||
numerator: 'vcpus-allocated', | ||
denominator: 'duration', | ||
output: 'vcpus-allocated-per-second', | ||
}; | ||
const divide = Divide(globalConfig); | ||
|
||
const input = [ | ||
{ | ||
timestamp: '2021-01-01T00:00:00Z', | ||
duration: 3600, | ||
'vcpus-allocated': 24, | ||
}, | ||
]; | ||
const response = await divide.execute(input); | ||
|
||
const expectedResult = [ | ||
{ | ||
timestamp: '2021-01-01T00:00:00Z', | ||
duration: 3600, | ||
'vcpus-allocated': 24, | ||
'vcpus-allocated-per-second': 24 / 3600, | ||
}, | ||
]; | ||
|
||
expect(response).toEqual(expectedResult); | ||
}); | ||
|
||
it('throws an error on missing params in input.', async () => { | ||
const expectedMessage = | ||
'"vcpus-allocated" parameter is required. Error code: invalid_type.'; | ||
|
||
const globalConfig = { | ||
numerator: 'vcpus-allocated', | ||
denominator: 3600, | ||
output: 'vcpus-allocated-per-second', | ||
}; | ||
const divide = Divide(globalConfig); | ||
|
||
expect.assertions(1); | ||
|
||
try { | ||
await divide.execute([ | ||
{ | ||
timestamp: '2021-01-01T00:00:00Z', | ||
duration: 3600, | ||
}, | ||
]); | ||
} catch (error) { | ||
expect(error).toStrictEqual( | ||
new InputValidationError(expectedMessage) | ||
); | ||
} | ||
}); | ||
}); | ||
|
||
it('throws an error when `denominator` is 0.', async () => { | ||
const expectedMessage = | ||
'"denominator" parameter is number must be greater than 0. Error code: too_small.'; | ||
|
||
const globalConfig = { | ||
numerator: 'vcpus-allocated', | ||
denominator: 0, | ||
output: 'vcpus-allocated-per-second', | ||
}; | ||
const divide = Divide(globalConfig); | ||
|
||
expect.assertions(1); | ||
|
||
try { | ||
await divide.execute([ | ||
{ | ||
timestamp: '2021-01-01T00:00:00Z', | ||
duration: 3600, | ||
'vcpus-allocated': 24, | ||
}, | ||
]); | ||
} catch (error) { | ||
expect(error).toStrictEqual(new InputValidationError(expectedMessage)); | ||
} | ||
}); | ||
}); | ||
}); |
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,96 @@ | ||
# Divide | ||
|
||
`divide` is a generic plugin for doing arithmetic division of two values in an `input` array. | ||
|
||
You provide the names of the values you want to divide, and a name to use to add the divide to the output array. | ||
|
||
For example, `boavizta-cpu` need `cpu/number-cores` to work, however `cloud-metadata` returns `vcpus-allocated`, to get number of cores you divide `vcpus-allocated` by 2. | ||
|
||
## Parameters | ||
|
||
### Plugin config | ||
|
||
- `numerator` - a parameter by a specific configured number | ||
- `denominator` - a parameter by a specific configured number or the number by which `numerator` is divided | ||
- `output` - the number to a configured output parameter | ||
|
||
### Inputs | ||
|
||
- `numerator` - as input parameter, must be available in the input array | ||
- `denominator` - must be available in the input array if is an input parameter | ||
- `output` - as input parameter, must be available in the input array | ||
|
||
## Returns | ||
|
||
- `output`: the division of `numerator` with the parameter name into `denominator` with the parameter name defined by `output` in global config. | ||
|
||
The plugin throws an exception if the division result is not a number. | ||
|
||
## Calculation | ||
|
||
```pseudocode | ||
output = input0 / input1 | ||
``` | ||
|
||
## Implementation | ||
|
||
To run the plugin, you must first create an instance of `Divide`. Then, you can call `execute()`. | ||
|
||
```typescript | ||
import {Divide} from '@grnsft/if-plugins'; | ||
|
||
const globalConfig = { | ||
numerator: 'vcpus-allocated', | ||
denominator: 2, | ||
output: 'cpu/number-cores', | ||
}; | ||
const divide = Divide(globalConfig); | ||
|
||
const input = [ | ||
{ | ||
timestamp: '2021-01-01T00:00:00Z', | ||
duration: 3600, | ||
'vcpus-allocated': 24, | ||
}, | ||
]; | ||
``` | ||
|
||
## 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 `if` and does not have to be done explicitly by the user. The following is an example manifest that calls `divide`: | ||
|
||
```yaml | ||
name: divide-demo | ||
description: | ||
tags: | ||
initialize: | ||
plugins: | ||
divide: | ||
method: Divide | ||
path: '@grnsft/if-plugins' | ||
global-config: | ||
numerator: vcpus-allocated | ||
denominator: 2 | ||
output: cpu/number-cores | ||
tree: | ||
children: | ||
child: | ||
pipeline: | ||
- divide | ||
config: | ||
divide: | ||
inputs: | ||
- timestamp: 2023-08-06T00:00 | ||
duration: 3600 | ||
vcpus-allocated: 24 | ||
``` | ||
You can run this example by saving it as `./examples/manifests/test/divide.yml` and executing the following command from the project root: | ||
|
||
```sh | ||
npm i -g @grnsft/if | ||
npm i -g @grnsft/if-plugins | ||
if --manifest ./examples/manifests/test/divide.yml --output ./examples/outputs/divide.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,97 @@ | ||
import {z} from 'zod'; | ||
|
||
import {buildErrorMessage} from '../../util/helpers'; | ||
import {ERRORS} from '../../util/errors'; | ||
import {validate} from '../../util/validations'; | ||
|
||
import {PluginInterface} from '../../interfaces'; | ||
import {ConfigParams, PluginParams} from '../../types/common'; | ||
|
||
const {InputValidationError, ConfigValidationError} = ERRORS; | ||
|
||
export const Divide = (globalConfig: ConfigParams): PluginInterface => { | ||
const errorBuilder = buildErrorMessage(Divide.name); | ||
const metadata = { | ||
kind: 'execute', | ||
}; | ||
|
||
/** | ||
* Checks global config value are valid. | ||
*/ | ||
const validateGlobalConfig = () => { | ||
if (!globalConfig) { | ||
throw new ConfigValidationError( | ||
errorBuilder({message: 'Configuration data is missing'}) | ||
); | ||
} | ||
const schema = z.object({ | ||
numerator: z.string().min(1), | ||
denominator: z.string().or(z.number().gt(0)), | ||
output: z.string(), | ||
}); | ||
|
||
return validate<z.infer<typeof schema>>(schema, globalConfig); | ||
}; | ||
|
||
/** | ||
* Checks for required fields in input. | ||
*/ | ||
const validateSingleInput = ( | ||
input: PluginParams, | ||
numerator: string, | ||
denominator: number | string | ||
) => { | ||
const schema = z | ||
.object({ | ||
[numerator]: z.number(), | ||
[denominator]: z.number().optional(), | ||
}) | ||
.refine(_data => { | ||
if (typeof denominator === 'string' && !input[denominator]) { | ||
throw new InputValidationError( | ||
errorBuilder({ | ||
message: `\`${denominator}\` is missing from the input`, | ||
}) | ||
); | ||
} | ||
return true; | ||
}); | ||
|
||
return validate<z.infer<typeof schema>>(schema, input); | ||
}; | ||
|
||
/** | ||
* Calculate the division of each input parameter. | ||
*/ | ||
const execute = async (inputs: PluginParams[]) => { | ||
const safeGlobalConfig = validateGlobalConfig(); | ||
const {numerator, denominator, output} = safeGlobalConfig; | ||
|
||
return inputs.map(input => { | ||
const safeInput = Object.assign( | ||
{}, | ||
input, | ||
validateSingleInput(input, numerator, denominator) | ||
); | ||
|
||
return { | ||
...input, | ||
[output]: calculateDivide(safeInput, numerator, denominator), | ||
}; | ||
}); | ||
}; | ||
|
||
/** | ||
* Calculates the division of the given parameter. | ||
*/ | ||
const calculateDivide = ( | ||
input: PluginParams, | ||
numerator: string, | ||
denominator: number | string | ||
) => input[numerator] / (input[denominator] || denominator); | ||
|
||
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