Skip to content

Commit

Permalink
attempt to use context for experiment data and glean
Browse files Browse the repository at this point in the history
  • Loading branch information
rhelmer committed Dec 20, 2024
1 parent 2bd45be commit 6b2f4d4
Show file tree
Hide file tree
Showing 9 changed files with 453 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { getExperiments } from "../../../../../../../functions/server/getExperim
import { getLocale } from "../../../../../../../functions/universal/getLocale";
import { getL10n } from "../../../../../../../functions/l10n/serverComponents";
import { getDataBrokerRemovalTimeEstimates } from "../../../../../../../functions/server/getDataBrokerRemovalTimeEstimates";
import { ExperimentsProvider } from "../../../../../../../../contextProviders/experiments";

const dashboardTabSlugs = ["action-needed", "fixed"];

Expand Down Expand Up @@ -152,27 +153,29 @@ export default async function DashboardPage({ params, searchParams }: Props) {
const signInCount = await getSignInCount(session.user.subscriber.id);

return (
<View
user={session.user}
isEligibleForPremium={userIsEligibleForPremium}
isEligibleForFreeScan={userIsEligibleForFreeScan}
userScanData={latestScan}
userBreaches={subBreaches}
enabledFeatureFlags={enabledFeatureFlags}
monthlySubscriptionUrl={`${monthlySubscriptionUrl}&${additionalSubplatParams.toString()}`}
yearlySubscriptionUrl={`${yearlySubscriptionUrl}&${additionalSubplatParams.toString()}`}
subscriptionBillingAmount={getSubscriptionBillingAmount()}
fxaSettingsUrl={fxaSettingsUrl}
scanCount={scanCount}
totalNumberOfPerformedScans={profileStats?.total}
isNewUser={isNewUser}
elapsedTimeInDaysSinceInitialScan={elapsedTimeInDaysSinceInitialScan}
experimentData={experimentData}
activeTab={activeTab}
hasFirstMonitoringScan={hasFirstMonitoringScan}
signInCount={signInCount}
autoOpenUpsellDialog={searchParams.dialog === "subscriptions"}
removalTimeEstimates={getDataBrokerRemovalTimeEstimates(latestScan)}
/>
<ExperimentsProvider experimentData={experimentData}>
<View
user={session.user}
isEligibleForPremium={userIsEligibleForPremium}
isEligibleForFreeScan={userIsEligibleForFreeScan}
userScanData={latestScan}
userBreaches={subBreaches}
enabledFeatureFlags={enabledFeatureFlags}
monthlySubscriptionUrl={`${monthlySubscriptionUrl}&${additionalSubplatParams.toString()}`}
yearlySubscriptionUrl={`${yearlySubscriptionUrl}&${additionalSubplatParams.toString()}`}
subscriptionBillingAmount={getSubscriptionBillingAmount()}
fxaSettingsUrl={fxaSettingsUrl}
scanCount={scanCount}
totalNumberOfPerformedScans={profileStats?.total}
isNewUser={isNewUser}
elapsedTimeInDaysSinceInitialScan={elapsedTimeInDaysSinceInitialScan}
experimentData={experimentData["Features"]}
activeTab={activeTab}
hasFirstMonitoringScan={hasFirstMonitoringScan}
signInCount={signInCount}
autoOpenUpsellDialog={searchParams.dialog === "subscriptions"}
removalTimeEstimates={getDataBrokerRemovalTimeEstimates(latestScan)}
/>
</ExperimentsProvider>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { checkSession } from "../../../../../../../functions/server/checkSession
import { checkUserHasMonthlySubscription } from "../../../../../../../functions/server/user";
import { getEmailPreferenceForPrimaryEmail } from "../../../../../../../../db/tables/subscriber_email_preferences";
import { CONST_SETTINGS_TAB_SLUGS } from "../../../../../../../../constants";
import { ExperimentsProvider } from "../../../../../../../../contextProviders/experiments";

type Props = {
params: {
Expand Down Expand Up @@ -112,23 +113,25 @@ export default async function SettingsPage({ params, searchParams }: Props) {
);

return (
<SettingsView
l10n={getL10n()}
user={session.user}
subscriber={userData}
data={settingsData}
emailAddresses={emailAddresses}
breachCountByEmailAddress={breachCountByEmailAddress}
fxaSettingsUrl={fxaSettingsUrl}
fxaSubscriptionsUrl={fxaSubscriptionsUrl}
monthlySubscriptionUrl={`${monthlySubscriptionUrl}&${additionalSubplatParams.toString()}`}
yearlySubscriptionUrl={`${yearlySubscriptionUrl}&${additionalSubplatParams.toString()}`}
subscriptionBillingAmount={getSubscriptionBillingAmount()}
enabledFeatureFlags={enabledFeatureFlags}
experimentData={experimentData}
lastScanDate={lastOneRepScan?.created_at}
isMonthlySubscriber={isMonthlySubscriber}
activeTab={activeTab}
/>
<ExperimentsProvider experimentData={experimentData}>
<SettingsView
l10n={getL10n()}
user={session.user}
subscriber={userData}
data={settingsData}
emailAddresses={emailAddresses}
breachCountByEmailAddress={breachCountByEmailAddress}
fxaSettingsUrl={fxaSettingsUrl}
fxaSubscriptionsUrl={fxaSubscriptionsUrl}
monthlySubscriptionUrl={`${monthlySubscriptionUrl}&${additionalSubplatParams.toString()}`}
yearlySubscriptionUrl={`${yearlySubscriptionUrl}&${additionalSubplatParams.toString()}`}
subscriptionBillingAmount={getSubscriptionBillingAmount()}
enabledFeatureFlags={enabledFeatureFlags}
experimentData={experimentData["Features"]}
lastScanDate={lastOneRepScan?.created_at}
isMonthlySubscriber={isMonthlySubscriber}
activeTab={activeTab}
/>
</ExperimentsProvider>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { getExperimentationId } from "../../../../../../functions/server/getExpe
import { getExperiments } from "../../../../../../functions/server/getExperiments";
import { getLocale } from "../../../../../../functions/universal/getLocale";
import { getL10n } from "../../../../../../functions/l10n/serverComponents";
import { ExperimentsProvider } from "../../../../../../../contextProviders/experiments";

const FreeScanSlug = "free-scan";

Expand Down Expand Up @@ -71,13 +72,15 @@ export default async function Onboarding({ params, searchParams }: Props) {
});

return (
<View
user={session.user}
dataBrokerCount={CONST_ONEREP_DATA_BROKER_COUNT}
breachesTotalCount={allBreachesCount}
stepId={firstSlug === FreeScanSlug ? "enterInfo" : "getStarted"}
previousRoute={previousRoute}
experimentData={experimentData}
/>
<ExperimentsProvider experimentData={experimentData}>
<View
user={session.user}
dataBrokerCount={CONST_ONEREP_DATA_BROKER_COUNT}
breachesTotalCount={allBreachesCount}
stepId={firstSlug === FreeScanSlug ? "enterInfo" : "getStarted"}
previousRoute={previousRoute}
experimentData={experimentData["Features"]}
/>
</ExperimentsProvider>
);
}
51 changes: 28 additions & 23 deletions src/app/(proper_react)/(redesign)/(public)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { getExperimentationId } from "../../../functions/server/getExperimentati
import { getExperiments } from "../../../functions/server/getExperiments";
import { getLocale } from "../../../functions/universal/getLocale";
import { AccountsMetricsFlowProvider } from "../../../../contextProviders/accounts-metrics-flow";
import { ExperimentsProvider } from "../../../../contextProviders/experiments";

type Props = {
searchParams: {
Expand Down Expand Up @@ -53,28 +54,32 @@ export default async function Page({ searchParams }: Props) {
typeof oneRepActivations === "undefined" ||
oneRepActivations > monthlySubscribersQuota;
return (
<AccountsMetricsFlowProvider
enabled={experimentData["landing-page-free-scan-cta"].enabled}
metricsFlowParams={{
entrypoint: CONST_URL_MONITOR_LANDING_PAGE_ID,
entrypoint_experiment: "landing-page-free-scan-cta",
entrypoint_variation:
experimentData["landing-page-free-scan-cta"].variant,
form_type:
experimentData["landing-page-free-scan-cta"].variant ===
"ctaWithEmail"
? "email"
: "button",
service: process.env.OAUTH_CLIENT_ID as string,
}}
>
<View
eligibleForPremium={eligibleForPremium}
l10n={getL10n()}
countryCode={countryCode}
scanLimitReached={scanLimitReached}
experimentData={experimentData}
/>
</AccountsMetricsFlowProvider>
<ExperimentsProvider experimentData={experimentData}>
<AccountsMetricsFlowProvider
enabled={
experimentData["Features"]["landing-page-free-scan-cta"].enabled
}
metricsFlowParams={{
entrypoint: CONST_URL_MONITOR_LANDING_PAGE_ID,
entrypoint_experiment: "landing-page-free-scan-cta",
entrypoint_variation:
experimentData["Features"]["landing-page-free-scan-cta"].variant,
form_type:
experimentData["Features"]["landing-page-free-scan-cta"].variant ===
"ctaWithEmail"
? "email"
: "button",
service: process.env.OAUTH_CLIENT_ID as string,
}}
>
<View
eligibleForPremium={eligibleForPremium}
l10n={getL10n()}
countryCode={countryCode}
scanLimitReached={scanLimitReached}
experimentData={experimentData["Features"]}
/>
</AccountsMetricsFlowProvider>
</ExperimentsProvider>
);
}
2 changes: 1 addition & 1 deletion src/app/api/v1/user/welcome-scan/create/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export async function POST(
previewMode: searchParams.get("nimbus_preview") === "true",
});
const optionalInfoExperimentData =
experimentData["welcome-scan-optional-info"];
experimentData["Features"]["welcome-scan-optional-info"];

const profileData: CreateProfileRequest = {
first_name: firstName,
Expand Down
11 changes: 4 additions & 7 deletions src/app/functions/server/getExperiments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ export async function getExperiments(params: {
locale: string;
countryCode: string;
previewMode: boolean;
}): Promise<ExperimentData["Features"]> {
}): Promise<ExperimentData> {
if (["local"].includes(process.env.APP_ENV ?? "local")) {
return localExperimentData["Features"];
return localExperimentData;
}

if (!process.env.NIMBUS_SIDECAR_URL) {
Expand Down Expand Up @@ -74,13 +74,10 @@ export async function getExperiments(params: {
experimentData = json;
}

return (
(experimentData as ExperimentData["Features"]) ??
defaultExperimentData["Features"]
);
return (experimentData as ExperimentData) ?? defaultExperimentData;
} catch (ex) {
logger.error("Could not connect to Cirrus", { serverUrl, ex });
captureException(ex);
return defaultExperimentData["Features"];
return defaultExperimentData;
}
}
27 changes: 27 additions & 0 deletions src/app/hooks/useGlean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import EventMetricType from "@mozilla/glean/private/metrics/event";
import type { GleanMetricMap } from "../../telemetry/generated/_map";
import { useSession } from "next-auth/react";
import { hasPremium } from "../functions/universal/user";
import { useExperiments } from "../../contextProviders/experiments";

export const useGlean = () => {
const session = useSession();
const experimentData = useExperiments();
const isPremiumUser = hasPremium(session.data?.user);

const record = useCallback(
Expand All @@ -33,10 +35,35 @@ export const useGlean = () => {
// Record the `plan_tier` key on all events.
// `plan_tier` is set on every metric, but it's too much work for TypeScript
// to infer that — hence the type assertion.
//
// TODO can we fix this? It looks like these are only for button->click which is misleading.
(data as GleanMetricMap["button"]["click"]).plan_tier = isPremiumUser
? "Plus"
: "Free";

// 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.
//
// TODO can we fix this? It looks like these are only for button->click which is misleading.
console.debug({ experimentData });
if (experimentData) {
(data as GleanMetricMap["button"]["click"]).nimbus_user_id =
experimentData["Enrollments"]["nimbus_user_id"];
(data as GleanMetricMap["button"]["click"]).nimbus_app_id =
experimentData["Enrollments"]["app_id"];
(data as GleanMetricMap["button"]["click"]).nimbus_experiment =
experimentData["Enrollments"]["experiment"];
(data as GleanMetricMap["button"]["click"]).nimbus_branch =
experimentData["Enrollments"]["branch"];
(data as GleanMetricMap["button"]["click"]).nimbus_experiment_type =
experimentData["Enrollments"]["experiment_type"];
(data as GleanMetricMap["button"]["click"]).nimbus_is_preview =
experimentData["Enrollments"]["is_preview"].toString();
} else {
console.warn("No experiment data available for Glean");
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
mod[event].record(data as any);
},
Expand Down
28 changes: 28 additions & 0 deletions src/contextProviders/experiments.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* 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/. */

"use client";

import { ReactNode, createContext, useContext } from "react";
import { ExperimentData } from "../telemetry/generated/nimbus/experiments";

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

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

export const ExperimentsProvider = ({
children,
experimentData,
}: ExperimentsProviderProps) => {
return (
<ExperimentsContext.Provider value={experimentData}>
{children}
</ExperimentsContext.Provider>
);
};

export const useExperiments = () => useContext(ExperimentsContext);
Loading

0 comments on commit 6b2f4d4

Please sign in to comment.