From bdfeb0c67851eaed21ef04de797a7c3543b348b2 Mon Sep 17 00:00:00 2001 From: manushak Date: Wed, 28 Feb 2024 20:42:30 +0400 Subject: [PATCH 1/7] docs: rename IEF to IF --- src/lib/azure-importer/README.md | 2 +- src/lib/boavizta/README.md | 6 +++--- src/lib/ccf/README.md | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lib/azure-importer/README.md b/src/lib/azure-importer/README.md index 9396f09..edbd797 100644 --- a/src/lib/azure-importer/README.md +++ b/src/lib/azure-importer/README.md @@ -14,7 +14,7 @@ You can create one using [portal.azure.com](https://portal.azure.com). You also The Azure Importer uses [AzureDefaultCredentials](https://learn.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet) method which is an abstraction for different scenarios of authentication. -- When hosting the IEF Azure Importer on an Azure service, you can provide a [managed identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview). +- When hosting the IF Azure Importer on an Azure service, you can provide a [managed identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview). - When running the Azure Importer outside of Azure, e.g. on your local machine, you can use an [App registration](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app) (an App registration is a representation of a technical service principal account; you can view it as an identity for your App on Azure). The following steps in this tutorial use a service principal. You can learn more at https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app diff --git a/src/lib/boavizta/README.md b/src/lib/boavizta/README.md index aa67ae6..238d8a7 100644 --- a/src/lib/boavizta/README.md +++ b/src/lib/boavizta/README.md @@ -3,11 +3,11 @@ > [!NOTE] > Boavizta is a community plugin, not part of the IF standard library. This means the IF core team are not closely monitoring these plugins to keep them up to date. You should do your own research before implementing them! -[Boavizta](https://boavizta.org/) is an environmental impact calculator that exposes an API we use in IEF to retrieve energy and embodied carbon estimates. +[Boavizta](https://boavizta.org/) is an environmental impact calculator that exposes an API we use in IF to retrieve energy and embodied carbon estimates. ## Implementation -Boavizta exposes a [REST API](https://doc.api.boavizta.org/). If the `boavizta` plugin is included in an IEF pipeline, IEF sends API requests to Boavizta. The request payload is generated from input data provided to IEF in an `manifest` file. +Boavizta exposes a [REST API](https://doc.api.boavizta.org/). If the `boavizta` plugin is included in an IF pipeline, IF sends API requests to Boavizta. The request payload is generated from input data provided to IF in an `manifest` file. ## Parameters @@ -77,7 +77,7 @@ const usage = await output.calculate([ ## Example `manifest` -In IEF plugins are expected to be invoked from an `manifest` file. This is a yaml containing the plugin configuration and inputs. The following `manifest` initializes and runs the `boavizta-cpu` plugin: +In IF plugins are expected to be invoked from an `manifest` file. This is a yaml containing the plugin configuration and inputs. The following `manifest` initializes and runs the `boavizta-cpu` plugin: ```yaml name: boavizta-demo diff --git a/src/lib/ccf/README.md b/src/lib/ccf/README.md index 9962d95..fecac7b 100644 --- a/src/lib/ccf/README.md +++ b/src/lib/ccf/README.md @@ -1,6 +1,6 @@ # Cloud Carbon Footprint -> [!NOTE] > `CCF` is a community plugin, not part of the IF standard library. This means the IF core team are not closely monitoring these plugins to keep them up to date. You should do your own research before manifestementing them! +> [!NOTE] > `CCF` is a community plugin, not part of the IF standard library. This means the IF core team are not closely monitoring these plugins to keep them up to date. You should do your own research before implementing them! "Cloud Carbon Footprint is an open source tool that provides visibility and tooling to measure, monitor and reduce your cloud carbon emissions. We use best practice methodologies to convert cloud utilization into estimated energy usage and carbon emissions, producing metrics and carbon savings estimates that can be shared with employees, investors, and other stakeholders." - [CCF](https://www.cloudcarbonfootprint.org/) @@ -22,9 +22,9 @@ - `carbon-embodied`: carbon emitted in manufacturing the device, in gCO2eq - `energy`: energy used by CPU in kWh -## IEF manifestementation +## IF Implementation -IEF remanifestements the Cloud Carbon Footprint methodology from scratch conforming to the IEF specification. This means the CCF plugins can be run inside IEF without any external API calls and can be invoked as part of a plugin pipeline defined in an `manifest`. +IF reimplements the Cloud Carbon Footprint methodology from scratch conforming to the IF specification. This means the CCF plugins can be run inside IF without any external API calls and can be invoked as part of a plugin pipeline defined in an `manifest`. Cloud Carbon Footprint includes calculations for three cloud vendors: AWS, Azure and GCP. @@ -40,11 +40,11 @@ And: `Embodied Emissions = estimated metric tons CO2e emissions from the manufacturing of datacenter servers, for compute usage` -You can read a detailed explanation ofn the calculations in the [CCF docs](https://www.cloudcarbonfootprint.org/docs/methodology/) and see the code for our manifestementation in [this repository](../../src/lib/ccf/). +You can read a detailed explanation ofn the calculations in the [CCF docs](https://www.cloudcarbonfootprint.org/docs/methodology/) and see the code for our implementing in [this repository](../../src/lib/ccf/). ## Usage -In IEF, the plugin is called from a `manifest`. A `manifest` is a `.yaml` file that contains configuration metadata and usage inputs. This is interpreted by the command line tool, `if`. The plugin input is expected to contain `duration`,`cpu/utilization`, `cloud/vendor` and `cloud/instance-type` fields. +In IF, the plugin is called from a `manifest`. A `manifest` is a `.yaml` file that contains configuration metadata and usage inputs. This is interpreted by the command line tool, `if`. The plugin input is expected to contain `duration`,`cpu/utilization`, `cloud/vendor` and `cloud/instance-type` fields. You can see example Typescript invocations for each `cloud/vendor` below: From 4da5654f10c5963fe9894a234175dfb3c7ae75d4 Mon Sep 17 00:00:00 2001 From: manushak Date: Wed, 28 Feb 2024 20:58:38 +0400 Subject: [PATCH 2/7] fix(lib): update global and node configs validations in co2js - update the doc --- src/lib/co2js/README.md | 24 ++++----- src/lib/co2js/index.ts | 115 +++++++++++++++++++++++++++------------- 2 files changed, 89 insertions(+), 50 deletions(-) diff --git a/src/lib/co2js/README.md b/src/lib/co2js/README.md index 00c8622..6c1a87b 100644 --- a/src/lib/co2js/README.md +++ b/src/lib/co2js/README.md @@ -10,9 +10,9 @@ - `dataReloadRatio` - a value between 0 and 1 representing the percentage of data that is downloaded by return visitors. -`firstVisitPercentage` - a value between 0 and 1 representing the percentage of new visitors. - `returnVisitPercentage` - a value between 0 and 1 representing the percentage of returning visitors. - `gridIntensity` - an object that can contain the following optional keys: - - `device` - - `dataCenter` - - `networks` + - `device` - a number representing the carbon intensity for the given segment (in grams per kilowatt-hour). Or, an object, which contains a key of country and a value that is an Alpha-3 ISO country code. + - `dataCenter` - a number representing the carbon intensity for the given segment (in grams per kilowatt-hour). Or, an object, which contains a key of country and a value that is an Alpha-3 ISO country code. + - `networks` - A number representing the carbon intensity for the given segment (in grams per kilowatt-hour). Or, an object, which contains a key of country and a value that is an Alpha-3 ISO country code. The value for `device`, `dataCenter`, or `networks` can be a number representing the carbon intensity for the given segment (in grams per kilowatt-hour). Or, an object, which contains a key of country and a value that is an Alpha-3 ISO country code. @@ -39,24 +39,24 @@ The CO2JS Framework is a community plugin, not part of the IF standard library. ## Usage -In IEF the plugin is called from an `impl`. An `impl` is a `.yaml` file that contains configuration metadata and usage inputs. This is interpreted by the command line tool, `if`. There, the plugin's `configure` method is called first. +In IF the plugin is called from an `manifest`. An `manifest` is a `.yaml` file that contains configuration metadata and usage inputs. This is interpreted by the command line tool, `if`. The plugin config should define a `type` supported by the CO2.JS library (either `swd` or `1byte`). These are different ways to calculate the operational carbon associated with a web application; `swd` is shorthand for 'sustainable web design' plugin and `1byte` refers to the OneByte mdoel. You can read about the details of these plugins and how they differ at the [Green Web Foundation website](https://developers.thegreenwebfoundation.org/co2js/explainer/methodologies-for-calculating-website-carbon/). Each input is expected to contain `network/data/bytes` or `network/data`, `duration` and `timestamp` fields. -## IMPL +## manifest -The following is an example of how CO2.JS can be invoked using an `impl`. +The following is an example of how CO2.JS can be invoked using an `manifest`. ```yaml name: co2js-demo -description: example impl invoking CO2.JS plugin +description: example manifest invoking CO2.JS plugin tags: initialize: plugins: co2js: - function: Co2js + method: Co2js path: '@grnsft/if-unofficial-plugins' global-config: options: @@ -82,19 +82,19 @@ tree: network/data/bytes: 1000000 ``` -This impl is run using `if` using the following command, run from the project root: +This manifest is run using `if` using the following command, run from the project root: ```sh npm i -g @grnsft/if npm i -g @grnsft/if-unofficial-plugins -if --impl ./examples/impls/test/co2js.yml --ompl ./examples/ompls/co2js.yml +if --manifest ./examples/manifests/test/co2js.yml --output ./examples/outputs/co2js.yml ``` -This yields a result that looks like the following (saved to `/ompls/co2js.yml`): +This yields a result that looks like the following (saved to `/outputs/co2js.yml`): ```yaml name: co2js-demo -description: example impl invoking CO2.JS model +description: example manifest invoking CO2.JS model tags: null initialize: plugins: diff --git a/src/lib/co2js/index.ts b/src/lib/co2js/index.ts index 2c37fa5..f316709 100644 --- a/src/lib/co2js/index.ts +++ b/src/lib/co2js/index.ts @@ -15,38 +15,24 @@ export const Co2js = (globalConfig?: ConfigParams): PluginInterface => { const errorBuilder = buildErrorMessage(Co2js.name); /** - * Executes the model for a list of input parameters. + * Executes the plugin for a list of input parameters. */ - const execute = async ( - inputs: PluginParams[], - config?: ConfigParams - ): Promise => { - const mergedConfig = Object.assign({}, config, globalConfig); - - validateConfig(mergedConfig); - - const model = new co2({model: mergedConfig.type}); + const execute = async (inputs: PluginParams[], config?: ConfigParams) => { + const mergedValidatedConfig = Object.assign( + {}, + validateConfig(config), + validateGlobalConfig() + ); + const model = new co2({model: mergedValidatedConfig.type}); return inputs.map(input => { - const mergedWithConfig = Object.assign({}, input, mergedConfig); - - if (!(input['network/data/bytes'] || input['network/data'])) { - throw new InputValidationError( - errorBuilder({ - message: 'Bytes not provided', - }) - ); - } - + const mergedWithConfig = Object.assign( + {}, + validateInput(input), + mergedValidatedConfig + ); const result = calculateResultByParams(mergedWithConfig, model); - if (result) { - return { - ...input, - 'carbon-operational': result, - }; - } - return result ? { ...input, @@ -83,23 +69,76 @@ export const Co2js = (globalConfig?: ConfigParams): PluginInterface => { }; /** - * Validates static parameters. + * Validates input parameters. + */ + const validateInput = (input: PluginParams) => { + const schema = z + .object({ + 'network/data/bytes': z.number(), + 'network/data': z.number(), + }) + .partial() + .refine(data => !!data['network/data/bytes'] || !!data['network/data'], { + message: + 'Either `network/data/bytes` or `network/data` should be provided in the input.', + }); + + return validate>(schema, input); + }; + + /** + * Validates Global config parameters. + */ + const validateGlobalConfig = () => { + const schema = z.object({ + options: z + .object({ + dataReloadRatio: z.number().min(0).max(1).optional(), + firstVisitPercentage: z.number().min(0).max(1).optional(), + returnVisitPercentage: z.number().min(0).max(1).optional(), + gridIntensity: z + .object({ + device: z + .number() + .or(z.object({country: z.string()})) + .optional(), + dataCenter: z + .number() + .or(z.object({country: z.string()})) + .optional(), + networks: z + .number() + .or(z.object({country: z.string()})) + .optional(), + }) + .optional(), + }) + .optional(), + }); + + return validate>(schema, globalConfig || {}); + }; + + /** + * Validates node config parameters. */ - const validateConfig = (config: ConfigParams) => { + const validateConfig = (config?: ConfigParams) => { + if (!config) { + throw new InputValidationError( + errorBuilder({ + message: 'Config is not provided', + }) + ); + } + const schema = z .object({ type: z.enum(['1byte', 'swd']), 'green-web-host': z.boolean(), - options: z - .object({ - dataReloadRatio: z.number().min(0).max(1).optional(), - firstVisitPercentage: z.number().min(0).max(1).optional(), - returnVisitPercentage: z.number().min(0).max(1).optional(), - gridIntensity: z.object({}).optional(), - }) - .optional(), }) - .refine(allDefined); + .refine(allDefined, { + message: '`type` and `green-web-host` are not provided in node config', + }); return validate>(schema, config); }; From 2ff6a5b982fbe1aeb55b1297eda190aedf4f2bc1 Mon Sep 17 00:00:00 2001 From: manushak Date: Wed, 28 Feb 2024 21:00:20 +0400 Subject: [PATCH 3/7] docs: minor fix ib co2js plogin doc --- src/lib/co2js/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/co2js/README.md b/src/lib/co2js/README.md index 6c1a87b..a826107 100644 --- a/src/lib/co2js/README.md +++ b/src/lib/co2js/README.md @@ -45,7 +45,7 @@ The plugin config should define a `type` supported by the CO2.JS library (either Each input is expected to contain `network/data/bytes` or `network/data`, `duration` and `timestamp` fields. -## manifest +## Manifest The following is an example of how CO2.JS can be invoked using an `manifest`. From 6555b3fcb05612ef9e7a7c44a0bac8aa78c01475 Mon Sep 17 00:00:00 2001 From: manushak Date: Wed, 28 Feb 2024 21:10:51 +0400 Subject: [PATCH 4/7] fix(lib): update return type of execute function in `teads-aws` - update the doc --- src/lib/teads-aws/README.md | 16 ++++++---------- src/lib/teads-aws/index.ts | 20 +++++++++++--------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/lib/teads-aws/README.md b/src/lib/teads-aws/README.md index 5b31d2a..8557dbb 100644 --- a/src/lib/teads-aws/README.md +++ b/src/lib/teads-aws/README.md @@ -1,4 +1,4 @@ -# Teads' AWS Estimation Model +# Teads' AWS Estimation Plugin > [!NOTE] > `Teads-AWS` is a community plugin, not part of the IF standard library. This means the IF core team are not closely monitoring these plugins to keep them up to date. You should do your own research before implementing them! @@ -6,13 +6,9 @@ Teads Engineering Team built a plugin for estimating AWS instances energy usage. The main benefit of this plugin is that it accounts for all the components involved in an instance's compute capacity. -## Model name - -IF recognizes the Teads AWS plugin as `teads-aws` - ## Parameters -### Model global config +### Plugin global config - `interpolation`: the interpolation method to apply to the TDP curve @@ -30,7 +26,7 @@ IF recognizes the Teads AWS plugin as `teads-aws` ## Implementation -IEF implements this plugin based on the data gathered from the CCF (Cloud Carbon Footprint) dataset. +IF implements this plugin based on the data gathered from the CCF (Cloud Carbon Footprint) dataset. Spline interpolation is implemented as the default method of estimating the usage using the power curve provided by `IDLE`, `10%`, `50%`, `100%` values in the dataset. @@ -55,7 +51,7 @@ const results = teads.execute([ ]); ``` -## Example `impl` +## Example `manifest` ```yaml name: teads-aws @@ -64,7 +60,7 @@ tags: initialize: plugins: teads-aws: - plugin: TeadsAWS + method: TeadsAWS path: '@grnsft/if-unofficial-plugins' global-config: interpolation: linear @@ -88,5 +84,5 @@ You can run this by passing it to `if`. Run impact using the following command r ```sh npm i -g @grnsft/if npm i -g @grnsft/if-unofficial-plugins -if --impl ./examples/impls/test/teads-aws.yml --ompl ./examples/ompls/teads-aws.yml +if --manifest ./examples/manifests/test/teads-aws.yml --output ./examples/outputs/teads-aws.yml ``` diff --git a/src/lib/teads-aws/index.ts b/src/lib/teads-aws/index.ts index dd07d6d..f9ae413 100644 --- a/src/lib/teads-aws/index.ts +++ b/src/lib/teads-aws/index.ts @@ -30,7 +30,7 @@ export const TeadsAWS = (globalConfig: ConfigParams): PluginInterface => { /** * Calculate the total emissions for a list of inputs. */ - const execute = async (inputs: PluginParams[]): Promise => { + const execute = async (inputs: PluginParams[]) => { standardizeInstanceMetrics(); return inputs.map(input => { @@ -40,14 +40,15 @@ export const TeadsAWS = (globalConfig: ConfigParams): PluginInterface => { const validExpectedLifespan = input['cpu/expected-lifespan'] ?? expectedLifespan; - input['energy'] = calculateEnergy(safeInput, instanceType); - input['carbon-embodied'] = embodiedEmissions( - safeInput, - instanceType, - validExpectedLifespan - ); - - return input; + return { + ...input, + energy: calculateEnergy(safeInput, instanceType), + 'carbon-embodied': embodiedEmissions( + safeInput, + instanceType, + validExpectedLifespan + ), + }; }); }; @@ -209,6 +210,7 @@ export const TeadsAWS = (globalConfig: ConfigParams): PluginInterface => { validateInstanceType(param['cloud/instance-type']); return true; }); + return validate(schema, input); }; From 5e5baafabe2534ab69821ee4f54b7c8327447570 Mon Sep 17 00:00:00 2001 From: manushak Date: Wed, 28 Feb 2024 21:11:57 +0400 Subject: [PATCH 5/7] test: fix co2js test --- src/__tests__/unit/lib/co2js/index.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/__tests__/unit/lib/co2js/index.test.ts b/src/__tests__/unit/lib/co2js/index.test.ts index 24c4bc6..fd24aa3 100644 --- a/src/__tests__/unit/lib/co2js/index.test.ts +++ b/src/__tests__/unit/lib/co2js/index.test.ts @@ -86,7 +86,7 @@ describe('lib/co2js: ', () => { ]); }); - it('returns a result when provided `options` in the input.', async () => { + it('returns a result when provided `options` in the global config.', async () => { const config = {type: 'swd', 'green-web-host': false}; const output = Co2js({ options: { @@ -161,7 +161,9 @@ describe('lib/co2js: ', () => { await output.execute(inputs, config); } catch (error) { expect(error).toEqual( - new InputValidationError('Co2js: Bytes not provided.') + new InputValidationError( + 'Either `network/data/bytes` or `network/data` should be provided in the input.' + ) ); expect(error).toBeInstanceOf(InputValidationError); } From 4b0c6ef2f94713af179ed62b246d46155a1d2031 Mon Sep 17 00:00:00 2001 From: manushak Date: Wed, 28 Feb 2024 21:13:37 +0400 Subject: [PATCH 6/7] fix(lib): update exporting `WattTimeGridEmissions` plugin --- src/lib/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/index.ts b/src/lib/index.ts index 4c13da1..d3ba6f4 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -4,4 +4,4 @@ export {CloudCarbonFootprint} from './ccf'; export {Co2js} from './co2js'; export {TeadsAWS} from './teads-aws'; export {TeadsCurve} from './teads-curve'; -export * from './watt-time'; +export {WattTimeGridEmissions} from './watt-time'; From b052d491aeed6763f5f5b2ec95f9cb44175c21e9 Mon Sep 17 00:00:00 2001 From: manushak Date: Wed, 28 Feb 2024 21:24:54 +0400 Subject: [PATCH 7/7] fix(lib): fix validation in `teads-curve` - update readme file --- src/lib/teads-curve/README.md | 21 +++++++-------- src/lib/teads-curve/index.ts | 49 ++++++++++++++--------------------- 2 files changed, 28 insertions(+), 42 deletions(-) diff --git a/src/lib/teads-curve/README.md b/src/lib/teads-curve/README.md index 077f856..0f828c2 100644 --- a/src/lib/teads-curve/README.md +++ b/src/lib/teads-curve/README.md @@ -4,20 +4,17 @@ Teads Engineering team has built a plugin that is capable of estimating CPU usages across varying type of CPUs using a curve commonly known as Teads Curve. -## Plugin name - -IF recognizes the Teads CPU plugin as `teads-curve`. - ## Parameters -### Plugin config +### Plugin global config -- `cpu/thermal-design-power`: the TDp of the processor - `interpolation`: the interpolation method to apply to the TDP data ### Inputs +- `cpu/thermal-design-power`: the TDp of the processor - `cpu/utilization`: percentage CPU utilization for the input +- `duration`: the amount of time the observation covers, in seconds ## Returns @@ -48,8 +45,8 @@ const teads = TeadsCurve({ const results = teads.execute([ { duration: 3600, // duration institute + timestamp: '2021-01-01T00:00:00Z', // ISO8601 / RFC3339 timestamp 'cpu/utilization': 100, // CPU usage as a value between 0 to 100 in percentage - datetime: '2021-01-01T00:00:00Z', // ISO8601 / RFC3339 timestamp }, ]); ``` @@ -73,8 +70,8 @@ const results = teads.execute( [ { duration: 3600, // duration institute + timestamp: '2021-01-01T00:00:00Z', // ISO8601 / RFC3339 timestamp 'cpu/utilization': 100, // CPU usage as a value between 0 to 100 in percentage - datetime: '2021-01-01T00:00:00Z', // ISO8601 / RFC3339 timestamp }, ], { @@ -83,7 +80,7 @@ const results = teads.execute( ); ``` -## Example `impl` +## Example `manifest` ```yaml name: teads-curve @@ -92,7 +89,7 @@ tags: initialize: plugins: teads-curve: - function: TeadsCurve + method: TeadsCurve path: '@grnsft/if-unofficial-plugins' global-config: interpolation: spline @@ -100,7 +97,7 @@ tree: children: child: pipeline: - - teads-cpu + - teads-curve inputs: - timestamp: 2023-07-06T00:00 duration: 3600 @@ -113,5 +110,5 @@ You can run this by passing it to `if`. Run impact using the following command r ```sh npm i -g @grnsft/if npm i -g @grnsft/if-unofficial-plugins -if --impl ./examples/impls/test/teads-cpu.yml --ompl ./examples/ompls/teads-cpu.yml +if --manifest ./examples/manifests/test/teads-curve.yml --output ./examples/outputs/teads-curve.yml ``` diff --git a/src/lib/teads-curve/index.ts b/src/lib/teads-curve/index.ts index f51d2f3..13345dc 100644 --- a/src/lib/teads-curve/index.ts +++ b/src/lib/teads-curve/index.ts @@ -22,16 +22,12 @@ export const TeadsCurve = (globalConfig?: ConfigParams): PluginInterface => { /** * Calculate the total emissions for a list of inputs. */ - const execute = async ( - inputs: PluginParams[], - config?: ConfigParams - ): Promise => { - const mergedConfig = Object.assign({}, globalConfig, config); - const validatedConfig = validateConfig(mergedConfig); + const execute = async (inputs: PluginParams[]) => { + const validatedConfig = validateConfig(globalConfig || {}); return inputs.map((input, index) => { - const safeInput = getValidatedInput(input); - const inputWithConfig: PluginParams = Object.assign( + const safeInput = validateInput(input); + const inputWithConfig = Object.assign( {}, input, safeInput, @@ -80,16 +76,17 @@ export const TeadsCurve = (globalConfig?: ConfigParams): PluginInterface => { * (wattage * duration) / (seconds in an hour) / 1000 = kWh */ const calculateEnergy = (input: PluginParams) => { - const {duration, 'cpu/utilization': cpu} = input; + const { + duration, + 'cpu/utilization': cpu, + 'cpu/thermal-design-power': cpuThermalDesignPower, + } = input; const spline: any = new Spline(POINTS, CURVE); const wattage = input.interpolation === Interpolation.SPLINE - ? spline.at(cpu) * input['cpu/thermal-design-power'] - : calculateLinearInterpolationWattage( - cpu, - input['cpu/thermal-design-power'] - ); + ? spline.at(cpu) * cpuThermalDesignPower + : calculateLinearInterpolationWattage(cpu, cpuThermalDesignPower); return (wattage * duration) / 3600 / 1000; }; @@ -161,34 +158,26 @@ export const TeadsCurve = (globalConfig?: ConfigParams): PluginInterface => { interpolation: z.nativeEnum(Interpolation).optional(), }); - //Manually add default value - config.interpolation = config.interpolation ?? Interpolation.SPLINE; + // Manually set default value + const interpolation = config.interpolation ?? Interpolation.SPLINE; - return validate>(schema, config); + return validate>(schema, {...config, interpolation}); }; /** * Validates parameters. */ - const validateParams = (params: object) => { + const validateInput = (input: PluginParams) => { const schema = z.object({ + duration: z.number().gt(0), 'cpu/utilization': z.number().min(0).max(100), 'cpu/thermal-design-power': z.number().min(1), }); - return validate>(schema, params); - }; - - /** - * Sets validated parameters for the class instance. - */ - const getValidatedInput = (params: object) => { - const safeParams = Object.assign({}, params, validateParams(params)); + // Manually set default value if the property is missing. + const cpuTDP = input['cpu/thermal-design-power'] ?? 0; - return { - 'cpu/thermal-design-power': safeParams['cpu/thermal-design-power'] ?? 0, - 'cpu/utilization': safeParams['cpu/utilization'], - }; + return validate>(schema, {...input, cpuTDP}); }; return {