Skip to content

Commit

Permalink
fix(lib): update global and node configs validations in co2js
Browse files Browse the repository at this point in the history
- update the doc
  • Loading branch information
manushak committed Feb 28, 2024
1 parent bdfeb0c commit 4da5654
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 50 deletions.
24 changes: 12 additions & 12 deletions src/lib/co2js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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:
Expand All @@ -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:
Expand Down
115 changes: 77 additions & 38 deletions src/lib/co2js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<PluginParams[]> => {
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,
Expand Down Expand Up @@ -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<z.infer<typeof schema>>(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<z.infer<typeof schema>>(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<z.infer<typeof schema>>(schema, config);
};
Expand Down

0 comments on commit 4da5654

Please sign in to comment.