Skip to content

Commit

Permalink
Adds support for verbose response when checking domains
Browse files Browse the repository at this point in the history
  • Loading branch information
fershad authored Apr 22, 2024
2 parents 91c1123 + a25a93e commit d012292
Show file tree
Hide file tree
Showing 11 changed files with 334 additions and 137 deletions.
2 changes: 1 addition & 1 deletion __mocks__/https.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ https.get.mockImplementation((url, options, callback) => {
stream.emit(
"data",
Buffer.from(
`{"google.com": {"url":"google.com","hosted_by":"Google Inc.","hosted_by_website":"https://www.google.com","partner":null,"green":true}}`
`{"google.com": {"url":"google.com","hosted_by":"Google Inc.","hosted_by_website":"https://www.google.com","partner":null,"green":true}, "pchome.com": {"url":"pchome.com","green":false} }`
)
);
} else {
Expand Down
25 changes: 0 additions & 25 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/helpers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ import {
FIRST_TIME_VIEWING_PERCENTAGE,
RETURNING_VISITOR_PERCENTAGE,
} from "../constants/index.js";

// Shared type definitions to be used across different files

/**
* @typedef {Object} DomainCheckOptions options to control the behavior when checking a domain
* @property {string} userAgentIdentifier - Optional. The app, site, or organisation that is making the request.
* @property {boolean} verbose - Optional. Whether to return a verbose response.
* @property {string[]} db - Optional. A database list to use for lookups.
*/

const formatNumber = (num) => parseFloat(num.toFixed(2));

function parseOptions(options) {
Expand Down
49 changes: 33 additions & 16 deletions src/hosting-api.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,77 @@
"use strict";

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

/**
* Check if a string or array of domains has been provided
* Check if a string or array of domains is hosted by a green web host by querying the Green Web Foundation API.
* @param {string|array} domain - The domain to check, or an array of domains to be checked.
* @param {string} userAgentIdentifier - Optional. The app, site, or organisation that is making the request.
* @param {string | DomainCheckOptions} optionsOrAgentId - Optional. An object of domain check options, or a string
* representing the app, site, or organisation that is making the request.
*/

function check(domain, userAgentIdentifier) {
function check(domain, optionsOrAgentId) {
const options =
typeof optionsOrAgentId === "string"
? { userAgentIdentifier: optionsOrAgentId }
: optionsOrAgentId;

if (options?.db && options.verbose) {
throw new Error("verbose mode cannot be used with a local lookup database");
}
// is it a single domain or an array of them?
if (typeof domain === "string") {
return checkAgainstAPI(domain, userAgentIdentifier);
return checkAgainstAPI(domain, options);
} else {
return checkDomainsAgainstAPI(domain, userAgentIdentifier);
return checkDomainsAgainstAPI(domain, options);
}
}

/**
* Check if a domain is hosted by a green web host by querying the Green Web Foundation API.
* @param {string} domain - The domain to check.
* @param {string} userAgentIdentifier - Optional. The app, site, or organisation that is making the request.
* @returns {boolean} - A boolean indicating whether the domain is hosted by a green web host.
* @param {DomainCheckOptions} options
* @returns - A boolean indicating whether the domain is hosted by a green web host if `options.verbose` is false,
* otherwise an object representing the domain host information.
*/
async function checkAgainstAPI(domain, userAgentIdentifier) {
async function checkAgainstAPI(domain, options = {}) {
const req = await fetch(
`https://api.thegreenwebfoundation.org/greencheck/${domain}`,
{
headers: getApiRequestHeaders(userAgentIdentifier),
headers: getApiRequestHeaders(options.userAgentIdentifier),
}
);
if (options?.db) {
return hostingJSON.check(domain, options.db);
}
const res = await req.json();
return res.green;
return options.verbose ? res : res.green;
}

/**
* Check if an array of domains is hosted by a green web host by querying the Green Web Foundation API.
* @param {array} domains - An array of domains to check.
* @param {string} userAgentIdentifier - Optional. The app, site, or organisation that is making the request.
* @returns {array} - An array of domains that are hosted by a green web host.
* @param {DomainCheckOptions} options
* @returns - An array of domains that are hosted by a green web host if `options.verbose` is false,
* otherwise a dictionary of domain to host information.
*/

async function checkDomainsAgainstAPI(domains, userAgentIdentifier) {
async function checkDomainsAgainstAPI(domains, options = {}) {
try {
const apiPath = "https://api.thegreenwebfoundation.org/v2/greencheckmulti";
const domainsString = JSON.stringify(domains);

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

const allGreenCheckResults = await req.json();

return greenDomainsFromResults(allGreenCheckResults);
return options.verbose
? allGreenCheckResults
: greenDomainsFromResults(allGreenCheckResults);
} catch (e) {
return [];
return options.verbose ? {} : [];
}
}

Expand Down
62 changes: 62 additions & 0 deletions src/hosting-api.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,33 @@ describe("hostingAPI", () => {
);
expect(res).toEqual(true);
});
it("handles the verbose=true option", async () => {
fetch.mockImplementation(() =>
Promise.resolve({
json: () =>
Promise.resolve({
url: "google.com",
hosted_by: "Google Inc.",
hosted_by_website: "https://www.google.com",
green: true,
}),
})
);
const res = await hosting.check("google.com", { verbose: true });
expect(fetch).toHaveBeenCalledTimes(1);
expect(fetch).toHaveBeenLastCalledWith(
expect.any(String),
expect.objectContaining({
headers: { "User-Agent": "co2js/1.2.34 " },
})
);
expect(res).toMatchObject({
green: true,
hosted_by: "Google Inc.",
hosted_by_website: "https://www.google.com",
url: "google.com",
});
});
});
describe("implicitly checking multiple domains with #check", () => {
it("using the API", async () => {
Expand Down Expand Up @@ -69,6 +96,41 @@ describe("hostingAPI", () => {
);
expect(res).toContain("google.com");
});
it("handles the verbose=true option", async () => {
fetch.mockImplementation(() =>
Promise.resolve({
json: () =>
Promise.resolve({
"google.com": {
url: "google.com",
hosted_by: "Google Inc.",
hosted_by_website: "https://www.google.com",
green: true,
},
"kochindustries.com": {
url: "kochindustries.com",
green: false,
},
}),
})
);
const res = await hosting.check(["google.com", "kochindustries.com"], {
verbose: true,
});
expect(fetch).toHaveBeenCalledTimes(1);
expect(res).toEqual({
"google.com": expect.objectContaining({
green: true,
hosted_by: "Google Inc.",
hosted_by_website: "https://www.google.com",
url: "google.com",
}),
"kochindustries.com": expect.objectContaining({
url: "kochindustries.com",
green: false,
}),
});
});
});
});
/* eslint-enable jest/no-disabled-tests */
105 changes: 105 additions & 0 deletions src/hosting-json.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"use strict";

/**
* 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.
*/
async function check(domain, db) {
// is it a single domain or an array of them?
if (typeof domain === "string") {
return checkInJSON(domain, db);
} else {
return checkDomainsInJSON(domain, db);
}
}

/**
* Check if a domain is hosted by a green web host by querying the database.
* @param {string} domain - The domain to check.
* @param {object} db - The database to check against.
* @returns {boolean} - A boolean indicating whether the domain is hosted by a green web host.
*/
function checkInJSON(domain, db) {
if (db.indexOf(domain) > -1) {
return true;
}
return false;
}

/**
* Extract the green domains from the results of a green check.
* @param {object} greenResults - The results of a green check.
* @returns {array} - An array of domains that are hosted by a green web host.
*/
function greenDomainsFromResults(greenResults) {
const entries = Object.entries(greenResults);
const greenEntries = entries.filter(([key, val]) => val.green);

return greenEntries.map(([key, val]) => val.url);
}

/**
* Check if an array of domains is hosted by a green web host by querying the database.
* @param {array} domains - An array of domains to check.
* @param {object} db - The database to check against.
* @returns {array} - An array of domains that are hosted by a green web host.
*/
function checkDomainsInJSON(domains, db) {
let greenDomains = [];

for (let domain of domains) {
if (db.indexOf(domain) > -1) {
greenDomains.push(domain);
}
}
return greenDomains;
}

/**
* Find the provided information a string or array of domains
* @param {string|array} domain - The domain to check, or an array of domains to be checked.
*/
function find(domain, db) {
// is it a single domain or an array of them?
if (typeof domain === "string") {
return findInJSON(domain, db);
} else {
return findDomainsInJSON(domain, db);
}
}

/**
* Check if a domain is hosted by a green web host by querying the database.
* @param {string} domain - The domain to check.
* @param {object} db - The database to check against.
* @returns {object} - An object representing the domain provided host information.
*/
function findInJSON(domain, db) {
if (db.indexOf(domain) > -1) {
return domain;
}
return {
url: domain,
green: false,
};
}

/**
* Check if an array of domains is hosted by a green web host by querying the database.
* @param {array} domains - An array of domains to check.
* @param {object} db - The database to check against.
* @returns {array} - A dictionary of domain to provided host information.
*/
function findDomainsInJSON(domains, db) {
const result = {};
for (let domain of domains) {
result[domain] = findInJSON(domain, db);
}
return result;
}

module.exports = {
check,
greenDomainsFromResults,
find,
};
Loading

0 comments on commit d012292

Please sign in to comment.