Source code

Revision control

Copy as Markdown

Other Tools

/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/**
* Small client for the Merino World Cup Soccer endpoints.
*
* Lives alongside the Smart Window tool that consumes it. Intentionally
* kept independent of newtab's TemporaryMerinoClientShim, which is shaped
* around `/api/v1/suggest` semantics that don't match the WCS responses.
*/
const ENDPOINT_PREF = "browser.smartwindow.worldcup.endpointURL";
const TIMEOUT_PREF = "browser.smartwindow.worldcup.timeoutMs";
const DEFAULT_ENDPOINT = "https://merino.services.mozilla.com";
const DEFAULT_TIMEOUT_MS = 2000;
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
clearTimeout: "resource://gre/modules/Timer.sys.mjs",
setTimeout: "resource://gre/modules/Timer.sys.mjs",
});
ChromeUtils.defineLazyGetter(lazy, "console", () =>
console.createInstance({
prefix: "WCSMerinoClient",
maxLogLevelPref: "browser.smartwindow.conversation.logLevel",
})
);
/**
* Static client for the Merino World Cup Soccer endpoints.
*/
export class WCSMerinoClient {
/**
* Fetch World Cup matches in a +/- 7 day window around `date`.
*
* @param {object} [params]
* @param {string} [params.date] RFC date YYYY-MM-DD; server defaults to today UTC.
* @param {string} [params.teams] Comma-separated 3-letter team keys, e.g. "BRA,ARG".
* @param {number} [params.limit] Max matches to return.
* @returns {Promise<{previous: object[], current: object[], next: object[]}>}
*/
static fetchMatches(params = {}) {
return WCSMerinoClient.#get("/api/v1/wcs/matches", params);
}
/**
* Fetch World Cup matches currently in progress.
*
* @param {object} [params]
* @param {string} [params.teams] Comma-separated 3-letter team keys.
* @returns {Promise<{matches: object[]}>}
*/
static fetchLive(params = {}) {
return WCSMerinoClient.#get("/api/v1/wcs/live", params);
}
static #endpointBase() {
return Services.prefs.getStringPref(ENDPOINT_PREF, DEFAULT_ENDPOINT);
}
static #timeoutMs() {
return Services.prefs.getIntPref(TIMEOUT_PREF, DEFAULT_TIMEOUT_MS);
}
static async #get(path, params) {
const url = new URL(path, WCSMerinoClient.#endpointBase());
for (const [key, value] of Object.entries(params)) {
if (value === undefined || value === null || value === "") {
continue;
}
url.searchParams.set(key, String(value));
}
const controller = new AbortController();
const timer = lazy.setTimeout(
() => controller.abort(),
WCSMerinoClient.#timeoutMs()
);
try {
const response = await fetch(url.toString(), {
signal: controller.signal,
credentials: "omit",
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
lazy.console.warn(`WCS request to ${path} failed:`, error);
throw error;
} finally {
lazy.clearTimeout(timer);
}
}
}