diff --git a/src/__tests__/unit/lib/co2js/index.test.ts b/src/__tests__/unit/lib/co2js/index.test.ts index f6786ed..989c4fc 100644 --- a/src/__tests__/unit/lib/co2js/index.test.ts +++ b/src/__tests__/unit/lib/co2js/index.test.ts @@ -38,12 +38,13 @@ describe('lib/co2js: ', () => { describe('execute(): ', () => { it('returns a result when `network/data/bytes` is provided in the input.', async () => { - const config = {type: '1byte', 'green-web-host': true}; + const config = {type: '1byte'}; const inputs = [ { timestamp: '2021-01-01T00:00:00Z', duration: 3600, 'network/data/bytes': 100000, + 'green-web-host': true, }, ]; const result = await output.execute(inputs, config); @@ -55,6 +56,7 @@ describe('lib/co2js: ', () => { timestamp: '2021-01-01T00:00:00Z', duration: 3600, 'network/data/bytes': 100000, + 'green-web-host': true, 'carbon-operational': 0.023195833333333332, }, ]); @@ -62,12 +64,13 @@ describe('lib/co2js: ', () => { it('returns the same input data when the co2 model returns undefined for the `type`.', async () => { process.env.WRONG_MODEL = 'true'; - const config = {type: '1byte', 'green-web-host': true}; + const config = {type: '1byte'}; const inputs = [ { timestamp: '2021-01-01T00:00:00Z', duration: 3600, 'network/data/bytes': 100000, + 'green-web-host': true, }, ]; const result = await output.execute(inputs, config); @@ -79,17 +82,19 @@ describe('lib/co2js: ', () => { timestamp: '2021-01-01T00:00:00Z', duration: 3600, 'network/data/bytes': 100000, + 'green-web-host': true, }, ]); }); it('returns a result when `network/data` is provided.', async () => { - const config = {type: '1byte', 'green-web-host': true}; + const config = {type: '1byte'}; const inputs = [ { timestamp: '2021-01-01T00:00:00Z', duration: 3600, 'network/data': 10, + 'green-web-host': true, }, ]; const result = await output.execute(inputs, config); @@ -101,18 +106,20 @@ describe('lib/co2js: ', () => { timestamp: '2021-01-01T00:00:00Z', duration: 3600, 'network/data': 10, + 'green-web-host': true, 'carbon-operational': 2319.583333333333, }, ]); }); it('returns a result when `green-web-host` is false.', async () => { - const config = {type: '1byte', 'green-web-host': false}; + const config = {type: '1byte'}; const inputs = [ { timestamp: '2021-01-01T00:00:00Z', duration: 3600, 'network/data/bytes': 100000, + 'green-web-host': false, }, ]; const result = await output.execute(inputs, config); @@ -124,6 +131,7 @@ describe('lib/co2js: ', () => { timestamp: '2021-01-01T00:00:00Z', duration: 3600, 'network/data/bytes': 100000, + 'green-web-host': false, 'carbon-operational': 0.029081299999999994, }, ]); @@ -131,12 +139,13 @@ describe('lib/co2js: ', () => { it('returns a result when `type` has `swg` value in the config.', async () => { process.env.SWD_TYPE = 'true'; - const config = {type: 'swd', 'green-web-host': true}; + const config = {type: 'swd'}; const inputs = [ { timestamp: '2021-01-01T00:00:00Z', duration: 3600, 'network/data/bytes': 100000, + 'green-web-host': true, }, ]; @@ -149,35 +158,28 @@ describe('lib/co2js: ', () => { timestamp: '2021-01-01T00:00:00Z', duration: 3600, 'network/data/bytes': 100000, + 'green-web-host': true, 'carbon-operational': 0.023208995205000006, }, ]); }); - it('returns a result when provided `options` in the global config.', async () => { + it('returns a result when `green-web-host` and `options` are provided in input.', async () => { process.env.SWD_TYPE = 'true'; - const config = {type: 'swd', 'green-web-host': false}; - const output = Co2js({ - options: { - dataReloadRatio: 0.6, - firstVisitPercentage: 0.9, - returnVisitPercentage: 0.1, - gridIntensity: { - device: 560.98, - dataCenter: 50, - networks: 437.66, - }, - }, - }); - + const config = {type: 'swd'}; const inputs = [ { timestamp: '2021-01-01T00:00:00Z', duration: 3600, 'network/data/bytes': 100000, + 'green-web-host': false, + options: { + dataReloadRatio: 0.6, + firstVisitPercentage: 0.9, + returnVisitPercentage: 0.1, + }, }, ]; - const result = await output.execute(inputs, config); expect.assertions(1); @@ -187,7 +189,13 @@ describe('lib/co2js: ', () => { timestamp: '2021-01-01T00:00:00Z', duration: 3600, 'network/data/bytes': 100000, - 'carbon-operational': 0.034497244224, + 'carbon-operational': 0.034032441600000005, + 'green-web-host': false, + options: { + dataReloadRatio: 0.6, + firstVisitPercentage: 0.9, + returnVisitPercentage: 0.1, + }, }, ]); }); @@ -200,6 +208,7 @@ describe('lib/co2js: ', () => { timestamp: '2021-01-01T00:00:00Z', duration: 3600, 'network/data/bytes': 100000, + 'green-web-host': true, }, ]; @@ -223,6 +232,7 @@ describe('lib/co2js: ', () => { timestamp: '2021-01-01T00:00:00Z', duration: 3600, 'network/data/bytes': 100000, + 'green-web-host': true, }, ]; @@ -242,6 +252,7 @@ describe('lib/co2js: ', () => { { timestamp: '2021-01-01T00:00:00Z', duration: 3600, + 'green-web-host': true, }, ]; diff --git a/src/lib/co2js/README.md b/src/lib/co2js/README.md index fc394d3..895823a 100644 --- a/src/lib/co2js/README.md +++ b/src/lib/co2js/README.md @@ -4,8 +4,16 @@ # Parameters -## Plugin global config +## Plugin node config + +- `type`: supported plugins by the library, `swd` or `1byte` +## Inputs + +- `network/data/bytes`: the number of bytes transferred or `network/data` if the number is in GB +- `green-web-host`: true if the website is hosted on a green web host, false otherwise +- `duration`: the amount of time the observation covers, in seconds +- `timestamp`: a timestamp for the observation - `options`: **SWD Plugin Only** an object containing any Sustainable Web Design specific variables to the changed. All keys are optional. - `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. @@ -16,16 +24,6 @@ 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. -## Plugin node config - -- `type`: supported plugins by the library, `swd` or `1byte` -- `green-web-host`: true if the website is hosted on a green web host, false otherwise - -## Inputs - -- `network/data/bytes`: the number of bytes transferred or `network/data` if the number is in GB -- `duration`: the amount of time the observation covers, in seconds -- `timestamp`: a timestamp for the observation ## Returns @@ -58,15 +56,8 @@ initialize: co2js: method: Co2js path: '@grnsft/if-unofficial-plugins' - global-config: - options: - dataReloadRatio: 0.6 - firstVisitPercentage: 0.9 - returnVisitPercentage: 0.1 - gridIntensity: - device: 560.98 - dataCenter: - country: 'TWN' + outputs: + - yaml tree: children: child: @@ -75,11 +66,19 @@ tree: config: co2js: type: swd - green-web-host: true inputs: - timestamp: 2023-07-06T00:00 duration: 1 network/data/bytes: 1000000 + green-web-host: true + options: + dataReloadRatio: 0.6 + firstVisitPercentage: 0.9 + returnVisitPercentage: 0.1 + gridIntensity: + device: 560.98 + dataCenter: + country: 'TWN' ``` You can run this by passing it to `ie`. Run impact using the following command run from the project root: @@ -101,15 +100,8 @@ initialize: co2js: path: '@grnsft/if-unofficial-plugins' method: Co2js - global-config: - options: - dataReloadRatio: 0.6 - firstVisitPercentage: 0.9 - returnVisitPercentage: 0.1 - gridIntensity: - device: 560.98 - dataCenter: - country: TWN + outputs: + - yaml tree: children: child: @@ -118,15 +110,32 @@ tree: config: co2js: type: swd - green-web-host: true inputs: - timestamp: 2023-07-06T00:00 duration: 1 network/data/bytes: 1000000 + green-web-host: true + options: + dataReloadRatio: 0.6 + firstVisitPercentage: 0.9 + returnVisitPercentage: 0.1 + gridIntensity: + device: 560.98 + dataCenter: + country: TWN outputs: - timestamp: 2023-07-06T00:00 duration: 1 network/data/bytes: 1000000 + green-web-host: true + options: + dataReloadRatio: 0.6 + firstVisitPercentage: 0.9 + returnVisitPercentage: 0.1 + gridIntensity: + device: 560.98 + dataCenter: + country: TWN carbon-operational: 0.34497244224000007 ``` @@ -139,31 +148,29 @@ You can see example Typescript invocations for each plugin below. ```typescript import {Co2js} from '@grnsft/if-unofficial-plugins'; -const globalConfig = { - options: { - // Optional - dataReloadRatio: 0.6, - firstVisitPercentage: 0.9, - returnVisitPercentage: 0.1, - gridIntensity: { - device: 560.98, - dataCenter: 50, - networks: 437.66, - }, - }, -}; -const co2js = Co2js(globalConfig); +const co2js = Co2js(); const results = await co2js.execute( [ { duration: 3600, // duration institute timestamp: '2021-01-01T00:00:00Z', // ISO8601 / RFC3339 timestamp 'network/data/bytes': 1000000, // bytes transferred + 'green-web-host': true, // true if the website is hosted on a green web host, false otherwise + options: { + // Optional + dataReloadRatio: 0.6, + firstVisitPercentage: 0.9, + returnVisitPercentage: 0.1, + gridIntensity: { + device: 560.98, + dataCenter: 50, + networks: 437.66, + }, + }, }, ], { type: 'swd', - 'green-web-host': true, // true if the website is hosted on a green web host, false otherwise } ); ``` @@ -180,11 +187,11 @@ const results = await co2js.execute( duration: 3600, // duration institute timestamp: '2021-01-01T00:00:00Z', // ISO8601 / RFC3339 timestamp 'network/data/bytes': 1000000, // bytes transferred + 'green-web-host': true, // true if the website is hosted on a green web host, false otherwise }, - ], +], { type: '1byte', - 'green-web-host': true, // true if the website is hosted on a green web host, false otherwise } ); ``` diff --git a/src/lib/co2js/index.ts b/src/lib/co2js/index.ts index f316709..82862f9 100644 --- a/src/lib/co2js/index.ts +++ b/src/lib/co2js/index.ts @@ -4,32 +4,29 @@ import {z} from 'zod'; import {PluginInterface} from '../../interfaces'; import {ConfigParams, PluginParams} from '../../types'; -import {allDefined, validate} from '../../util/validations'; +import {validate} from '../../util/validations'; import {buildErrorMessage} from '../../util/helpers'; import {ERRORS} from '../../util/errors'; const {InputValidationError} = ERRORS; -export const Co2js = (globalConfig?: ConfigParams): PluginInterface => { +export const Co2js = (): PluginInterface => { const metadata = {kind: 'execute'}; const errorBuilder = buildErrorMessage(Co2js.name); - /** * Executes the plugin for a list of input parameters. */ const execute = async (inputs: PluginParams[], config?: ConfigParams) => { - const mergedValidatedConfig = Object.assign( - {}, - validateConfig(config), - validateGlobalConfig() - ); - const model = new co2({model: mergedValidatedConfig.type}); + const validatedConfig = validateConfig(config); + const model = new co2({model: validatedConfig.type}); return inputs.map(input => { + const validatedInput = validateInput(input); + const mergedWithConfig = Object.assign( {}, - validateInput(input), - mergedValidatedConfig + validatedConfig, + validatedInput ); const result = calculateResultByParams(mergedWithConfig, model); @@ -72,51 +69,41 @@ export const Co2js = (globalConfig?: ConfigParams): PluginInterface => { * Validates input parameters. */ const validateInput = (input: PluginParams) => { - const schema = z + const inputSchema = z .object({ - 'network/data/bytes': z.number(), - 'network/data': z.number(), + 'network/data/bytes': z.number().optional(), + 'network/data': z.number().optional(), + '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({ + 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(), }) - .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 || {}); + return validate>(inputSchema, input); }; /** @@ -131,14 +118,9 @@ export const Co2js = (globalConfig?: ConfigParams): PluginInterface => { ); } - const schema = z - .object({ - type: z.enum(['1byte', 'swd']), - 'green-web-host': z.boolean(), - }) - .refine(allDefined, { - message: '`type` and `green-web-host` are not provided in node config', - }); + const schema = z.object({ + type: z.enum(['1byte', 'swd']), + }); return validate>(schema, config); };