Support a `local` channel for local development

In local development, the Nimbus sidecar isn't available, so we
just explicitly load the default values for the `local` channel.
This commit is contained in:
Vincent 2024-04-26 14:14:38 +02:00 коммит произвёл Vincent
Родитель 56bea1a528
Коммит 0d52cb1510
3 изменённых файлов: 60 добавлений и 32 удалений

Просмотреть файл

@ -1,6 +1,7 @@
about:
description: Nimbus Feature Manifest for Monitor Web testing
channels:
- local
- staging
- production
features:
@ -12,6 +13,8 @@ features:
type: Boolean
default: false
defaults:
- channel: local
value: { "enabled": true }
- channel: staging
value: { "enabled": true }
- channel: production

Просмотреть файл

@ -7,6 +7,7 @@ import { logger } from "./logging";
import {
ExperimentData,
defaultExperimentData,
localExperimentData,
} from "../../../telemetry/generated/nimbus/experiments";
import { ExperimentationId } from "./getExperimentationId";
@ -25,34 +26,35 @@ export async function getExperiments(params: {
locale: string;
countryCode: string;
}): Promise<ExperimentData> {
if (["stage", "production"].includes(process.env.APP_ENV ?? "local")) {
const serverUrl = process.env.NIMBUS_SIDECAR_URL;
if (!serverUrl) {
throw new Error("env var NIMBUS_SIDECAR_URL not set");
}
try {
const response = await fetch(`${serverUrl}/v1/features`, {
headers: {
"Content-Type": "application/json",
},
method: "POST",
body: JSON.stringify({
client_id: params.experimentationId,
context: {
// Nimbus takes a language, rather than a locale, hence the .split:
locale: params.locale.split("-")[0],
countryCode: params.countryCode,
},
}),
});
const experimentData = (await response.json()) as ExperimentData;
return experimentData ?? defaultExperimentData;
} catch (ex) {
logger.error(`Could not connect to Cirrus on ${serverUrl}`, ex);
captureException(ex);
}
if (["local"].includes(process.env.APP_ENV ?? "local")) {
return localExperimentData;
}
const serverUrl = process.env.NIMBUS_SIDECAR_URL;
if (!serverUrl) {
throw new Error("env var NIMBUS_SIDECAR_URL not set");
}
try {
const response = await fetch(`${serverUrl}/v1/features`, {
headers: {
"Content-Type": "application/json",
},
method: "POST",
body: JSON.stringify({
client_id: params.experimentationId,
context: {
// Nimbus takes a language, rather than a locale, hence the .split:
locale: params.locale.split("-")[0],
countryCode: params.countryCode,
},
}),
});
const experimentData = (await response.json()) as ExperimentData;
return experimentData ?? defaultExperimentData;
} catch (ex) {
logger.error(`Could not connect to Cirrus on ${serverUrl}`, ex);
captureException(ex);
return defaultExperimentData;
}
return defaultExperimentData;
}

Просмотреть файл

@ -12,7 +12,7 @@ run();
* @typedef {"String" | "Boolean" | "Int" | "Text" | "Image" | `Option<${Type}>` | `List<${Type}>` | `Map<${Type}, ${Type}>`} Type
*/
/**
* @typedef {"staging" | "production"} Channel
* @typedef {"local" | "staging" | "production"} Channel
*/
/**
* @typedef {Record<
@ -32,7 +32,7 @@ run();
* features: Record<string, {
* description: string;
* variables: Variables;
* defaults: Array<{
* defaults?: Array<{
* channel: Channel;
* values: Record<keyof Variables, unknown>;
* }>;
@ -64,7 +64,9 @@ async function run() {
"// AUTOGENERATED `npm run build-nimbus`. DO NOT EDIT. DO NOT COMMIT.\n\n" +
getFeaturesTypeDef(nimbusConfig) +
"\n" +
getFallbackObject(nimbusConfig);
getFallbackObject(nimbusConfig) +
"\n" +
getLocalOverrides(nimbusConfig);
await mkdir("src/telemetry/generated/nimbus/", { recursive: true });
await writeFile("src/telemetry/generated/nimbus/experiments.ts", typedef);
}
@ -90,6 +92,27 @@ function getFallbackObject(nimbusConfig) {
return `export const defaultExperimentData: ExperimentData = {\n${featureFallbackDefs.join("\n")}};\n`;
}
/**
* @param {NimbusConfig} nimbusConfig
* @returns string
*/
function getLocalOverrides(nimbusConfig) {
const featureLocalOverridesDefs = Object.keys(nimbusConfig.features).map(
(featureId) => {
const localOverrides = nimbusConfig.features[featureId].defaults?.find(
(defaultData) => defaultData.channel === "local",
);
const overriddenValuesDef =
typeof localOverrides === "undefined"
? ""
: ` ...${JSON.stringify(localOverrides.value, null, 2).replaceAll("\n", "\n ")}\n`;
return ` "${featureId}": {\n ...defaultExperimentData["${featureId}"],\n${overriddenValuesDef} },\n`;
},
);
return `export const localExperimentData: ExperimentData = {\n${featureLocalOverridesDefs.join("\n")}};\n`;
}
/**
* @param {NimbusConfig} nimbusConfig
* @returns string