Skip to content

Commit

Permalink
Handle both V1 and V2 of Cirrus' data structure
Browse files Browse the repository at this point in the history
  • Loading branch information
Vinnl committed Jan 16, 2025
1 parent 98cb111 commit 2b7d8d7
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 12 deletions.
26 changes: 20 additions & 6 deletions src/app/functions/server/getExperiments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ import {
import { ExperimentationId } from "./getExperimentationId";
import { getEnabledFeatureFlags } from "../../../db/tables/featureFlags";

/**
* After we removing the `CirrusV2` flag, we can return the full `ExperimentData`/
* But until then, we can make the old experiment data object look like the new one,
* but we can't backfill the `Enrollments` property.
*/
export type ExperimentData_V2_Or_V2LikeV1 = Partial<ExperimentData> &
Required<Pick<ExperimentData, "Features">>;

/**
* Call the Cirrus sidecar, which returns a list of eligible experiments for the current user.
*
Expand All @@ -28,7 +36,7 @@ export async function getExperiments(params: {
locale: string;
countryCode: string;
previewMode: boolean;
}): Promise<ExperimentData> {
}): Promise<ExperimentData_V2_Or_V2LikeV1> {
if (["local"].includes(process.env.APP_ENV ?? "local")) {
return localExperimentData;
}
Expand Down Expand Up @@ -78,16 +86,22 @@ export async function getExperiments(params: {
throw new Error(`Cirrus request failed: ${response.statusText}`);
}

const json = await response.json();
// With the `CirrusV2` flag enabled, the response is `ExperimentData`,
// otherwise, it's just `ExperimentData["Features"]`.
const json = (await response.json()) as
| ExperimentData
| ExperimentData["Features"];

let experimentData;
let experimentData: ExperimentData_V2_Or_V2LikeV1;
if (flags.includes("CirrusV2")) {
experimentData = json["Features"];
experimentData = json as ExperimentData;
} else {
experimentData = json;
experimentData = {
Features: json as ExperimentData["Features"],
};
}

return (experimentData as ExperimentData) ?? defaultExperimentData;
return experimentData ?? defaultExperimentData;
} catch (ex) {
logger.error("Could not connect to Cirrus", {
serverUrl,
Expand Down
5 changes: 4 additions & 1 deletion src/app/hooks/useGlean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ export const useGlean = () => {
// Record the `nimbus_*` keys on all events.
// `nimbus_*` is set on every metric, but it's too much work for TypeScript
// to infer that — hence the type assertion.
if (experimentData) {
if (
experimentData &&
typeof experimentData["Enrollments"] !== "undefined"
) {
(data as GleanMetricMap["button"]["click"]).nimbus_user_id =
experimentData["Enrollments"]["nimbus_user_id"];
(data as GleanMetricMap["button"]["click"]).nimbus_app_id =
Expand Down
7 changes: 5 additions & 2 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,11 @@ export default async function RootLayout({
previewMode: nimbusPreviewMode === "true",
});

const nimbus_user_id = experimentData["Enrollments"].nimbus_user_id;
if (nimbus_user_id !== experimentationId) {
const nimbus_user_id = experimentData["Enrollments"]?.nimbus_user_id;
if (
typeof nimbus_user_id !== "undefined" &&
nimbus_user_id !== experimentationId
) {
Sentry.captureMessage(
`Nimbus user ID from Cirrus: [${nimbus_user_id}] did not match experimentationId: [${experimentationId}]`,
);
Expand Down
7 changes: 4 additions & 3 deletions src/contextProviders/experiments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
"use client";

import { ReactNode, createContext, useContext } from "react";
import { ExperimentData } from "../telemetry/generated/nimbus/experiments";
import { ExperimentData_V2_Or_V2LikeV1 } from "../app/functions/server/getExperiments";

interface ExperimentsProviderProps {
children: ReactNode;
experimentData: ExperimentData;
experimentData: ExperimentData_V2_Or_V2LikeV1;
}

export const ExperimentsContext = createContext<ExperimentData | null>(null);
export const ExperimentsContext =
createContext<ExperimentData_V2_Or_V2LikeV1 | null>(null);

export const ExperimentsProvider = ({
children,
Expand Down

0 comments on commit 2b7d8d7

Please sign in to comment.