Skip to content

Commit

Permalink
Adds user agent header for requests to Green Web Foundation APIs
Browse files Browse the repository at this point in the history
the version from package.json is used to inject an environment variable
when esbuild runs, which the running code can read to create the correct
user agent string when making requests.

fixes thegreenwebfoundation#181
  • Loading branch information
sfishel18 committed Dec 31, 2023
1 parent 51b85e2 commit 9de35bb
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 18 deletions.
3 changes: 3 additions & 0 deletions .esbuild.browser.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
const esbuildCommon = require("./.esbuild.common");

require('esbuild').buildSync({
...esbuildCommon,
entryPoints: ['src/index.js'],
outdir: 'dist/iife',
globalName: 'co2',
Expand Down
7 changes: 7 additions & 0 deletions .esbuild.common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const CO2JS_VERSION = require("./package.json").version;

module.exports = {
define: {
"process.env.CO2JS_VERSION": JSON.stringify(CO2JS_VERSION),
},
};
4 changes: 3 additions & 1 deletion .esbuild.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ const esbuild = require('esbuild')
// For this build however we need to filter out some extra files
// that are used for nodejs, but not in browsers, so we use the
// library directly instead of using `esbuild-plugin-glob` as a plugin
const glob = require('tiny-glob');
const glob = require('tiny-glob')
const esbuildCommon = require('./.esbuild.common')

async function main() {
const results = await glob('src/**/!(*.test.js|test-constants.js|!(*.js))')
Expand All @@ -12,6 +13,7 @@ async function main() {
const justBrowserCompatibleFiles = results.filter(filepath => !filepath.endsWith('node.js'))

esbuild.build({
...esbuildCommon,
entryPoints: justBrowserCompatibleFiles,
bundle: false,
minify: false,
Expand Down
2 changes: 2 additions & 0 deletions .esbuild.node.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const { globPlugin } = require('esbuild-plugin-glob');
const esbuildCommon = require('./.esbuild.common');

function main() {
require('esbuild').build({
...esbuildCommon,
entryPoints: ['src/**/!(*.test.js|test-constants.js|!(*.js))'],
bundle: false,
minify: false,
Expand Down
11 changes: 10 additions & 1 deletion src/helpers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,13 @@ function parseOptions(options) {
return adjustments;
}

export { formatNumber, parseOptions };
/**
* Returns an object containing all the HTTP headers to use when making a request to the Green Web Foundation API.
*
* @returns {import('http').OutgoingHttpHeaders}
*/
function getApiRequestHeaders() {
return { "user-agent": `co2js/${process.env.CO2JS_VERSION}` };
}

export { formatNumber, parseOptions, getApiRequestHeaders };
11 changes: 9 additions & 2 deletions src/hosting-api.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"use strict";

import { getApiRequestHeaders } from "./helpers";

/**
* Check if a string or array of domains has been provided
* @param {string|array} domain - The domain to check, or an array of domains to be checked.
Expand All @@ -21,7 +23,10 @@ function check(domain) {
*/
async function checkAgainstAPI(domain) {
const req = await fetch(
`https://api.thegreenwebfoundation.org/greencheck/${domain}`
`https://api.thegreenwebfoundation.org/greencheck/${domain}`,
{
headers: getApiRequestHeaders(),
}
);
const res = await req.json();
return res.green;
Expand All @@ -38,7 +43,9 @@ async function checkDomainsAgainstAPI(domains) {
const apiPath = "https://api.thegreenwebfoundation.org/v2/greencheckmulti";
const domainsString = JSON.stringify(domains);

const req = await fetch(`${apiPath}/${domainsString}`);
const req = await fetch(`${apiPath}/${domainsString}`, {
headers: getApiRequestHeaders(),
});

const allGreenCheckResults = await req.json();

Expand Down
17 changes: 17 additions & 0 deletions src/hosting-api.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import hosting from "./hosting-node.js";
import nock from "nock";
/* eslint-disable jest/no-disabled-tests */

process.env.CO2JS_VERSION = "1.2.34";

describe("hostingAPI", () => {
describe("checking a single domain with #check", () => {
it.skip("using the API", async () => {
Expand All @@ -15,6 +18,20 @@ describe("hostingAPI", () => {
const res = await hosting.check("google.com");
expect(res).toEqual(true);
});
it("sets the correct user agent header", async () => {
let userAgent;
const scope = nock("https://api.thegreenwebfoundation.org/")
.get("/greencheck/google.com")
.reply(200, function () {
userAgent = this.req.headers["user-agent"];
return {
url: "google.com",
green: true,
};
});
const res = await hosting.check("google.com");
expect(userAgent).toEqual("co2js/1.2.34");
});
});
describe("implicitly checking multiple domains with #check", () => {
it.skip("using the API", async () => {
Expand Down
33 changes: 19 additions & 14 deletions src/hosting-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This lets us keep the total library small, and dependencies minimal.
import https from "https";

import hostingJSON from "./hosting-json.node.js";
import { getApiRequestHeaders } from "./helpers/index.js";

/**
* Accept a url and perform an http request, returning the body
Expand All @@ -22,22 +23,26 @@ import hostingJSON from "./hosting-json.node.js";
async function getBody(url) {
return new Promise(function (resolve, reject) {
// Do async job
const req = https.get(url, function (res) {
if (res.statusCode < 200 || res.statusCode >= 300) {
return reject(
new Error(
`Could not get info from: ${url}. Status Code: ${res.statusCode}`
)
);
}
const data = [];
const req = https.get(
url,
{ headers: getApiRequestHeaders() },
function (res) {
if (res.statusCode < 200 || res.statusCode >= 300) {
return reject(
new Error(
`Could not get info from: ${url}. Status Code: ${res.statusCode}`
)
);
}
const data = [];

res.on("data", (chunk) => {
data.push(chunk);
});
res.on("data", (chunk) => {
data.push(chunk);
});

res.on("end", () => resolve(Buffer.concat(data).toString()));
});
res.on("end", () => resolve(Buffer.concat(data).toString()));
}
);
req.end();
});
}
Expand Down
17 changes: 17 additions & 0 deletions src/hosting.test.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
"use strict";

import fs from "fs";
import https from "https";
import path from "path";

import pagexray from "pagexray";

import hosting from "./hosting-node.js";

process.env.CO2JS_VERSION = "1.2.34";

const jsonPath = path.resolve(
__dirname,
"..",
Expand All @@ -17,13 +20,18 @@ const jsonPath = path.resolve(

describe("hosting", () => {
let har;
let httpsGetSpy;
beforeEach(() => {
har = JSON.parse(
fs.readFileSync(
path.resolve(__dirname, "../data/fixtures/tgwf.har"),
"utf8"
)
);
httpsGetSpy = jest.spyOn(https, "get");
});
afterEach(() => {
jest.restoreAllMocks();
});
describe("checking all domains on a page object with #checkPage", () => {
it("returns a list of green domains, when passed a page object", async () => {
Expand Down Expand Up @@ -57,6 +65,15 @@ describe("hosting", () => {
const res = await hosting.check("google.com");
expect(res).toEqual(true);
});
it("sets the correct user agent header", async () => {
await hosting.check("google.com");
expect(httpsGetSpy).toHaveBeenCalledTimes(1);
expect(httpsGetSpy).toHaveBeenLastCalledWith(
expect.any(String),
expect.objectContaining({ headers: { "user-agent": "co2js/1.2.34" } }),
expect.any(Function)
);
});
});
describe("checking multiple domains with #check", () => {
it("Use the API", async () => {
Expand Down

0 comments on commit 9de35bb

Please sign in to comment.