Skip to content

Commit

Permalink
feat: Store Lighthouse results
Browse files Browse the repository at this point in the history
  • Loading branch information
flozia committed Jan 17, 2025
2 parents e4ce3cb + 80e7178 commit cae73d7
Show file tree
Hide file tree
Showing 14 changed files with 79 additions and 442 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/lighthouse_cron.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,6 @@ jobs:
- name: Report results
run: npm run cron:report-lighthouse-results
env:
BQ_LIGHTHOUSE_PROJECT: ${{ secrets.BQ_LIGHTHOUSE_PROJECT }}
BQ_LIGHTHOUSE_DATASET: ${{ secrets.BQ_LIGHTHOUSE_DATASET }}
BQ_LIGHTHOUSE_TABLE: ${{ secrets.BQ_LIGHTHOUSE_TABLE }}
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ export default async function DashboardPage({ params, searchParams }: Props) {
totalNumberOfPerformedScans={profileStats?.total}
isNewUser={isNewUser}
elapsedTimeInDaysSinceInitialScan={elapsedTimeInDaysSinceInitialScan}
experimentData={experimentData["Features"]}
experimentData={experimentData}
activeTab={activeTab}
hasFirstMonitoringScan={hasFirstMonitoringScan}
signInCount={signInCount}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export default async function SettingsPage({ params, searchParams }: Props) {
yearlySubscriptionUrl={`${yearlySubscriptionUrl}&${additionalSubplatParams.toString()}`}
subscriptionBillingAmount={getSubscriptionBillingAmount()}
enabledFeatureFlags={enabledFeatureFlags}
experimentData={experimentData["Features"]}
experimentData={experimentData}
lastScanDate={lastOneRepScan?.created_at}
isMonthlySubscriber={isMonthlySubscriber}
activeTab={activeTab}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export default async function Onboarding({ params, searchParams }: Props) {
breachesTotalCount={allBreachesCount}
stepId={firstSlug === FreeScanSlug ? "enterInfo" : "getStarted"}
previousRoute={previousRoute}
experimentData={experimentData["Features"]}
experimentData={experimentData}
/>
);
}
20 changes: 9 additions & 11 deletions src/app/(proper_react)/(redesign)/(public)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,41 +59,39 @@ export default async function Page({ searchParams }: Props) {
oneRepActivations > monthlySubscribersQuota;
return (
<AccountsMetricsFlowProvider
enabled={experimentData["Features"]["landing-page-free-scan-cta"].enabled}
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["Features"]["landing-page-free-scan-cta"].variant,
experimentData["landing-page-free-scan-cta"].variant,
form_type:
experimentData["Features"]["landing-page-free-scan-cta"].variant ===
experimentData["landing-page-free-scan-cta"].variant ===
"ctaWithEmail"
? "email"
: "button",
service: process.env.OAUTH_CLIENT_ID as string,
}}
>
{enabledFeatureFlags.includes("LandingPageRedesign") &&
experimentData["Features"][
"landing-page-redesign-plus-eligible-experiment"
].enabled &&
experimentData["Features"][
"landing-page-redesign-plus-eligible-experiment"
].variant === "redesign" ? (
experimentData["landing-page-redesign-plus-eligible-experiment"]
.enabled &&
experimentData["landing-page-redesign-plus-eligible-experiment"]
.variant === "redesign" ? (
<LandingViewRedesign
eligibleForPremium={eligibleForPremium}
l10n={getL10n()}
countryCode={countryCode}
scanLimitReached={scanLimitReached}
experimentData={experimentData["Features"]}
experimentData={experimentData}
/>
) : (
<LandingView
eligibleForPremium={eligibleForPremium}
l10n={getL10n()}
countryCode={countryCode}
scanLimitReached={scanLimitReached}
experimentData={experimentData["Features"]}
experimentData={experimentData}
/>
)}
</AccountsMetricsFlowProvider>
Expand Down
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["Features"]["welcome-scan-optional-info"];
experimentData["welcome-scan-optional-info"];

const profileData: CreateProfileRequest = {
first_name: firstName,
Expand Down
11 changes: 7 additions & 4 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> {
}): Promise<ExperimentData["Features"]> {
if (["local"].includes(process.env.APP_ENV ?? "local")) {
return localExperimentData;
return localExperimentData["Features"];
}

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

return (experimentData as ExperimentData) ?? defaultExperimentData;
return (
(experimentData as ExperimentData["Features"]) ??
defaultExperimentData["Features"]
);
} catch (ex) {
logger.error("Could not connect to Cirrus", {
serverUrl,
Expand All @@ -96,6 +99,6 @@ export async function getExperiments(params: {
params,
});
captureException(ex);
return defaultExperimentData;
return defaultExperimentData["Features"];
}
}
27 changes: 1 addition & 26 deletions src/app/hooks/useGlean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,9 @@ 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();
// Telemetry recording is mocked in our unit tests, therefore we
// do not have test coverage for this method.
/* c8 ignore start */
const isPremiumUser = hasPremium(session.data?.user);
const record = useCallback(
async <
Expand All @@ -41,30 +36,10 @@ export const useGlean = () => {
? "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.
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);
},
[isPremiumUser, experimentData],
[isPremiumUser],
);
/* c8 ignore end */

Expand Down
30 changes: 2 additions & 28 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ import { GoogleAnalyticsWorkaround } from "./components/client/GoogleAnalyticsWo
import StripeScript from "./components/client/StripeScript";
import { GleanScript } from "./components/client/GleanScript";
import { getExperimentationId } from "./functions/server/getExperimentationId";
import { getExperiments } from "./functions/server/getExperiments";
import { getCountryCode } from "./functions/server/getCountryCode";
import { ExperimentsProvider } from "../contextProviders/experiments";
import * as Sentry from "@sentry/nextjs";

const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });

