Adds world_cup_matches and world_cup_live as feature-gated tools backed by a small WCS Merino client. Pref-gated behind browser.smartwindow.worldcup.enabled (default false). Backend endpoints land via bug 2037696. Feature-gated tools are dispatched via a Map lookup before the main switch so future additions don't keep growing fetchWithHistory's complexity. Differential Revision: https://phabricator.services.mozilla.com/D299461
100 lines
3.1 KiB
JavaScript
100 lines
3.1 KiB
JavaScript
/**
|
|
* 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);
|
|
}
|
|
}
|
|
}
|