Expand Down Expand Up @@ -58,26 +54,6 @@ export default async function RootLayout({
const nonce = headers().get("x-nonce") ?? "";
const currentLocale = getLocale(getL10nBundles());
const session = await getServerSession();
const headersList = headers();
const countryCode = getCountryCode(headersList);

// Check for Nimbus preview mode. Note that this requires a full page reload
// to activate: https://nextjs.org/docs/app/api-reference/file-conventions/layout#caveats
const nimbusPreviewMode = headers().get("x-nimbus-preview-mode");
const experimentationId = getExperimentationId(session?.user ?? null);
const experimentData = await getExperiments({
experimentationId: experimentationId,
countryCode: countryCode,
locale: currentLocale,
previewMode: nimbusPreviewMode === "true",
});

const nimbus_user_id = experimentData["Enrollments"].nimbus_user_id;
if (nimbus_user_id !== experimentationId) {
Sentry.captureMessage(
`Nimbus user ID from Cirrus: [${nimbus_user_id}] did not match experimentationId: [${experimentationId}]`,
);
}

return (
<html lang={currentLocale}>
Expand All @@ -88,14 +64,12 @@ export default async function RootLayout({
data-ga4-measurement-id={CONST_GA4_MEASUREMENT_ID}
data-node-env={process.env.NODE_ENV}
>
<ExperimentsProvider experimentData={experimentData}>
<SessionProvider session={session}>{children}</SessionProvider>
</ExperimentsProvider>
<SessionProvider session={session}>{children}</SessionProvider>
</body>
<StripeScript />
<GleanScript
channel={process.env.APP_ENV ?? ""}
experimentationId={experimentationId}
experimentationId={getExperimentationId(session?.user ?? null)}
/>
{headers().get("DNT") !== "1" && (
<GoogleAnalyticsWorkaround
Expand Down
31 changes: 0 additions & 31 deletions src/contextProviders/experiments.tsx

This file was deleted.

28 changes: 28 additions & 0 deletions src/db/migrations/20250113145507_new_broker_status_table.js
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/. */

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
export function up(knex) {
return knex.schema
.table("qa_custom_brokers", table => {
table.string('broker_status').nullable();
table.string('scan_result_status').nullable();
table.string('url').nullable();
});
}

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
export function down(knex) {
return knex.schema.table('qa_custom_brokers',table => {
table.dropColumn('broker_status');
table.dropColumn('scan_result_status');
table.dropColumn('url');
});
}
8 changes: 0 additions & 8 deletions src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,6 @@ export function middleware(request: NextRequest) {
existingExperimentationId?.value ?? `guest-${crypto.randomUUID()}`;
requestHeaders.set("x-experimentation-id", experimentationId);

// Check for Nimbus preview mode. Note that this requires a full page reload
// to activate: https://nextjs.org/docs/app/api-reference/file-conventions/layout#caveats
const nimbusPreviewMode = request.nextUrl.searchParams.get("nimbus_preview");
requestHeaders.set(
"x-nimbus-preview-mode",
nimbusPreviewMode === "true" ? "true" : "false",
);

const response = NextResponse.next({
request: {
headers: requestHeaders,
Expand Down
48 changes: 26 additions & 22 deletions src/scripts/cronjobs/reportLighthouseResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,26 @@ type LighthouseResult = {
};
};

async function getLighthouseResults() {
type LighthouseFullResult = {
url: LighthouseResult["url"];
fetchTime: string;
isRepresentativeRun: boolean;
summary: LighthouseResult["summary"];
audits: {
id: string;
score: number;
numericValue: number;
}[];
};

async function uploadLighthouseReport(results: LighthouseFullResult[]) {
if (
!process.env.BQ_LIGHTHOUSE_PROJECT ||
!process.env.BQ_LIGHTHOUSE_DATASET ||
!process.env.BQ_LIGHTHOUSE_TABLE
) {
return null;
console.error("Missing environment variables");
return;
}

try {
Expand All @@ -47,14 +60,9 @@ async function getLighthouseResults() {
const table = bigQueryClient
.dataset(process.env.BQ_LIGHTHOUSE_DATASET)
.table(process.env.BQ_LIGHTHOUSE_TABLE);
const [metadata] = await table.getMetadata();

console.log(
`Table description: ${metadata.description || "No description"}`,
);
return table;
await table.insert(results);
} catch (error) {
console.error("Error querying Lighthouse results", error);
console.error("Error uploading results", JSON.stringify(error, null, 2));
}
}

Expand All @@ -78,7 +86,7 @@ async function run() {
lighthouseResults
.filter((result) => result.isRepresentativeRun === true)
.map(async (medianResult) => {
const { jsonPath, url, summary } = medianResult;
const { jsonPath, url, isRepresentativeRun, summary } = medianResult;
const fullReport = JSON.parse(
await readFile(new URL(jsonPath, import.meta.url), {
encoding: "utf8",
Expand All @@ -89,21 +97,17 @@ async function run() {
return { id, score, numericValue };
});

return { url, summary, audits };
return {
url,
fetchTime: fullReport.fetchTime,
isRepresentativeRun,
summary,
audits,
};
}),
);

const transformedData = lighthouseReport.map((item) => {
return {
url: item.url,
...item.summary,
};
});
console.table(transformedData);
logger.info("lighthouse_report", lighthouseReport);

const results = await getLighthouseResults();
console.info("BigQuery dataset", results);
await uploadLighthouseReport(lighthouseReport);
}

try {
Expand Down
Loading

0 comments on commit cae73d7

Please sign in to comment.