From 8b953e293ce8f44ab4bb2e3ad0eed0fc66e9d3b6 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sat, 22 Mar 2025 19:21:05 -0700 Subject: [PATCH 01/38] Update TODO list --- TODO.md | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 00000000..971709c0 --- /dev/null +++ b/TODO.md @@ -0,0 +1,107 @@ +Goal: stand up mobile version of experiments/rollouts + +Epics: + +- BIG UNKNOWN: figure out outside-of-nimbus plan +- stand up android + - messaging surface + - onboarding surface + - UNKNOWN: enumerate other surfaces +- stand up iOS + - UNKNOWN: sort out surfaces + - redo android steps, but without all the refactoring + +User Stories. As an Android PM, I should + +- have an easy view of rollouts/branches on the messaging surface + + - so I can see a bunch of what Android users see (on the message surface) + - 1-10 + +- research + + - [surfaces & guidelines](https://mozilla-hub.atlassian.net/wiki/spaces/FIREFOX/pages/210206760/Mobile+Message+Surface+Guidelines) + - [mobile telemetry docs](https://experimenter.info/messaging/mobile-messaging/#events-emitted) + - [desktop explore](https://mozilla.cloud.looker.com/explore/user_journey/event_counts) + + 1. look at iOS telemetry & explores + + - [iOS message probes](https://dictionary.telemetry.mozilla.org/apps/firefox_ios?page=1&search=messag) + - Note no experiments probes like Android has + - [iOS event count explore](https://mozilla.cloud.looker.com/explore/firefox_ios/event_counts?qid=OZqOXzZqTujARgvCK12NJ4) + - [recent iOS clicked events](https://mozilla.cloud.looker.com/explore/firefox_ios/event_counts?qid=jQpgYwZpBZEhW73B1dcyzu&toggle=fil,vis) + - XXX look at onboarding also + + 2. look at Android telemetry & explores + - [Android message probes - Glean dictionary](https://dictionary.telemetry.mozilla.org/apps/fenix?page=1&search=messaging) + - [android explore](https://mozilla.cloud.looker.com/explore/fenix/event_counts)$$ + - [recent android messaing click events with most extra keys and experiments](https://mozilla.cloud.looker.com/explore/fenix/event_counts?qid=u0OKWHjWgTcstNgbzvyyBc&toggle=fil) + - [recent android onboarding events](https://mozilla.cloud.looker.com/explore/fenix/event_counts?qid=n71HDr0LIxuNS3vGX9essN&toggle=fil) + - Need to understand this telemetry compared to JSON + +- +- open questions + - what does telemetry look like for onboarding? other surfaces? similar to messaging? + - Does Click telemetry on both iOS and Android alwyas mean CTA? Or something else? + - What is action_uuid extra key (see docs)? + - **Do non-experimental message send pings on iOS or Android?** + - + +1. Draft plan for Android page + + 1. ?File ticket + 2. Build chart for Android messaging (DONE) + 3. Build 2nd chart (LATER) + 4. Build dashboard (DONE: id = 2191) + 5. Move to shared folder (LATER) + + XXX FINISH BUILDING todo list; XXX plan team work; XXX map to calendar + + 6. Build Android page + + 1. ~~Review existing clone for "completed" (DONE)~~ + 2. ~~?Consider options for cloning, since we'll want Android completed page too, and iOS pages (DONE)~~ + 3. ~~Create new dir with new page.tsx (MUST)~~ + 4. ~~TDD Factor out dashboard (DONE)~~ + 1. Use platform search param (TRIED; TOO FIDDLY, MAYBE LATER) + 2. ~~Put in separate android/ route (DONE)~~ + + 5. Make test & code updates to not display local table (WIP) + 6. Factor "application=" out of env (MUST) + 7. Add cases / refactor multiple feature ID list in experimentUtils.ts (MUST) + 8. Add cases / refactor nimbusRecipe.ts:getBranchInfo (MUST) + + 9. Add cases / refactor messageUtils.getDashboard (LATER) + 10. Update / move messageUtils.getDashboardIdForTemplate (LATER) + 11. Add cases / refactor templates & getSurfaceDataForTemplate (LATER) + 12. Add cases / refactor looker.ts:getCTRPercentData (LATER) + 13. Make pills exclude local if not on desktop (LATER) + + 14. Update columns.tsx:filterBySurface (LATER) + 15. Add l10n (LATER) + 16. Factor Out NimbusMessageTable (NICE) + 17. Factor out high-level data fetching (NICE) + + 18. Pull in Android experiments using that URL + 19. Build dashboard link + 20. How to handle multi types + 21. Build CTR + 22. How to handle multi types + +2. standup 2nd page + + - TDD? clone for mobile + +3. standup 2nd dashboard + + * review mobile telemetry using glean dict + * look at explores available for those tables + * TDD? subclass recipes (desktop & mobile) + + 1. Separate dashboards per surface: onboarding, (messaging genrally - may need to split into message_surface dashboard) + 2. 2.What about QA? + +Later: + +- clean up text on messaging graph +- From 21ecd44b4634a7484579ce13560d24d7100c7af5 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sat, 22 Mar 2025 19:22:42 -0700 Subject: [PATCH 02/38] Add basic instructions for copilot case sensitivity. --- .github/copilot-instructions.md | 14 ++++++++++++++ .github/copilot-test-generation.md | 14 ++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 .github/copilot-instructions.md create mode 100644 .github/copilot-test-generation.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..738ac577 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,14 @@ +# GitHub Copilot Instructions + +## Case-Sensitive Filesystem + +Some of our development happens on a case-sensitive filesystem. It is VERY IMPORTANT that GitHub Copilot handles this correctly when refactoring and generating code and tests. + +### Guidelines + +1. **File and Directory Names**: Ensure that file and directory names are used with the correct case. For example, `MyFile.ts` and `myfile.ts` are different files on a case-sensitive filesystem. +2. **Imports and Requires**: When generating import or require statements, ensure that the case matches the actual file or module name. +3. **Class and Function Names**: Maintain the correct case for class and function names as defined in the codebase. +4. **Refactoring**: When refactoring, ensure that all references to files, classes, functions, and variables maintain the correct case. + +By following these guidelines, we can avoid issues related to case sensitivity in our development process. diff --git a/.github/copilot-test-generation.md b/.github/copilot-test-generation.md new file mode 100644 index 00000000..738ac577 --- /dev/null +++ b/.github/copilot-test-generation.md @@ -0,0 +1,14 @@ +# GitHub Copilot Instructions + +## Case-Sensitive Filesystem + +Some of our development happens on a case-sensitive filesystem. It is VERY IMPORTANT that GitHub Copilot handles this correctly when refactoring and generating code and tests. + +### Guidelines + +1. **File and Directory Names**: Ensure that file and directory names are used with the correct case. For example, `MyFile.ts` and `myfile.ts` are different files on a case-sensitive filesystem. +2. **Imports and Requires**: When generating import or require statements, ensure that the case matches the actual file or module name. +3. **Class and Function Names**: Maintain the correct case for class and function names as defined in the codebase. +4. **Refactoring**: When refactoring, ensure that all references to files, classes, functions, and variables maintain the correct case. + +By following these guidelines, we can avoid issues related to case sensitivity in our development process. From 6a0112ed3c3212d52c2fe9c568ed05a2831ef33e Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sat, 22 Mar 2025 19:41:28 -0700 Subject: [PATCH 03/38] Refactor page.tsx to call fetchData and pass params --- app/android/page.tsx | 7 ++++--- app/dashboard.tsx | 26 ++++++++++++++------------ app/page.tsx | 23 ++++++++++++++++++++--- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/app/android/page.tsx b/app/android/page.tsx index 69ece864..904fe12d 100644 --- a/app/android/page.tsx +++ b/app/android/page.tsx @@ -1,5 +1,6 @@ -import { Dashboard } from "@/app/dashboard"; +import { fetchData, Dashboard } from "@/app/dashboard"; -export default function Page() { - return ; +export default async function Page() { + const data = await fetchData(); + return ; } diff --git a/app/dashboard.tsx b/app/dashboard.tsx index 9e79d155..c53794f7 100644 --- a/app/dashboard.tsx +++ b/app/dashboard.tsx @@ -192,7 +192,7 @@ async function getMsgRolloutCollection( return msgRolloutRecipeCollection; } -async function fetchData() { +export async function fetchData() { const recipeCollection = new NimbusRecipeCollection(); await recipeCollection.fetchRecipes(); console.log("recipeCollection.length = ", recipeCollection.recipes.length); @@ -268,19 +268,21 @@ const ReleasedTable = async ({ platform, localData }: ReleasedTableProps) => { interface DashboardProps { platform?: string; + localData: FxMSMessageInfo[]; + experimentAndBranchInfo: any[]; + totalExperiments: number; + msgRolloutInfo: any[]; + totalRolloutExperiments: number; } -export const Dashboard = async ( - { platform }: DashboardProps = { platform: "desktop" }, -) => { - const { - localData, - experimentAndBranchInfo, - totalExperiments, - msgRolloutInfo, - totalRolloutExperiments, - } = await fetchData(); - +export const Dashboard = async ({ + platform = "desktop", + localData, + experimentAndBranchInfo, + totalExperiments, + msgRolloutInfo, + totalRolloutExperiments, +}: DashboardProps) => { return (
diff --git a/app/page.tsx b/app/page.tsx index 1bd77d9b..b7fe886a 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,5 +1,22 @@ -import { Dashboard } from "@/app/dashboard"; +import { fetchData, Dashboard } from "@/app/dashboard"; -export default function Page() { - return ; +export default async function Page() { + const { + localData, + experimentAndBranchInfo, + totalExperiments, + msgRolloutInfo, + totalRolloutExperiments, + } = await fetchData(); + + return ( + + ); } From b3b9a9487dd9f0dbe6f62212895d865e4ca5169c Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sat, 22 Mar 2025 19:48:37 -0700 Subject: [PATCH 04/38] Fixup Android calling of Dashboard component. --- app/android/page.tsx | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/app/android/page.tsx b/app/android/page.tsx index 904fe12d..21960689 100644 --- a/app/android/page.tsx +++ b/app/android/page.tsx @@ -1,6 +1,22 @@ import { fetchData, Dashboard } from "@/app/dashboard"; export default async function Page() { - const data = await fetchData(); - return ; + const { + localData, + experimentAndBranchInfo, + totalExperiments, + msgRolloutInfo, + totalRolloutExperiments, + } = await fetchData(); + + return ( + + ); } From 9e18edede38ae789510250cdf4eaccb5a1f631a7 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sat, 22 Mar 2025 19:59:48 -0700 Subject: [PATCH 05/38] Made localData optional to Dashboard component --- app/android/page.tsx | 1 - app/dashboard.tsx | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/android/page.tsx b/app/android/page.tsx index 21960689..2c0bcde7 100644 --- a/app/android/page.tsx +++ b/app/android/page.tsx @@ -12,7 +12,6 @@ export default async function Page() { return ( { interface DashboardProps { platform?: string; - localData: FxMSMessageInfo[]; + localData?: FxMSMessageInfo[]; experimentAndBranchInfo: any[]; totalExperiments: number; msgRolloutInfo: any[]; @@ -290,7 +290,8 @@ export const Dashboard = async ({
- + {localData + ? : null}
Current {platform} Message Rollouts From e31c39e04f742134aa7735fd68b22c35eb0fd643 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sat, 22 Mar 2025 20:08:23 -0700 Subject: [PATCH 06/38] Make the file case instructions clearer --- .github/copilot-instructions.md | 13 ++++++++++++- .github/copilot-test-generation.md | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 738ac577..b540e3d7 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -11,4 +11,15 @@ Some of our development happens on a case-sensitive filesystem. It is VERY IMPOR 3. **Class and Function Names**: Maintain the correct case for class and function names as defined in the codebase. 4. **Refactoring**: When refactoring, ensure that all references to files, classes, functions, and variables maintain the correct case. -By following these guidelines, we can avoid issues related to case sensitivity in our development process. +### Specific Instructions for Component Files + +When working with component files where the component name is uppercase and the file name contains lowercase, ensure the following: + +1. **Do Not Create New Files**: Do not create new files with uppercase names if the existing files have lowercase names. +2. **Correct File Names**: Use the existing files with the correct case. +3. **Correct Imports**: When importing components in other files, ensure the import statement uses the correct case: + ```tsx + import Component from "@/app/component"; + ``` + +By following these guidelines, we can avoid issues related to case sensitivity and unnecessary file creation in our development process. diff --git a/.github/copilot-test-generation.md b/.github/copilot-test-generation.md index 738ac577..b540e3d7 100644 --- a/.github/copilot-test-generation.md +++ b/.github/copilot-test-generation.md @@ -11,4 +11,15 @@ Some of our development happens on a case-sensitive filesystem. It is VERY IMPOR 3. **Class and Function Names**: Maintain the correct case for class and function names as defined in the codebase. 4. **Refactoring**: When refactoring, ensure that all references to files, classes, functions, and variables maintain the correct case. -By following these guidelines, we can avoid issues related to case sensitivity in our development process. +### Specific Instructions for Component Files + +When working with component files where the component name is uppercase and the file name contains lowercase, ensure the following: + +1. **Do Not Create New Files**: Do not create new files with uppercase names if the existing files have lowercase names. +2. **Correct File Names**: Use the existing files with the correct case. +3. **Correct Imports**: When importing components in other files, ensure the import statement uses the correct case: + ```tsx + import Component from "@/app/component"; + ``` + +By following these guidelines, we can avoid issues related to case sensitivity and unnecessary file creation in our development process. From 6f199bb49fb3eb6aa973a2cc1ea8e0adea45257a Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sat, 22 Mar 2025 20:28:55 -0700 Subject: [PATCH 07/38] Fixup dashboard test --- __tests__/app/dashboard.test.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/__tests__/app/dashboard.test.tsx b/__tests__/app/dashboard.test.tsx index 1d5f2e49..4ce5d530 100644 --- a/__tests__/app/dashboard.test.tsx +++ b/__tests__/app/dashboard.test.tsx @@ -11,9 +11,19 @@ global.fetch = jest.fn(() => }), ) as jest.Mock; +const mockFetchData = { + localData: [], + experimentAndBranchInfo: [], + totalExperiments: 0, + msgRolloutInfo: [], + totalRolloutExperiments: 0, +}; + describe.skip("Dashboard", () => { it("all timeline pill ids exist in the Dashboard component in /", async () => { - const dashboard = await render(await ()); + const dashboard = await render( + await + ); const firefox = dashboard.getByTestId("firefox"); const experiments = dashboard.getByTestId("live_experiments"); From 487c0c79be8baa32e742f80336182468a5cc4017 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sat, 22 Mar 2025 20:31:33 -0700 Subject: [PATCH 08/38] Move platform to a union type --- app/android/page.tsx | 5 ++++- app/dashboard.tsx | 5 +++-- app/page.tsx | 2 +- app/types.ts | 0 lib/types.ts | 3 +++ 5 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 app/types.ts create mode 100644 lib/types.ts diff --git a/app/android/page.tsx b/app/android/page.tsx index 2c0bcde7..605aeb9c 100644 --- a/app/android/page.tsx +++ b/app/android/page.tsx @@ -1,4 +1,7 @@ import { fetchData, Dashboard } from "@/app/dashboard"; +import { Platform } from "@/lib/types"; + + export default async function Page() { const { @@ -11,7 +14,7 @@ export default async function Page() { return ( { }; interface DashboardProps { - platform?: string; + platform?: Platform; localData?: FxMSMessageInfo[]; experimentAndBranchInfo: any[]; totalExperiments: number; @@ -276,7 +277,7 @@ interface DashboardProps { } export const Dashboard = async ({ - platform = "desktop", + platform = "Desktop", localData, experimentAndBranchInfo, totalExperiments, diff --git a/app/page.tsx b/app/page.tsx index b7fe886a..0f4de03c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -11,7 +11,7 @@ export default async function Page() { return ( Date: Sun, 23 Mar 2025 10:31:08 -0700 Subject: [PATCH 09/38] Refactor fetchData to its own file --- app/android/page.tsx | 6 ++---- app/dashboard.tsx | 48 ++++---------------------------------------- app/page.tsx | 3 ++- 3 files changed, 8 insertions(+), 49 deletions(-) diff --git a/app/android/page.tsx b/app/android/page.tsx index 605aeb9c..0910294c 100644 --- a/app/android/page.tsx +++ b/app/android/page.tsx @@ -1,7 +1,5 @@ -import { fetchData, Dashboard } from "@/app/dashboard"; -import { Platform } from "@/lib/types"; - - +import { Dashboard } from "@/app/dashboard"; +import { fetchData } from "@/app/fetchData"; export default async function Page() { const { diff --git a/app/dashboard.tsx b/app/dashboard.tsx index d3d5096f..6a8b3e38 100644 --- a/app/dashboard.tsx +++ b/app/dashboard.tsx @@ -17,7 +17,6 @@ import { maybeCreateWelcomePreview, getPreviewLink, messageHasMicrosurvey, - compareSurfacesFn, } from "../lib/messageUtils.ts"; import { NimbusRecipeCollection } from "../lib/nimbusRecipeCollection"; @@ -31,7 +30,7 @@ import { InfoPopover } from "@/components/ui/infopopover.tsx"; import { Timeline } from "@/components/ui/timeline.tsx"; import { Platform } from "@/lib/types"; -const isLookerEnabled = process.env.IS_LOOKER_ENABLED === "true"; +export const isLookerEnabled = process.env.IS_LOOKER_ENABLED === "true"; const hidden_message_impression_threshold = process.env.HIDDEN_MESSAGE_IMPRESSION_THRESHOLD; @@ -133,7 +132,7 @@ async function appendFxMSTelemetryData(existingMessageData: any) { * lib/asrouter-local-prod-messages/data.json and also FxMS telemetry data if * Looker credentials are enabled. */ -async function getASRouterLocalMessageInfoFromFile(): Promise< +export async function getASRouterLocalMessageInfoFromFile(): Promise< FxMSMessageInfo[] > { const fs = require("fs"); @@ -157,7 +156,7 @@ async function getASRouterLocalMessageInfoFromFile(): Promise< return messages; } -async function getMsgExpRecipeCollection( +export async function getMsgExpRecipeCollection( recipeCollection: NimbusRecipeCollection, ): Promise { const expOnlyCollection = new NimbusRecipeCollection(); @@ -178,7 +177,7 @@ async function getMsgExpRecipeCollection( return msgExpRecipeCollection; } -async function getMsgRolloutCollection( +export async function getMsgRolloutCollection( recipeCollection: NimbusRecipeCollection, ): Promise { const msgRolloutRecipeCollection = new NimbusRecipeCollection(); @@ -193,45 +192,6 @@ async function getMsgRolloutCollection( return msgRolloutRecipeCollection; } -export async function fetchData() { - const recipeCollection = new NimbusRecipeCollection(); - await recipeCollection.fetchRecipes(); - console.log("recipeCollection.length = ", recipeCollection.recipes.length); - - const localData = (await getASRouterLocalMessageInfoFromFile()).sort( - compareSurfacesFn, - ); - - const msgExpRecipeCollection = - await getMsgExpRecipeCollection(recipeCollection); - const msgRolloutRecipeCollection = - await getMsgRolloutCollection(recipeCollection); - - const experimentAndBranchInfo = isLookerEnabled - ? await msgExpRecipeCollection.getExperimentAndBranchInfos() - : msgExpRecipeCollection.recipes.map((recipe: NimbusRecipe) => - recipe.getRecipeInfo(), - ); - - const totalExperiments = msgExpRecipeCollection.recipes.length; - - const msgRolloutInfo = isLookerEnabled - ? await msgRolloutRecipeCollection.getExperimentAndBranchInfos() - : msgRolloutRecipeCollection.recipes.map((recipe: NimbusRecipe) => - recipe.getRecipeInfo(), - ); - - const totalRolloutExperiments = msgRolloutRecipeCollection.recipes.length; - - return { - localData, - experimentAndBranchInfo, - totalExperiments, - msgRolloutInfo, - totalRolloutExperiments, - }; -} - interface ReleasedTableProps { platform: string; localData: FxMSMessageInfo[]; diff --git a/app/page.tsx b/app/page.tsx index 0f4de03c..4f0e1d84 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,4 +1,5 @@ -import { fetchData, Dashboard } from "@/app/dashboard"; +import { Dashboard } from "@/app/dashboard"; +import { fetchData } from "@/app/fetchData"; export default async function Page() { const { From 0340475b23cad70bbdcc81dc68d450ad3af5f336 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sun, 23 Mar 2025 10:43:57 -0700 Subject: [PATCH 10/38] Renamed fetchData.tsx to fetchData.ts --- app/fetchData.tsx | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 app/fetchData.tsx diff --git a/app/fetchData.tsx b/app/fetchData.tsx new file mode 100644 index 00000000..6bc6eef8 --- /dev/null +++ b/app/fetchData.tsx @@ -0,0 +1,40 @@ +import { compareSurfacesFn } from "@/lib/messageUtils"; +import { NimbusRecipe } from "@/lib/nimbusRecipe"; +import { NimbusRecipeCollection } from "@/lib/nimbusRecipeCollection"; +import { getASRouterLocalMessageInfoFromFile, getMsgExpRecipeCollection, getMsgRolloutCollection, isLookerEnabled } from "./dashboard"; + + +export async function fetchData() { + const recipeCollection = new NimbusRecipeCollection(); + await recipeCollection.fetchRecipes(); + console.log("recipeCollection.length = ", recipeCollection.recipes.length); + + const localData = (await getASRouterLocalMessageInfoFromFile()).sort( + compareSurfacesFn + ); + + const msgExpRecipeCollection = await getMsgExpRecipeCollection(recipeCollection); + const msgRolloutRecipeCollection = await getMsgRolloutCollection(recipeCollection); + + const experimentAndBranchInfo = isLookerEnabled + ? await msgExpRecipeCollection.getExperimentAndBranchInfos() + : msgExpRecipeCollection.recipes.map((recipe: NimbusRecipe) => recipe.getRecipeInfo() + ); + + const totalExperiments = msgExpRecipeCollection.recipes.length; + + const msgRolloutInfo = isLookerEnabled + ? await msgRolloutRecipeCollection.getExperimentAndBranchInfos() + : msgRolloutRecipeCollection.recipes.map((recipe: NimbusRecipe) => recipe.getRecipeInfo() + ); + + const totalRolloutExperiments = msgRolloutRecipeCollection.recipes.length; + + return { + localData, + experimentAndBranchInfo, + totalExperiments, + msgRolloutInfo, + totalRolloutExperiments, + }; +} From ebe78b039415c52de680245361e8e6e508e953e6 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sun, 23 Mar 2025 10:49:05 -0700 Subject: [PATCH 11/38] Export compareDatesFn to fix the app --- app/dashboard.tsx | 2 +- app/fetchData.ts | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 app/fetchData.ts diff --git a/app/dashboard.tsx b/app/dashboard.tsx index 6a8b3e38..10bca5d9 100644 --- a/app/dashboard.tsx +++ b/app/dashboard.tsx @@ -46,7 +46,7 @@ const hidden_message_impression_threshold = * @returns -1 if the start date for message a is after the start date for * message b, zero if they're equal, and 1 otherwise. */ -function compareDatesFn(a: NimbusRecipe, b: NimbusRecipe): number { +export function compareDatesFn(a: NimbusRecipe, b: NimbusRecipe): number { if (a._rawRecipe.startDate && b._rawRecipe.startDate) { if (a._rawRecipe.startDate > b._rawRecipe.startDate) { return -1; diff --git a/app/fetchData.ts b/app/fetchData.ts new file mode 100644 index 00000000..65f5c681 --- /dev/null +++ b/app/fetchData.ts @@ -0,0 +1,58 @@ +import { compareSurfacesFn } from "@/lib/messageUtils"; +import { NimbusRecipe } from "@/lib/nimbusRecipe"; +import { NimbusRecipeCollection } from "@/lib/nimbusRecipeCollection"; +import { compareDatesFn, getASRouterLocalMessageInfoFromFile, getMsgRolloutCollection, isLookerEnabled } from "./dashboard"; + +export async function fetchData() { + const recipeCollection = new NimbusRecipeCollection(); + await recipeCollection.fetchRecipes(); + console.log("recipeCollection.length = ", recipeCollection.recipes.length); + + const localData = (await getASRouterLocalMessageInfoFromFile()).sort( + compareSurfacesFn + ); + + const msgExpRecipeCollection = await getMsgExpRecipeCollection(recipeCollection); + const msgRolloutRecipeCollection = await getMsgRolloutCollection(recipeCollection); + + const experimentAndBranchInfo = isLookerEnabled + ? await msgExpRecipeCollection.getExperimentAndBranchInfos() + : msgExpRecipeCollection.recipes.map((recipe: NimbusRecipe) => recipe.getRecipeInfo() + ); + + const totalExperiments = msgExpRecipeCollection.recipes.length; + + const msgRolloutInfo = isLookerEnabled + ? await msgRolloutRecipeCollection.getExperimentAndBranchInfos() + : msgRolloutRecipeCollection.recipes.map((recipe: NimbusRecipe) => recipe.getRecipeInfo() + ); + + const totalRolloutExperiments = msgRolloutRecipeCollection.recipes.length; + + return { + localData, + experimentAndBranchInfo, + totalExperiments, + msgRolloutInfo, + totalRolloutExperiments, + }; +} +export async function getMsgExpRecipeCollection( + recipeCollection: NimbusRecipeCollection +): Promise { + const expOnlyCollection = new NimbusRecipeCollection(); + expOnlyCollection.recipes = recipeCollection.recipes.filter((recipe) => recipe.isExpRecipe() + ); + console.log("expOnlyCollection.length = ", expOnlyCollection.recipes.length); + + const msgExpRecipeCollection = new NimbusRecipeCollection(); + msgExpRecipeCollection.recipes = expOnlyCollection.recipes + .filter((recipe) => recipe.usesMessagingFeatures()) + .sort(compareDatesFn); + console.log( + "msgExpRecipeCollection.length = ", + msgExpRecipeCollection.recipes.length + ); + + return msgExpRecipeCollection; +} From dccc2a03fb3f1a646f23f4745d69cf8fd902a86c Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sun, 23 Mar 2025 11:01:09 -0700 Subject: [PATCH 12/38] Remove obsolete fetchData.tsx --- app/fetchData.tsx | 40 ---------------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 app/fetchData.tsx diff --git a/app/fetchData.tsx b/app/fetchData.tsx deleted file mode 100644 index 6bc6eef8..00000000 --- a/app/fetchData.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { compareSurfacesFn } from "@/lib/messageUtils"; -import { NimbusRecipe } from "@/lib/nimbusRecipe"; -import { NimbusRecipeCollection } from "@/lib/nimbusRecipeCollection"; -import { getASRouterLocalMessageInfoFromFile, getMsgExpRecipeCollection, getMsgRolloutCollection, isLookerEnabled } from "./dashboard"; - - -export async function fetchData() { - const recipeCollection = new NimbusRecipeCollection(); - await recipeCollection.fetchRecipes(); - console.log("recipeCollection.length = ", recipeCollection.recipes.length); - - const localData = (await getASRouterLocalMessageInfoFromFile()).sort( - compareSurfacesFn - ); - - const msgExpRecipeCollection = await getMsgExpRecipeCollection(recipeCollection); - const msgRolloutRecipeCollection = await getMsgRolloutCollection(recipeCollection); - - const experimentAndBranchInfo = isLookerEnabled - ? await msgExpRecipeCollection.getExperimentAndBranchInfos() - : msgExpRecipeCollection.recipes.map((recipe: NimbusRecipe) => recipe.getRecipeInfo() - ); - - const totalExperiments = msgExpRecipeCollection.recipes.length; - - const msgRolloutInfo = isLookerEnabled - ? await msgRolloutRecipeCollection.getExperimentAndBranchInfos() - : msgRolloutRecipeCollection.recipes.map((recipe: NimbusRecipe) => recipe.getRecipeInfo() - ); - - const totalRolloutExperiments = msgRolloutRecipeCollection.recipes.length; - - return { - localData, - experimentAndBranchInfo, - totalExperiments, - msgRolloutInfo, - totalRolloutExperiments, - }; -} From a85e0125e447cf907bab2a3ca9ea339ae0cab10e Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sun, 23 Mar 2025 11:02:49 -0700 Subject: [PATCH 13/38] Move getASRouterLocalMessageIntoFromFile to fetchData.ts --- app/dashboard.tsx | 33 ++----------------------- app/fetchData.ts | 63 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 54 insertions(+), 42 deletions(-) diff --git a/app/dashboard.tsx b/app/dashboard.tsx index 10bca5d9..68c45639 100644 --- a/app/dashboard.tsx +++ b/app/dashboard.tsx @@ -59,7 +59,7 @@ export function compareDatesFn(a: NimbusRecipe, b: NimbusRecipe): number { return 0; } -async function getASRouterLocalColumnFromJSON( +export async function getASRouterLocalColumnFromJSON( messageDef: any, ): Promise { let fxmsMsgInfo: FxMSMessageInfo = { @@ -114,7 +114,7 @@ let columnsShown = false; * message data is also cleaned up to match the message data objects from * ASRouter, remove any test messages, and update templates. */ -async function appendFxMSTelemetryData(existingMessageData: any) { +export async function appendFxMSTelemetryData(existingMessageData: any) { // Get Looker message data (taken from the query in Look // https://mozilla.cloud.looker.com/looks/2162) const lookId = "2162"; @@ -127,35 +127,6 @@ async function appendFxMSTelemetryData(existingMessageData: any) { return mergedData; } -/** - * @returns message data in the form of FxMSMessageInfo from - * lib/asrouter-local-prod-messages/data.json and also FxMS telemetry data if - * Looker credentials are enabled. - */ -export async function getASRouterLocalMessageInfoFromFile(): Promise< - FxMSMessageInfo[] -> { - const fs = require("fs"); - - let data = fs.readFileSync( - "lib/asrouter-local-prod-messages/data.json", - "utf8", - ); - let json_data = JSON.parse(data); - - if (isLookerEnabled) { - json_data = await appendFxMSTelemetryData(json_data); - } - - let messages = await Promise.all( - json_data.map(async (messageDef: any): Promise => { - return await getASRouterLocalColumnFromJSON(messageDef); - }), - ); - - return messages; -} - export async function getMsgExpRecipeCollection( recipeCollection: NimbusRecipeCollection, ): Promise { diff --git a/app/fetchData.ts b/app/fetchData.ts index 65f5c681..54e9e6c0 100644 --- a/app/fetchData.ts +++ b/app/fetchData.ts @@ -1,7 +1,14 @@ import { compareSurfacesFn } from "@/lib/messageUtils"; import { NimbusRecipe } from "@/lib/nimbusRecipe"; import { NimbusRecipeCollection } from "@/lib/nimbusRecipeCollection"; -import { compareDatesFn, getASRouterLocalMessageInfoFromFile, getMsgRolloutCollection, isLookerEnabled } from "./dashboard"; +import { + appendFxMSTelemetryData, + compareDatesFn, + getASRouterLocalColumnFromJSON, + getMsgRolloutCollection, + isLookerEnabled, +} from "@/app/dashboard"; +import { FxMSMessageInfo } from "./columns"; export async function fetchData() { const recipeCollection = new NimbusRecipeCollection(); @@ -9,23 +16,27 @@ export async function fetchData() { console.log("recipeCollection.length = ", recipeCollection.recipes.length); const localData = (await getASRouterLocalMessageInfoFromFile()).sort( - compareSurfacesFn + compareSurfacesFn, ); - const msgExpRecipeCollection = await getMsgExpRecipeCollection(recipeCollection); - const msgRolloutRecipeCollection = await getMsgRolloutCollection(recipeCollection); + const msgExpRecipeCollection = + await getMsgExpRecipeCollection(recipeCollection); + const msgRolloutRecipeCollection = + await getMsgRolloutCollection(recipeCollection); const experimentAndBranchInfo = isLookerEnabled ? await msgExpRecipeCollection.getExperimentAndBranchInfos() - : msgExpRecipeCollection.recipes.map((recipe: NimbusRecipe) => recipe.getRecipeInfo() - ); + : msgExpRecipeCollection.recipes.map((recipe: NimbusRecipe) => + recipe.getRecipeInfo(), + ); const totalExperiments = msgExpRecipeCollection.recipes.length; const msgRolloutInfo = isLookerEnabled ? await msgRolloutRecipeCollection.getExperimentAndBranchInfos() - : msgRolloutRecipeCollection.recipes.map((recipe: NimbusRecipe) => recipe.getRecipeInfo() - ); + : msgRolloutRecipeCollection.recipes.map((recipe: NimbusRecipe) => + recipe.getRecipeInfo(), + ); const totalRolloutExperiments = msgRolloutRecipeCollection.recipes.length; @@ -38,10 +49,11 @@ export async function fetchData() { }; } export async function getMsgExpRecipeCollection( - recipeCollection: NimbusRecipeCollection + recipeCollection: NimbusRecipeCollection, ): Promise { const expOnlyCollection = new NimbusRecipeCollection(); - expOnlyCollection.recipes = recipeCollection.recipes.filter((recipe) => recipe.isExpRecipe() + expOnlyCollection.recipes = recipeCollection.recipes.filter((recipe) => + recipe.isExpRecipe(), ); console.log("expOnlyCollection.length = ", expOnlyCollection.recipes.length); @@ -51,8 +63,37 @@ export async function getMsgExpRecipeCollection( .sort(compareDatesFn); console.log( "msgExpRecipeCollection.length = ", - msgExpRecipeCollection.recipes.length + msgExpRecipeCollection.recipes.length, ); return msgExpRecipeCollection; } +/** + * @returns message data in the form of FxMSMessageInfo from + * lib/asrouter-local-prod-messages/data.json and also FxMS telemetry data if + * Looker credentials are enabled. + */ + +export async function getASRouterLocalMessageInfoFromFile(): Promise< + FxMSMessageInfo[] +> { + const fs = require("fs"); + + let data = fs.readFileSync( + "lib/asrouter-local-prod-messages/data.json", + "utf8", + ); + let json_data = JSON.parse(data); + + if (isLookerEnabled) { + json_data = await appendFxMSTelemetryData(json_data); + } + + let messages = await Promise.all( + json_data.map(async (messageDef: any): Promise => { + return await getASRouterLocalColumnFromJSON(messageDef); + }), + ); + + return messages; +} From deef4351cd11d24580c169ab0655d76b7ec21d7e Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sun, 23 Mar 2025 11:05:57 -0700 Subject: [PATCH 14/38] Move getASRouterLocalColumnFromJSON to fetchData.ts --- app/dashboard.tsx | 53 --------------------------------------------- app/fetchData.ts | 55 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 55 deletions(-) diff --git a/app/dashboard.tsx b/app/dashboard.tsx index 68c45639..5a9dd329 100644 --- a/app/dashboard.tsx +++ b/app/dashboard.tsx @@ -5,18 +5,11 @@ import { } from "./columns"; import { cleanLookerData, - getCTRPercentData, mergeLookerData, runLookQuery, } from "@/lib/looker.ts"; import { - getDashboard, - getSurfaceDataForTemplate, - getTemplateFromMessage, _isAboutWelcomeTemplate, - maybeCreateWelcomePreview, - getPreviewLink, - messageHasMicrosurvey, } from "../lib/messageUtils.ts"; import { NimbusRecipeCollection } from "../lib/nimbusRecipeCollection"; @@ -59,52 +52,6 @@ export function compareDatesFn(a: NimbusRecipe, b: NimbusRecipe): number { return 0; } -export async function getASRouterLocalColumnFromJSON( - messageDef: any, -): Promise { - let fxmsMsgInfo: FxMSMessageInfo = { - product: "Desktop", - id: messageDef.id, - template: messageDef.template, - surface: getSurfaceDataForTemplate(getTemplateFromMessage(messageDef)) - .surface, - segment: "some segment", - metrics: "some metrics", - ctrPercent: undefined, // may be populated from Looker data - ctrPercentChange: undefined, // may be populated from Looker data - previewLink: getPreviewLink(maybeCreateWelcomePreview(messageDef)), - impressions: undefined, // may be populated from Looker data - hasMicrosurvey: messageHasMicrosurvey(messageDef.id), - hidePreview: messageDef.hidePreview, - }; - - const channel = "release"; - - if (isLookerEnabled) { - const ctrPercentData = await getCTRPercentData( - fxmsMsgInfo.id, - fxmsMsgInfo.template, - channel, - ); - if (ctrPercentData) { - fxmsMsgInfo.ctrPercent = ctrPercentData.ctrPercent; - fxmsMsgInfo.impressions = ctrPercentData.impressions; - } - } - - fxmsMsgInfo.ctrDashboardLink = getDashboard( - fxmsMsgInfo.template, - fxmsMsgInfo.id, - channel, - ); - - // dashboard link -> dashboard id -> query id -> query -> ctr_percent_from_lastish_day - - // console.log("fxmsMsgInfo: ", fxmsMsgInfo) - - return fxmsMsgInfo; -} - let columnsShown = false; /** diff --git a/app/fetchData.ts b/app/fetchData.ts index 54e9e6c0..fb2f1d55 100644 --- a/app/fetchData.ts +++ b/app/fetchData.ts @@ -1,14 +1,22 @@ -import { compareSurfacesFn } from "@/lib/messageUtils"; +import { + compareSurfacesFn, + getDashboard, + getPreviewLink, + getSurfaceDataForTemplate, + getTemplateFromMessage, + maybeCreateWelcomePreview, + messageHasMicrosurvey, +} from "@/lib/messageUtils"; import { NimbusRecipe } from "@/lib/nimbusRecipe"; import { NimbusRecipeCollection } from "@/lib/nimbusRecipeCollection"; import { appendFxMSTelemetryData, compareDatesFn, - getASRouterLocalColumnFromJSON, getMsgRolloutCollection, isLookerEnabled, } from "@/app/dashboard"; import { FxMSMessageInfo } from "./columns"; +import { getCTRPercentData } from "@/lib/looker"; export async function fetchData() { const recipeCollection = new NimbusRecipeCollection(); @@ -97,3 +105,46 @@ export async function getASRouterLocalMessageInfoFromFile(): Promise< return messages; } +export async function getASRouterLocalColumnFromJSON( + messageDef: any, +): Promise { + let fxmsMsgInfo: FxMSMessageInfo = { + product: "Desktop", + id: messageDef.id, + template: messageDef.template, + surface: getSurfaceDataForTemplate(getTemplateFromMessage(messageDef)) + .surface, + segment: "some segment", + metrics: "some metrics", + ctrPercent: undefined, // may be populated from Looker data + ctrPercentChange: undefined, // may be populated from Looker data + previewLink: getPreviewLink(maybeCreateWelcomePreview(messageDef)), + impressions: undefined, // may be populated from Looker data + hasMicrosurvey: messageHasMicrosurvey(messageDef.id), + hidePreview: messageDef.hidePreview, + }; + + const channel = "release"; + + if (isLookerEnabled) { + const ctrPercentData = await getCTRPercentData( + fxmsMsgInfo.id, + fxmsMsgInfo.template, + channel, + ); + if (ctrPercentData) { + fxmsMsgInfo.ctrPercent = ctrPercentData.ctrPercent; + fxmsMsgInfo.impressions = ctrPercentData.impressions; + } + } + + fxmsMsgInfo.ctrDashboardLink = getDashboard( + fxmsMsgInfo.template, + fxmsMsgInfo.id, + channel, + ); + + // dashboard link -> dashboard id -> query id -> query -> ctr_percent_from_lastish_day + // console.log("fxmsMsgInfo: ", fxmsMsgInfo) + return fxmsMsgInfo; +} From 4ed15b07eabb5c256870f2fd504d50025995521e Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sun, 23 Mar 2025 11:09:27 -0700 Subject: [PATCH 15/38] Remove extra copy of getMsgExpRecipeCollection from dashboard.tsx --- app/dashboard.tsx | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/app/dashboard.tsx b/app/dashboard.tsx index 5a9dd329..380f1a03 100644 --- a/app/dashboard.tsx +++ b/app/dashboard.tsx @@ -74,27 +74,6 @@ export async function appendFxMSTelemetryData(existingMessageData: any) { return mergedData; } -export async function getMsgExpRecipeCollection( - recipeCollection: NimbusRecipeCollection, -): Promise { - const expOnlyCollection = new NimbusRecipeCollection(); - expOnlyCollection.recipes = recipeCollection.recipes.filter((recipe) => - recipe.isExpRecipe(), - ); - console.log("expOnlyCollection.length = ", expOnlyCollection.recipes.length); - - const msgExpRecipeCollection = new NimbusRecipeCollection(); - msgExpRecipeCollection.recipes = expOnlyCollection.recipes - .filter((recipe) => recipe.usesMessagingFeatures()) - .sort(compareDatesFn); - console.log( - "msgExpRecipeCollection.length = ", - msgExpRecipeCollection.recipes.length, - ); - - return msgExpRecipeCollection; -} - export async function getMsgRolloutCollection( recipeCollection: NimbusRecipeCollection, ): Promise { From 05e7baa76988f18a49415c1ed871005ce466f65b Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sun, 23 Mar 2025 11:10:56 -0700 Subject: [PATCH 16/38] Remove dead code --- app/dashboard.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/dashboard.tsx b/app/dashboard.tsx index 380f1a03..d3468ed6 100644 --- a/app/dashboard.tsx +++ b/app/dashboard.tsx @@ -52,8 +52,6 @@ export function compareDatesFn(a: NimbusRecipe, b: NimbusRecipe): number { return 0; } -let columnsShown = false; - /** * Appends any FxMS telemetry message data from the query in Look * https://mozilla.cloud.looker.com/looks/2162 that does not already exist (ie. From f95527b0f81e9e92c200a29049de020cf6139d26 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sun, 23 Mar 2025 11:17:41 -0700 Subject: [PATCH 17/38] Move appendFxMSTelemetryData to fetchData.ts --- app/dashboard.tsx | 25 ------------------------- app/fetchData.ts | 29 ++++++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/app/dashboard.tsx b/app/dashboard.tsx index d3468ed6..3017eebe 100644 --- a/app/dashboard.tsx +++ b/app/dashboard.tsx @@ -3,11 +3,6 @@ import { FxMSMessageInfo, fxmsMessageColumns, } from "./columns"; -import { - cleanLookerData, - mergeLookerData, - runLookQuery, -} from "@/lib/looker.ts"; import { _isAboutWelcomeTemplate, } from "../lib/messageUtils.ts"; @@ -52,26 +47,6 @@ export function compareDatesFn(a: NimbusRecipe, b: NimbusRecipe): number { return 0; } -/** - * Appends any FxMS telemetry message data from the query in Look - * https://mozilla.cloud.looker.com/looks/2162 that does not already exist (ie. - * no duplicate message ids) in existingMessageData and returns the result. The - * message data is also cleaned up to match the message data objects from - * ASRouter, remove any test messages, and update templates. - */ -export async function appendFxMSTelemetryData(existingMessageData: any) { - // Get Looker message data (taken from the query in Look - // https://mozilla.cloud.looker.com/looks/2162) - const lookId = "2162"; - let lookerData = await runLookQuery(lookId); - - // Clean and merge Looker data with existing data - let jsonLookerData = cleanLookerData(lookerData); - let mergedData = mergeLookerData(existingMessageData, jsonLookerData); - - return mergedData; -} - export async function getMsgRolloutCollection( recipeCollection: NimbusRecipeCollection, ): Promise { diff --git a/app/fetchData.ts b/app/fetchData.ts index fb2f1d55..20f1385d 100644 --- a/app/fetchData.ts +++ b/app/fetchData.ts @@ -10,14 +10,17 @@ import { import { NimbusRecipe } from "@/lib/nimbusRecipe"; import { NimbusRecipeCollection } from "@/lib/nimbusRecipeCollection"; import { - appendFxMSTelemetryData, compareDatesFn, getMsgRolloutCollection, isLookerEnabled, } from "@/app/dashboard"; import { FxMSMessageInfo } from "./columns"; -import { getCTRPercentData } from "@/lib/looker"; - +import { + cleanLookerData, + getCTRPercentData, + mergeLookerData, + runLookQuery, +} from "@/lib/looker.ts"; export async function fetchData() { const recipeCollection = new NimbusRecipeCollection(); await recipeCollection.fetchRecipes(); @@ -148,3 +151,23 @@ export async function getASRouterLocalColumnFromJSON( // console.log("fxmsMsgInfo: ", fxmsMsgInfo) return fxmsMsgInfo; } + +/** + * Appends any FxMS telemetry message data from the query in Look + * https://mozilla.cloud.looker.com/looks/2162 that does not already exist (ie. + * no duplicate message ids) in existingMessageData and returns the result. The + * message data is also cleaned up to match the message data objects from + * ASRouter, remove any test messages, and update templates. + */ +export async function appendFxMSTelemetryData(existingMessageData: any) { + // Get Looker message data (taken from the query in Look + // https://mozilla.cloud.looker.com/looks/2162) + const lookId = "2162"; + let lookerData = await runLookQuery(lookId); + + // Clean and merge Looker data with existing data + let jsonLookerData = cleanLookerData(lookerData); + let mergedData = mergeLookerData(existingMessageData, jsonLookerData); + + return mergedData; +} From 18873130514a33e6119916b85d04e6dd3f832842 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sun, 23 Mar 2025 11:22:24 -0700 Subject: [PATCH 18/38] Move functions to fetchData and clean up --- app/dashboard.tsx | 43 ------------------------------------------ app/fetchData.ts | 48 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 43 insertions(+), 48 deletions(-) diff --git a/app/dashboard.tsx b/app/dashboard.tsx index 3017eebe..18356129 100644 --- a/app/dashboard.tsx +++ b/app/dashboard.tsx @@ -7,10 +7,8 @@ import { _isAboutWelcomeTemplate, } from "../lib/messageUtils.ts"; -import { NimbusRecipeCollection } from "../lib/nimbusRecipeCollection"; import { _substituteLocalizations } from "../lib/experimentUtils.ts"; -import { NimbusRecipe } from "../lib/nimbusRecipe.ts"; import { MessageTable } from "./message-table"; import { MenuButton } from "@/components/ui/menubutton.tsx"; @@ -18,50 +16,9 @@ import { InfoPopover } from "@/components/ui/infopopover.tsx"; import { Timeline } from "@/components/ui/timeline.tsx"; import { Platform } from "@/lib/types"; -export const isLookerEnabled = process.env.IS_LOOKER_ENABLED === "true"; - const hidden_message_impression_threshold = process.env.HIDDEN_MESSAGE_IMPRESSION_THRESHOLD; -/** - * A sorting function to sort messages by their start dates in descending order. - * If one or both of the recipes is missing a start date, they will be ordered - * identically since there's not enough information to properly sort them by - * date. - * - * @param a Nimbus recipe to compare with `b`. - * @param b Nimbus recipe to compare with `a`. - * @returns -1 if the start date for message a is after the start date for - * message b, zero if they're equal, and 1 otherwise. - */ -export function compareDatesFn(a: NimbusRecipe, b: NimbusRecipe): number { - if (a._rawRecipe.startDate && b._rawRecipe.startDate) { - if (a._rawRecipe.startDate > b._rawRecipe.startDate) { - return -1; - } else if (a._rawRecipe.startDate < b._rawRecipe.startDate) { - return 1; - } - } - - // a must be equal to b - return 0; -} - -export async function getMsgRolloutCollection( - recipeCollection: NimbusRecipeCollection, -): Promise { - const msgRolloutRecipeCollection = new NimbusRecipeCollection(); - msgRolloutRecipeCollection.recipes = recipeCollection.recipes - .filter((recipe) => recipe.usesMessagingFeatures() && !recipe.isExpRecipe()) - .sort(compareDatesFn); - console.log( - "msgRolloutRecipeCollection.length = ", - msgRolloutRecipeCollection.recipes.length, - ); - - return msgRolloutRecipeCollection; -} - interface ReleasedTableProps { platform: string; localData: FxMSMessageInfo[]; diff --git a/app/fetchData.ts b/app/fetchData.ts index 20f1385d..447c7784 100644 --- a/app/fetchData.ts +++ b/app/fetchData.ts @@ -9,11 +9,6 @@ import { } from "@/lib/messageUtils"; import { NimbusRecipe } from "@/lib/nimbusRecipe"; import { NimbusRecipeCollection } from "@/lib/nimbusRecipeCollection"; -import { - compareDatesFn, - getMsgRolloutCollection, - isLookerEnabled, -} from "@/app/dashboard"; import { FxMSMessageInfo } from "./columns"; import { cleanLookerData, @@ -21,6 +16,9 @@ import { mergeLookerData, runLookQuery, } from "@/lib/looker.ts"; + +const isLookerEnabled = process.env.IS_LOOKER_ENABLED === "true"; + export async function fetchData() { const recipeCollection = new NimbusRecipeCollection(); await recipeCollection.fetchRecipes(); @@ -171,3 +169,43 @@ export async function appendFxMSTelemetryData(existingMessageData: any) { return mergedData; } + +/** + * A sorting function to sort messages by their start dates in descending order. + * If one or both of the recipes is missing a start date, they will be ordered + * identically since there's not enough information to properly sort them by + * date. + * + * @param a Nimbus recipe to compare with `b`. + * @param b Nimbus recipe to compare with `a`. + * @returns -1 if the start date for message a is after the start date for + * message b, zero if they're equal, and 1 otherwise. + */ + +export function compareDatesFn(a: NimbusRecipe, b: NimbusRecipe): number { + if (a._rawRecipe.startDate && b._rawRecipe.startDate) { + if (a._rawRecipe.startDate > b._rawRecipe.startDate) { + return -1; + } else if (a._rawRecipe.startDate < b._rawRecipe.startDate) { + return 1; + } + } + + // a must be equal to b + return 0; +} + +export async function getMsgRolloutCollection( + recipeCollection: NimbusRecipeCollection, +): Promise { + const msgRolloutRecipeCollection = new NimbusRecipeCollection(); + msgRolloutRecipeCollection.recipes = recipeCollection.recipes + .filter((recipe) => recipe.usesMessagingFeatures() && !recipe.isExpRecipe()) + .sort(compareDatesFn); + console.log( + "msgRolloutRecipeCollection.length = ", + msgRolloutRecipeCollection.recipes.length, + ); + + return msgRolloutRecipeCollection; +} From a8c8fbb0e19797a4e9155fd36e61b370e73006b9 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sun, 23 Mar 2025 11:23:30 -0700 Subject: [PATCH 19/38] Appease prettier --- app/dashboard.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/dashboard.tsx b/app/dashboard.tsx index 18356129..ad65e5ce 100644 --- a/app/dashboard.tsx +++ b/app/dashboard.tsx @@ -3,9 +3,7 @@ import { FxMSMessageInfo, fxmsMessageColumns, } from "./columns"; -import { - _isAboutWelcomeTemplate, -} from "../lib/messageUtils.ts"; +import { _isAboutWelcomeTemplate } from "../lib/messageUtils.ts"; import { _substituteLocalizations } from "../lib/experimentUtils.ts"; @@ -78,8 +76,9 @@ export const Dashboard = async ({
- {localData - ? : null} + {localData ? ( + + ) : null}
Current {platform} Message Rollouts From 92605cc4b9bc26ddcc789a4bf39a78c3a0c00161 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sun, 23 Mar 2025 11:25:18 -0700 Subject: [PATCH 20/38] Update comment about fetchData file home --- app/fetchData.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/fetchData.ts b/app/fetchData.ts index 447c7784..56dab0db 100644 --- a/app/fetchData.ts +++ b/app/fetchData.ts @@ -1,3 +1,4 @@ +// XXX ultimately, this wants to live in lib/fetchData.ts, but we need to get rid of our dependency on columns.tsx first. import { compareSurfacesFn, getDashboard, From 84d260182b695e7b042ff2b0af6b6150d15f995d Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sun, 23 Mar 2025 14:08:06 -0700 Subject: [PATCH 21/38] Formatting tweaks --- TODO.md | 6 +++--- __tests__/app/dashboard.test.tsx | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/TODO.md b/TODO.md index 971709c0..7db24ea6 100644 --- a/TODO.md +++ b/TODO.md @@ -44,8 +44,7 @@ User Stories. As an Android PM, I should - what does telemetry look like for onboarding? other surfaces? similar to messaging? - Does Click telemetry on both iOS and Android alwyas mean CTA? Or something else? - What is action_uuid extra key (see docs)? - - **Do non-experimental message send pings on iOS or Android?** - - + - ## **Do non-experimental message send pings on iOS or Android?** 1. Draft plan for Android page @@ -63,6 +62,7 @@ User Stories. As an Android PM, I should 2. ~~?Consider options for cloning, since we'll want Android completed page too, and iOS pages (DONE)~~ 3. ~~Create new dir with new page.tsx (MUST)~~ 4. ~~TDD Factor out dashboard (DONE)~~ + 1. Use platform search param (TRIED; TOO FIDDLY, MAYBE LATER) 2. ~~Put in separate android/ route (DONE)~~ @@ -71,7 +71,7 @@ User Stories. As an Android PM, I should 7. Add cases / refactor multiple feature ID list in experimentUtils.ts (MUST) 8. Add cases / refactor nimbusRecipe.ts:getBranchInfo (MUST) - 9. Add cases / refactor messageUtils.getDashboard (LATER) + 9. Add cases / refactor messageUtils.getDashboard (LATER) 10. Update / move messageUtils.getDashboardIdForTemplate (LATER) 11. Add cases / refactor templates & getSurfaceDataForTemplate (LATER) 12. Add cases / refactor looker.ts:getCTRPercentData (LATER) diff --git a/__tests__/app/dashboard.test.tsx b/__tests__/app/dashboard.test.tsx index 4ce5d530..48ae9b3e 100644 --- a/__tests__/app/dashboard.test.tsx +++ b/__tests__/app/dashboard.test.tsx @@ -21,9 +21,7 @@ const mockFetchData = { describe.skip("Dashboard", () => { it("all timeline pill ids exist in the Dashboard component in /", async () => { - const dashboard = await render( - await - ); + const dashboard = await render(await ()); const firefox = dashboard.getByTestId("firefox"); const experiments = dashboard.getByTestId("live_experiments"); From db42cee894a76ef3d11834403b78a1f55f676c18 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sun, 23 Mar 2025 14:09:45 -0700 Subject: [PATCH 22/38] Update TODO list --- TODO.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index 7db24ea6..3ec2211b 100644 --- a/TODO.md +++ b/TODO.md @@ -44,7 +44,7 @@ User Stories. As an Android PM, I should - what does telemetry look like for onboarding? other surfaces? similar to messaging? - Does Click telemetry on both iOS and Android alwyas mean CTA? Or something else? - What is action_uuid extra key (see docs)? - - ## **Do non-experimental message send pings on iOS or Android?** + - **Do non-experimental message send pings on iOS or Android?** 1. Draft plan for Android page @@ -66,7 +66,7 @@ User Stories. As an Android PM, I should 1. Use platform search param (TRIED; TOO FIDDLY, MAYBE LATER) 2. ~~Put in separate android/ route (DONE)~~ - 5. Make test & code updates to not display local table (WIP) + 5. Make test & code updates to not display local table (DONE) 6. Factor "application=" out of env (MUST) 7. Add cases / refactor multiple feature ID list in experimentUtils.ts (MUST) 8. Add cases / refactor nimbusRecipe.ts:getBranchInfo (MUST) From 31dc0095f5e7d9fabf864169efdcd85a68a92924 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sun, 23 Mar 2025 16:45:41 -0700 Subject: [PATCH 23/38] Flesh out Android standup plan --- TODO.md | 57 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/TODO.md b/TODO.md index 3ec2211b..1d5fc823 100644 --- a/TODO.md +++ b/TODO.md @@ -62,37 +62,46 @@ User Stories. As an Android PM, I should 2. ~~?Consider options for cloning, since we'll want Android completed page too, and iOS pages (DONE)~~ 3. ~~Create new dir with new page.tsx (MUST)~~ 4. ~~TDD Factor out dashboard (DONE)~~ - 1. Use platform search param (TRIED; TOO FIDDLY, MAYBE LATER) 2. ~~Put in separate android/ route (DONE)~~ + 5. ~~Refactor to not display local table on Android (DONE)~~ - 5. Make test & code updates to not display local table (DONE) 6. Factor "application=" out of env (MUST) - 7. Add cases / refactor multiple feature ID list in experimentUtils.ts (MUST) - 8. Add cases / refactor nimbusRecipe.ts:getBranchInfo (MUST) - - 9. Add cases / refactor messageUtils.getDashboard (LATER) - 10. Update / move messageUtils.getDashboardIdForTemplate (LATER) - 11. Add cases / refactor templates & getSurfaceDataForTemplate (LATER) - 12. Add cases / refactor looker.ts:getCTRPercentData (LATER) - 13. Make pills exclude local if not on desktop (LATER) - - 14. Update columns.tsx:filterBySurface (LATER) - 15. Add l10n (LATER) - 16. Factor Out NimbusMessageTable (NICE) - 17. Factor out high-level data fetching (NICE) - - 18. Pull in Android experiments using that URL - 19. Build dashboard link - 20. How to handle multi types - 21. Build CTR - 22. How to handle multi types - -2. standup 2nd page + 1. Create PlatformInfo interface + 1. application name + 2. Create PlatformInfoDict containing (android, desktop) + 3. pull experiments path component into EXPERIMENTER_API_PREFIX + 4. Get application param from PlatformInfoDict; remove from env + 5. Get status param from appropriate files; remove from env + 7. Pull platform-specific-feature-list from experimentUtils into + PlatformInfo (MUST) + 8. Move nimbusRecipe.ts:getBranchInfo into own file included into PlatformInfo? (add messaging case for now and push to later or SPIKE) + + 9. Add cases / refactor messageUtils.getDashboard (IMPT) + 10. Update / move messageUtils.getDashboardIdForTemplate (IMPT) + + 11. Make pills exclude local if not on desktop (NICE) +. 2. Add cases / refactor looker.ts:getCTRPercentData (NICE) + + 1. Add cases / refactor templates & getSurfaceDataForTemplate (LATER) + 2. Support microsurveys badge, if sensible on mobile (LATER) + 3. Update columns.tsx:filterBySurface (LATER) + 4. Add l10n (LATER) + + 5. Factor Out NimbusMessageTable (EVEN LATER) + 6. Factor out high-level data fetching (EVEN LATER) + + 7. Pull in Android experiments using that URL + 8. Build dashboard link + 9. How to handle multi types + 10. Build CTR + 11. How to handle multi types + +1. standup 2nd page - TDD? clone for mobile -3. standup 2nd dashboard +2. standup 2nd dashboard * review mobile telemetry using glean dict * look at explores available for those tables From 20b3e7a8e40dfe8e05752d6c1a492c72d8f6b4e3 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Mon, 24 Mar 2025 09:03:28 -0700 Subject: [PATCH 24/38] Minor TODO update --- TODO.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index 1d5fc823..11544e24 100644 --- a/TODO.md +++ b/TODO.md @@ -67,7 +67,7 @@ User Stories. As an Android PM, I should 5. ~~Refactor to not display local table on Android (DONE)~~ 6. Factor "application=" out of env (MUST) - 1. Create PlatformInfo interface + 1. Create PlatformInfo interface 1. application name 2. Create PlatformInfoDict containing (android, desktop) 3. pull experiments path component into EXPERIMENTER_API_PREFIX @@ -75,7 +75,7 @@ User Stories. As an Android PM, I should 5. Get status param from appropriate files; remove from env 7. Pull platform-specific-feature-list from experimentUtils into PlatformInfo (MUST) - 8. Move nimbusRecipe.ts:getBranchInfo into own file included into PlatformInfo? (add messaging case for now and push to later or SPIKE) + 8. Move nimbusRecipe.ts:getBranchInfo into own file included into PlatformInfo? (fallback: add messaging case for now; move to PlatformInfo later) 9. Add cases / refactor messageUtils.getDashboard (IMPT) 10. Update / move messageUtils.getDashboardIdForTemplate (IMPT) From a9002b11e5bdab77d3755a520dcbc1d7d2fa9c76 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Mon, 24 Mar 2025 09:04:34 -0700 Subject: [PATCH 25/38] Add WIP MOBILE-EPICS list --- MOBILE-EPICS.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 MOBILE-EPICS.md diff --git a/MOBILE-EPICS.md b/MOBILE-EPICS.md new file mode 100644 index 00000000..8ec5ae81 --- /dev/null +++ b/MOBILE-EPICS.md @@ -0,0 +1,33 @@ + +# Android +1. Dashboard Epic + 1. Make messaging feature Looker dashboard (3: DONE) + 2. Add monthly CTR, shared folder, permissions & docs) (LATER) +2. Stand up viewable (though incorrect) dashboard route (3: DONE) +3. Make things work for Android & Desktop enough to see Android msg rollouts: + 1. Page route/dashboard component (5: DONE) + 2. Data fetching: (5: DONE) + 3. Experimenter API client work (5) + 5. Feature ID list (3) + 6. Nimbus.GetBranchInfo (5) +4. Basic usability fixes + 1. Hide & LATER experiments table if not working (2) + 2. Make pills exclude "Firefox" on Android page (3?) +5. Rapidly Port features + 1. Add at least one other subsurfaces now? See Research + 2. Dashboard links + 3. Add full length Impressions/CTR chart to Looker dashboard + 4. Inline Impressions/CTR + 5. Better surface column + 6. Completed page +6. Maybe (some need research) + 1. Add other feature IDs? Prob at least onboarding + 2. Microsurveys badge? + 3. Surface-based filtering? +# Key Unknowns to research + 1. Non-Nimbus mobile messaging telemetry + 2. How much work is adding messaging sub-surfaces? + 3. Which other feature ids (eg onboarding) are desired? How much work will they be? + +# iOS + From 43a56a8d395fb9ce3a663d45fc2d0d38b0363188 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Mon, 24 Mar 2025 11:32:27 -0700 Subject: [PATCH 26/38] Update mobile epics --- MOBILE-EPICS.md | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/MOBILE-EPICS.md b/MOBILE-EPICS.md index 8ec5ae81..a8d81d03 100644 --- a/MOBILE-EPICS.md +++ b/MOBILE-EPICS.md @@ -1,33 +1,39 @@ +Top-level bullets are user story epics, 2nd-level bullet are regular user stories # Android -1. Dashboard Epic +1. Looker Dashboard 1. Make messaging feature Looker dashboard (3: DONE) - 2. Add monthly CTR, shared folder, permissions & docs) (LATER) -2. Stand up viewable (though incorrect) dashboard route (3: DONE) + 2. Shared folder, permissions & docs) (LATER) +2. Stand up viewable (though incorrect) Skylight dashboard route (3: DONE) 3. Make things work for Android & Desktop enough to see Android msg rollouts: 1. Page route/dashboard component (5: DONE) 2. Data fetching: (5: DONE) 3. Experimenter API client work (5) - 5. Feature ID list (3) - 6. Nimbus.GetBranchInfo (5) -4. Basic usability fixes - 1. Hide & LATER experiments table if not working (2) - 2. Make pills exclude "Firefox" on Android page (3?) -5. Rapidly Port features - 1. Add at least one other subsurfaces now? See Research - 2. Dashboard links - 3. Add full length Impressions/CTR chart to Looker dashboard - 4. Inline Impressions/CTR - 5. Better surface column + 4. Feature ID list (3) + 5. Nimbus.GetBranchInfo (5) + +4. Add experiments - or hide and LATER if interesting amount of work (3) +5. Key Unknowns to research + 1. Make a list of all-subsurfaces with links to telemetry + 2. How to handle production telemetry + 3. How much work is adding messaging sub-surfaces? + 4. Which other feature ids (eg onboarding) are desired? How much work will they be +6. Basic usability fix + 1. Make pills exclude "Firefox" on Android page (3?) +7. Handle production telemetry (waiting on research) +7. Rapidly Port features + 1. Add at least one other subsurface now? (waiting on research) + 2. Dashboard links (5) + 3. Add monthly Impressions/CTR chart to Looker dashboard (2) + 4. Inline Impressions/CTR (8 - needs breakdown or SPIKE) + 5. Fully useful surface columns () 6. Completed page -6. Maybe (some need research) +8. Maybe 1. Add other feature IDs? Prob at least onboarding 2. Microsurveys badge? 3. Surface-based filtering? -# Key Unknowns to research - 1. Non-Nimbus mobile messaging telemetry - 2. How much work is adding messaging sub-surfaces? - 3. Which other feature ids (eg onboarding) are desired? How much work will they be? # iOS +1. Looker Dashboard +2. Stand up viewable (though incorrect) Skylight dashboard route (3) From 2b3b3c92615bd2452cc4c9b90afc2b752d7d0e5b Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Mon, 24 Mar 2025 12:58:44 -0700 Subject: [PATCH 27/38] Appease prettier --- MOBILE-EPICS.md | 10 +++-- TODO.md | 99 +++++++++++++++++++++++++------------------------ 2 files changed, 56 insertions(+), 53 deletions(-) diff --git a/MOBILE-EPICS.md b/MOBILE-EPICS.md index a8d81d03..2f694119 100644 --- a/MOBILE-EPICS.md +++ b/MOBILE-EPICS.md @@ -1,11 +1,13 @@ Top-level bullets are user story epics, 2nd-level bullet are regular user stories # Android + 1. Looker Dashboard 1. Make messaging feature Looker dashboard (3: DONE) 2. Shared folder, permissions & docs) (LATER) 2. Stand up viewable (though incorrect) Skylight dashboard route (3: DONE) 3. Make things work for Android & Desktop enough to see Android msg rollouts: + 1. Page route/dashboard component (5: DONE) 2. Data fetching: (5: DONE) 3. Experimenter API client work (5) @@ -17,23 +19,23 @@ Top-level bullets are user story epics, 2nd-level bullet are regular user storie 1. Make a list of all-subsurfaces with links to telemetry 2. How to handle production telemetry 3. How much work is adding messaging sub-surfaces? - 4. Which other feature ids (eg onboarding) are desired? How much work will they be + 4. Which other feature ids (eg onboarding) are desired? How much work will they be 6. Basic usability fix 1. Make pills exclude "Firefox" on Android page (3?) 7. Handle production telemetry (waiting on research) -7. Rapidly Port features +8. Rapidly Port features 1. Add at least one other subsurface now? (waiting on research) 2. Dashboard links (5) 3. Add monthly Impressions/CTR chart to Looker dashboard (2) 4. Inline Impressions/CTR (8 - needs breakdown or SPIKE) 5. Fully useful surface columns () 6. Completed page -8. Maybe +9. Maybe 1. Add other feature IDs? Prob at least onboarding 2. Microsurveys badge? 3. Surface-based filtering? # iOS + 1. Looker Dashboard 2. Stand up viewable (though incorrect) Skylight dashboard route (3) - diff --git a/TODO.md b/TODO.md index 11544e24..b5ead058 100644 --- a/TODO.md +++ b/TODO.md @@ -48,60 +48,61 @@ User Stories. As an Android PM, I should 1. Draft plan for Android page - 1. ?File ticket - 2. Build chart for Android messaging (DONE) - 3. Build 2nd chart (LATER) - 4. Build dashboard (DONE: id = 2191) - 5. Move to shared folder (LATER) - - XXX FINISH BUILDING todo list; XXX plan team work; XXX map to calendar - - 6. Build Android page - - 1. ~~Review existing clone for "completed" (DONE)~~ - 2. ~~?Consider options for cloning, since we'll want Android completed page too, and iOS pages (DONE)~~ - 3. ~~Create new dir with new page.tsx (MUST)~~ - 4. ~~TDD Factor out dashboard (DONE)~~ - 1. Use platform search param (TRIED; TOO FIDDLY, MAYBE LATER) - 2. ~~Put in separate android/ route (DONE)~~ - 5. ~~Refactor to not display local table on Android (DONE)~~ - - 6. Factor "application=" out of env (MUST) - 1. Create PlatformInfo interface - 1. application name - 2. Create PlatformInfoDict containing (android, desktop) - 3. pull experiments path component into EXPERIMENTER_API_PREFIX - 4. Get application param from PlatformInfoDict; remove from env - 5. Get status param from appropriate files; remove from env - 7. Pull platform-specific-feature-list from experimentUtils into - PlatformInfo (MUST) - 8. Move nimbusRecipe.ts:getBranchInfo into own file included into PlatformInfo? (fallback: add messaging case for now; move to PlatformInfo later) - - 9. Add cases / refactor messageUtils.getDashboard (IMPT) - 10. Update / move messageUtils.getDashboardIdForTemplate (IMPT) - - 11. Make pills exclude local if not on desktop (NICE) -. 2. Add cases / refactor looker.ts:getCTRPercentData (NICE) - - 1. Add cases / refactor templates & getSurfaceDataForTemplate (LATER) - 2. Support microsurveys badge, if sensible on mobile (LATER) - 3. Update columns.tsx:filterBySurface (LATER) - 4. Add l10n (LATER) - - 5. Factor Out NimbusMessageTable (EVEN LATER) - 6. Factor out high-level data fetching (EVEN LATER) - - 7. Pull in Android experiments using that URL - 8. Build dashboard link - 9. How to handle multi types - 10. Build CTR - 11. How to handle multi types + 1. ?File ticket + 2. Build chart for Android messaging (DONE) + 3. Build 2nd chart (LATER) + 4. Build dashboard (DONE: id = 2191) + 5. Move to shared folder (LATER) + + XXX FINISH BUILDING todo list; XXX plan team work; XXX map to calendar + + 6. Build Android page + + 1. ~~Review existing clone for "completed" (DONE)~~ + 2. ~~?Consider options for cloning, since we'll want Android completed page too, and iOS pages (DONE)~~ + 3. ~~Create new dir with new page.tsx (MUST)~~ + 4. ~~TDD Factor out dashboard (DONE)~~ + 1. Use platform search param (TRIED; TOO FIDDLY, MAYBE LATER) + 2. ~~Put in separate android/ route (DONE)~~ + 5. ~~Refactor to not display local table on Android (DONE)~~ + + 6. Factor "application=" out of env (MUST) + 1. Create PlatformInfo interface + 1. application name + 2. Create PlatformInfoDict containing (android, desktop) + 3. pull experiments path component into EXPERIMENTER_API_PREFIX + 4. Get application param from PlatformInfoDict; remove from env + 5. Get status param from appropriate files; remove from env + 7. Pull platform-specific-feature-list from experimentUtils into + PlatformInfo (MUST) + 8. Move nimbusRecipe.ts:getBranchInfo into own file included into PlatformInfo? (fallback: add messaging case for now; move to PlatformInfo later) + + 9. Add cases / refactor messageUtils.getDashboard (IMPT) + 10. Update / move messageUtils.getDashboardIdForTemplate (IMPT) + + 11. Make pills exclude local if not on desktop (NICE) + + . 2. Add cases / refactor looker.ts:getCTRPercentData (NICE) + + 1. Add cases / refactor templates & getSurfaceDataForTemplate (LATER) + 2. Support microsurveys badge, if sensible on mobile (LATER) + 3. Update columns.tsx:filterBySurface (LATER) + 4. Add l10n (LATER) + + 5. Factor Out NimbusMessageTable (EVEN LATER) + 6. Factor out high-level data fetching (EVEN LATER) + + 7. Pull in Android experiments using that URL + 8. Build dashboard link + 9. How to handle multi types + 10. Build CTR + 11. How to handle multi types 1. standup 2nd page - TDD? clone for mobile -2. standup 2nd dashboard +1. standup 2nd dashboard * review mobile telemetry using glean dict * look at explores available for those tables From 3c89b3b9daee4973cefc90132940c755d09e20ff Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Tue, 25 Mar 2025 16:47:03 -0700 Subject: [PATCH 28/38] Create platformInfo object --- lib/platformInfo.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 lib/platformInfo.ts diff --git a/lib/platformInfo.ts b/lib/platformInfo.ts new file mode 100644 index 00000000..efebd876 --- /dev/null +++ b/lib/platformInfo.ts @@ -0,0 +1,21 @@ + +interface PlatformInfo { + nimbusAppSlug: string; + displayName: string; +} + +export const platformDictionary: { [key: string]: PlatformInfo } = { + android: { + nimbusAppSlug: "fenix", + displayName: "Android", + }, + ios: { + nimbusAppSlug: "ios", + displayName: "iOS", + }, + desktop: { + nimbusAppSlug: "firefox-desktop", + displayName: "Desktop", + }, +}; + From 9465ea5ca672bed27332e29446a1758de6309ffc Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Tue, 25 Mar 2025 17:14:18 -0700 Subject: [PATCH 29/38] Added a test to check for correct URL construction --- __tests__/lib/nimbusRecipeCollection.test.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/__tests__/lib/nimbusRecipeCollection.test.ts b/__tests__/lib/nimbusRecipeCollection.test.ts index f9d5082e..3b2a8585 100644 --- a/__tests__/lib/nimbusRecipeCollection.test.ts +++ b/__tests__/lib/nimbusRecipeCollection.test.ts @@ -27,6 +27,26 @@ describe("NimbusRecipeCollection", () => { expect(recipes).toEqual([new NimbusRecipe(fakeFetchData[0])]); }); + + it("constructs the correct URL for live experiments", async () => { + const nimbusRecipeCollection = new NimbusRecipeCollection(); + await nimbusRecipeCollection.fetchRecipes(); + + expect(global.fetch).toHaveBeenCalledWith( + `${process.env.EXPERIMENTER_API_PREFIX}${process.env.EXPERIMENTER_API_CALL_LIVE}`, + { credentials: "omit" }, + ); + }); + + it("constructs the correct URL for completed experiments", async () => { + const nimbusRecipeCollection = new NimbusRecipeCollection(true); + await nimbusRecipeCollection.fetchRecipes(); + + expect(global.fetch).toHaveBeenCalledWith( + `${process.env.EXPERIMENTER_API_PREFIX}${process.env.EXPERIMENTER_API_CALL_COMPLETED}`, + { credentials: "omit" }, + ); + }); }); describe("getExperimentAndBranchInfos", () => { From 68477443897c498be649aada938e7033b31f6fba Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Wed, 26 Mar 2025 13:22:08 -0700 Subject: [PATCH 30/38] Move experiments path component to a more sensible env var --- .env | 6 +++--- TODO.md | 39 ++++++++++++++++++++------------------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/.env b/.env index c1f850a2..075c426e 100644 --- a/.env +++ b/.env @@ -2,16 +2,16 @@ # Base URL for the experimenter API (defaults to the production instance) # -EXPERIMENTER_API_PREFIX="https://experimenter.services.mozilla.com/api/v7/" +EXPERIMENTER_API_PREFIX="https://experimenter.services.mozilla.com/api/v7/experiments/" # API calls with parameters to fetch experiments we want to display. # https://htmlpreview.github.io/?https://github.com/mozilla/experimenter/blob/main/docs/experimenter/swagger-ui.html has more info. # # Live experiments -EXPERIMENTER_API_CALL_LIVE="experiments/?status=Live&application=firefox-desktop" +EXPERIMENTER_API_CALL_LIVE="?status=Live&application=firefox-desktop" # Completed experiments -EXPERIMENTER_API_CALL_COMPLETED="experiments/?status=Complete&application=firefox-desktop" +EXPERIMENTER_API_CALL_COMPLETED="?status=Complete&application=firefox-desktop" # Looker configurables IS_LOOKER_ENABLED=false diff --git a/TODO.md b/TODO.md index b5ead058..d3c73ea8 100644 --- a/TODO.md +++ b/TODO.md @@ -67,12 +67,13 @@ User Stories. As an Android PM, I should 5. ~~Refactor to not display local table on Android (DONE)~~ 6. Factor "application=" out of env (MUST) - 1. Create PlatformInfo interface - 1. application name - 2. Create PlatformInfoDict containing (android, desktop) - 3. pull experiments path component into EXPERIMENTER_API_PREFIX - 4. Get application param from PlatformInfoDict; remove from env - 5. Get status param from appropriate files; remove from env + 1. ~~Create PlatformInfo interface~~ + 1. ~~application name~~ + 2. ~~Create PlatformInfoDict containing (android, desktop)~~ + 3. ~~pull experiments path component into EXPERIMENTER_API_PREFIX~~ + 4. Figure out how to resolve NimbusAppSlug and Platform param stuff + 5. Get application param from PlatformInfoDict; remove from env + 6. Get status param from appropriate files; remove from env 7. Pull platform-specific-feature-list from experimentUtils into PlatformInfo (MUST) 8. Move nimbusRecipe.ts:getBranchInfo into own file included into PlatformInfo? (fallback: add messaging case for now; move to PlatformInfo later) @@ -84,25 +85,25 @@ User Stories. As an Android PM, I should . 2. Add cases / refactor looker.ts:getCTRPercentData (NICE) - 1. Add cases / refactor templates & getSurfaceDataForTemplate (LATER) - 2. Support microsurveys badge, if sensible on mobile (LATER) - 3. Update columns.tsx:filterBySurface (LATER) - 4. Add l10n (LATER) + 12. Add cases / refactor templates & getSurfaceDataForTemplate (LATER) + 13. Support microsurveys badge, if sensible on mobile (LATER) + 14. Update columns.tsx:filterBySurface (LATER) + 15. Add l10n (LATER) - 5. Factor Out NimbusMessageTable (EVEN LATER) - 6. Factor out high-level data fetching (EVEN LATER) + 16. Factor Out NimbusMessageTable (EVEN LATER) + 17. Factor out high-level data fetching (EVEN LATER) - 7. Pull in Android experiments using that URL - 8. Build dashboard link - 9. How to handle multi types - 10. Build CTR - 11. How to handle multi types + 18. Pull in Android experiments using that URL + 19. Build dashboard link + 20. How to handle multi types + 21. Build CTR + 22. How to handle multi types -1. standup 2nd page +2. standup 2nd page - TDD? clone for mobile -1. standup 2nd dashboard +3. standup 2nd dashboard * review mobile telemetry using glean dict * look at explores available for those tables From 73566a48af7b70e5d6188cfc0a7fa6e326987201 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Wed, 26 Mar 2025 13:36:07 -0700 Subject: [PATCH 31/38] Standarize platform typing to the nimbus platform slug strings --- app/android/page.tsx | 2 +- app/dashboard.tsx | 2 +- app/page.tsx | 2 +- lib/platformInfo.ts | 15 +++++---------- lib/types.ts | 8 +++++--- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/app/android/page.tsx b/app/android/page.tsx index 0910294c..d428ab94 100644 --- a/app/android/page.tsx +++ b/app/android/page.tsx @@ -12,7 +12,7 @@ export default async function Page() { return ( = { + fenix: { displayName: "Android", }, ios: { - nimbusAppSlug: "ios", displayName: "iOS", }, - desktop: { - nimbusAppSlug: "firefox-desktop", + "firefox-desktop": { displayName: "Desktop", }, }; - diff --git a/lib/types.ts b/lib/types.ts index f6dda6b0..68681e99 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -1,3 +1,5 @@ -// These are used as strings in the UI, so we want to force them to be -// consistent and not have to worry about typos. -export type Platform = "Android" | "iOS" | "Desktop"; +// These are the same strings that the experimenter API uses to determine which +// endpoint to hit. XXX we should use our own ID and put this in +// PlatformInfo in case Nimbus changes its strings. + +export type Platform = "fenix" | "ios" | "firefox-desktop"; From e248a93b89d1c52e57789a18d2ac980417e9cef6 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Wed, 26 Mar 2025 13:44:25 -0700 Subject: [PATCH 32/38] Fix platformDisplayName use --- app/dashboard.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/dashboard.tsx b/app/dashboard.tsx index e98f0471..7c29d838 100644 --- a/app/dashboard.tsx +++ b/app/dashboard.tsx @@ -13,16 +13,17 @@ import { MenuButton } from "@/components/ui/menubutton.tsx"; import { InfoPopover } from "@/components/ui/infopopover.tsx"; import { Timeline } from "@/components/ui/timeline.tsx"; import { Platform } from "@/lib/types"; +import { platformDictionary } from "@/lib/platformInfo.ts"; const hidden_message_impression_threshold = process.env.HIDDEN_MESSAGE_IMPRESSION_THRESHOLD; interface ReleasedTableProps { - platform: string; + platformDisplayName: string; localData: FxMSMessageInfo[]; } -const ReleasedTable = async ({ platform, localData }: ReleasedTableProps) => { +const ReleasedTable = async ({ platformDisplayName, localData }: ReleasedTableProps) => { return (
{ data-testid="firefox" className="scroll-m-20 text-xl font-semibold text-center pt-6 flex items-center justify-center gap-x-1" > - {platform} Messages Released on Firefox + {platformDisplayName} Messages Released on Firefox { + + const platformDisplayName = platformDictionary[platform].displayName; + return (
@@ -77,11 +81,11 @@ export const Dashboard = async ({
{localData ? ( - + ) : null}
- Current {platform} Message Rollouts + Current {platformDisplayName} Message Rollouts
- Current {platform} Message Experiments + Current {platformDisplayName} Message Experiments
Date: Wed, 26 Mar 2025 17:02:36 -0700 Subject: [PATCH 33/38] Remove some env vars for modularity --- .env | 9 --------- .env.sample | 5 +++-- __tests__/lib/nimbusRecipeCollection.test.ts | 12 +++++++++--- app/android/page.tsx | 8 ++++++-- app/fetchData.ts | 5 +++-- app/page.tsx | 6 ++++-- lib/nimbusRecipeCollection.ts | 17 ++++++++++++++--- 7 files changed, 39 insertions(+), 23 deletions(-) diff --git a/.env b/.env index 075c426e..3641f420 100644 --- a/.env +++ b/.env @@ -4,15 +4,6 @@ # EXPERIMENTER_API_PREFIX="https://experimenter.services.mozilla.com/api/v7/experiments/" -# API calls with parameters to fetch experiments we want to display. -# https://htmlpreview.github.io/?https://github.com/mozilla/experimenter/blob/main/docs/experimenter/swagger-ui.html has more info. -# -# Live experiments -EXPERIMENTER_API_CALL_LIVE="?status=Live&application=firefox-desktop" - -# Completed experiments -EXPERIMENTER_API_CALL_COMPLETED="?status=Complete&application=firefox-desktop" - # Looker configurables IS_LOOKER_ENABLED=false LOOKERSDK_BASE_URL=null diff --git a/.env.sample b/.env.sample index 39026f15..5a380ee4 100644 --- a/.env.sample +++ b/.env.sample @@ -1,7 +1,8 @@ # Copy this to .env.local to set local variables - # Preview -# EXPERIMENTER_API_CALL="experiments/?status=Preview&application=firefox-desktop" +# Base URL for the experimenter API (defaults to the production instance) +# +EXPERIMENTER_API_PREFIX="https://experimenter.services.mozilla.com/api/v7/experiments/" # Disable Auth0 for dev && preview environments IS_AUTH_ENABLED='false' diff --git a/__tests__/lib/nimbusRecipeCollection.test.ts b/__tests__/lib/nimbusRecipeCollection.test.ts index 3b2a8585..5abf41fa 100644 --- a/__tests__/lib/nimbusRecipeCollection.test.ts +++ b/__tests__/lib/nimbusRecipeCollection.test.ts @@ -2,6 +2,9 @@ import { NimbusRecipe } from "@/lib/nimbusRecipe"; import { NimbusRecipeCollection } from "@/lib/nimbusRecipeCollection"; import { ExperimentFakes } from "@/__tests__/ExperimentFakes.mjs"; import { RecipeInfo } from "@/app/columns"; +import { Platform } from "@/lib/types"; + +const platform: Platform = "firefox-desktop"; const fakeFetchData = [ExperimentFakes.recipe()]; global.fetch = jest.fn(() => @@ -29,11 +32,14 @@ describe("NimbusRecipeCollection", () => { }); it("constructs the correct URL for live experiments", async () => { - const nimbusRecipeCollection = new NimbusRecipeCollection(); + const nimbusRecipeCollection = new NimbusRecipeCollection( + false, + platform, + ); //XXX YYY await nimbusRecipeCollection.fetchRecipes(); expect(global.fetch).toHaveBeenCalledWith( - `${process.env.EXPERIMENTER_API_PREFIX}${process.env.EXPERIMENTER_API_CALL_LIVE}`, + `${process.env.EXPERIMENTER_API_PREFIX}?status=Live&application=${platform}`, { credentials: "omit" }, ); }); @@ -43,7 +49,7 @@ describe("NimbusRecipeCollection", () => { await nimbusRecipeCollection.fetchRecipes(); expect(global.fetch).toHaveBeenCalledWith( - `${process.env.EXPERIMENTER_API_PREFIX}${process.env.EXPERIMENTER_API_CALL_COMPLETED}`, + `${process.env.EXPERIMENTER_API_PREFIX}?status=Complete&application=${platform}`, { credentials: "omit" }, ); }); diff --git a/app/android/page.tsx b/app/android/page.tsx index d428ab94..1fa3e62e 100644 --- a/app/android/page.tsx +++ b/app/android/page.tsx @@ -1,5 +1,8 @@ import { Dashboard } from "@/app/dashboard"; import { fetchData } from "@/app/fetchData"; +import { Platform } from "@/lib/types"; + +const platform: Platform = "fenix"; export default async function Page() { const { @@ -8,11 +11,12 @@ export default async function Page() { totalExperiments, msgRolloutInfo, totalRolloutExperiments, - } = await fetchData(); + } = await fetchData(platform); + return ( ; isCompleted: boolean; fetchRecipes: () => Promise>; + platform: Platform; }; /** @@ -47,16 +49,25 @@ async function updateBranchesCTR(recipe: NimbusRecipe): Promise { export class NimbusRecipeCollection implements NimbusRecipeCollectionType { recipes: Array; isCompleted: boolean; + platform: Platform; - constructor(isCompleted: boolean = false) { + // XXX XXX remove this default platform, it's a total footgun + constructor( + isCompleted: boolean = false, + platform: Platform = "firefox-desktop", + ) { this.recipes = []; this.isCompleted = isCompleted; + this.platform = platform; } async fetchRecipes(): Promise> { - let experimenterUrl = `${process.env.EXPERIMENTER_API_PREFIX}${process.env.EXPERIMENTER_API_CALL_LIVE}`; + // XXX should really be using URL.parse and URLSearchParams to manage all + // this stuff + let experimenterUrl = `${process.env.EXPERIMENTER_API_PREFIX}?status=Live&application=${this.platform}`; if (this.isCompleted) { - experimenterUrl = `${process.env.EXPERIMENTER_API_PREFIX}${process.env.EXPERIMENTER_API_CALL_COMPLETED}`; + // XXX rename to isComplete for consistency + experimenterUrl = `${process.env.EXPERIMENTER_API_PREFIX}?status=Complete&application=${this.platform}`; } // console.log("experimenterURL = ", experimenterUrl) From 06c39071f73ebb7e549fe89e4a3c4e470c280952 Mon Sep 17 00:00:00 2001 From: Sarah Chung Date: Tue, 15 Apr 2025 14:16:00 -0400 Subject: [PATCH 34/38] Clean up PR to review --- MOBILE-EPICS.md | 41 --------------- TODO.md | 118 ------------------------------------------- app/android/page.tsx | 1 - app/dashboard.tsx | 11 ++-- 4 files changed, 8 insertions(+), 163 deletions(-) delete mode 100644 MOBILE-EPICS.md delete mode 100644 TODO.md diff --git a/MOBILE-EPICS.md b/MOBILE-EPICS.md deleted file mode 100644 index 2f694119..00000000 --- a/MOBILE-EPICS.md +++ /dev/null @@ -1,41 +0,0 @@ -Top-level bullets are user story epics, 2nd-level bullet are regular user stories - -# Android - -1. Looker Dashboard - 1. Make messaging feature Looker dashboard (3: DONE) - 2. Shared folder, permissions & docs) (LATER) -2. Stand up viewable (though incorrect) Skylight dashboard route (3: DONE) -3. Make things work for Android & Desktop enough to see Android msg rollouts: - - 1. Page route/dashboard component (5: DONE) - 2. Data fetching: (5: DONE) - 3. Experimenter API client work (5) - 4. Feature ID list (3) - 5. Nimbus.GetBranchInfo (5) - -4. Add experiments - or hide and LATER if interesting amount of work (3) -5. Key Unknowns to research - 1. Make a list of all-subsurfaces with links to telemetry - 2. How to handle production telemetry - 3. How much work is adding messaging sub-surfaces? - 4. Which other feature ids (eg onboarding) are desired? How much work will they be -6. Basic usability fix - 1. Make pills exclude "Firefox" on Android page (3?) -7. Handle production telemetry (waiting on research) -8. Rapidly Port features - 1. Add at least one other subsurface now? (waiting on research) - 2. Dashboard links (5) - 3. Add monthly Impressions/CTR chart to Looker dashboard (2) - 4. Inline Impressions/CTR (8 - needs breakdown or SPIKE) - 5. Fully useful surface columns () - 6. Completed page -9. Maybe - 1. Add other feature IDs? Prob at least onboarding - 2. Microsurveys badge? - 3. Surface-based filtering? - -# iOS - -1. Looker Dashboard -2. Stand up viewable (though incorrect) Skylight dashboard route (3) diff --git a/TODO.md b/TODO.md deleted file mode 100644 index d3c73ea8..00000000 --- a/TODO.md +++ /dev/null @@ -1,118 +0,0 @@ -Goal: stand up mobile version of experiments/rollouts - -Epics: - -- BIG UNKNOWN: figure out outside-of-nimbus plan -- stand up android - - messaging surface - - onboarding surface - - UNKNOWN: enumerate other surfaces -- stand up iOS - - UNKNOWN: sort out surfaces - - redo android steps, but without all the refactoring - -User Stories. As an Android PM, I should - -- have an easy view of rollouts/branches on the messaging surface - - - so I can see a bunch of what Android users see (on the message surface) - - 1-10 - -- research - - - [surfaces & guidelines](https://mozilla-hub.atlassian.net/wiki/spaces/FIREFOX/pages/210206760/Mobile+Message+Surface+Guidelines) - - [mobile telemetry docs](https://experimenter.info/messaging/mobile-messaging/#events-emitted) - - [desktop explore](https://mozilla.cloud.looker.com/explore/user_journey/event_counts) - - 1. look at iOS telemetry & explores - - - [iOS message probes](https://dictionary.telemetry.mozilla.org/apps/firefox_ios?page=1&search=messag) - - Note no experiments probes like Android has - - [iOS event count explore](https://mozilla.cloud.looker.com/explore/firefox_ios/event_counts?qid=OZqOXzZqTujARgvCK12NJ4) - - [recent iOS clicked events](https://mozilla.cloud.looker.com/explore/firefox_ios/event_counts?qid=jQpgYwZpBZEhW73B1dcyzu&toggle=fil,vis) - - XXX look at onboarding also - - 2. look at Android telemetry & explores - - [Android message probes - Glean dictionary](https://dictionary.telemetry.mozilla.org/apps/fenix?page=1&search=messaging) - - [android explore](https://mozilla.cloud.looker.com/explore/fenix/event_counts)$$ - - [recent android messaing click events with most extra keys and experiments](https://mozilla.cloud.looker.com/explore/fenix/event_counts?qid=u0OKWHjWgTcstNgbzvyyBc&toggle=fil) - - [recent android onboarding events](https://mozilla.cloud.looker.com/explore/fenix/event_counts?qid=n71HDr0LIxuNS3vGX9essN&toggle=fil) - - Need to understand this telemetry compared to JSON - -- -- open questions - - what does telemetry look like for onboarding? other surfaces? similar to messaging? - - Does Click telemetry on both iOS and Android alwyas mean CTA? Or something else? - - What is action_uuid extra key (see docs)? - - **Do non-experimental message send pings on iOS or Android?** - -1. Draft plan for Android page - - 1. ?File ticket - 2. Build chart for Android messaging (DONE) - 3. Build 2nd chart (LATER) - 4. Build dashboard (DONE: id = 2191) - 5. Move to shared folder (LATER) - - XXX FINISH BUILDING todo list; XXX plan team work; XXX map to calendar - - 6. Build Android page - - 1. ~~Review existing clone for "completed" (DONE)~~ - 2. ~~?Consider options for cloning, since we'll want Android completed page too, and iOS pages (DONE)~~ - 3. ~~Create new dir with new page.tsx (MUST)~~ - 4. ~~TDD Factor out dashboard (DONE)~~ - 1. Use platform search param (TRIED; TOO FIDDLY, MAYBE LATER) - 2. ~~Put in separate android/ route (DONE)~~ - 5. ~~Refactor to not display local table on Android (DONE)~~ - - 6. Factor "application=" out of env (MUST) - 1. ~~Create PlatformInfo interface~~ - 1. ~~application name~~ - 2. ~~Create PlatformInfoDict containing (android, desktop)~~ - 3. ~~pull experiments path component into EXPERIMENTER_API_PREFIX~~ - 4. Figure out how to resolve NimbusAppSlug and Platform param stuff - 5. Get application param from PlatformInfoDict; remove from env - 6. Get status param from appropriate files; remove from env - 7. Pull platform-specific-feature-list from experimentUtils into - PlatformInfo (MUST) - 8. Move nimbusRecipe.ts:getBranchInfo into own file included into PlatformInfo? (fallback: add messaging case for now; move to PlatformInfo later) - - 9. Add cases / refactor messageUtils.getDashboard (IMPT) - 10. Update / move messageUtils.getDashboardIdForTemplate (IMPT) - - 11. Make pills exclude local if not on desktop (NICE) - - . 2. Add cases / refactor looker.ts:getCTRPercentData (NICE) - - 12. Add cases / refactor templates & getSurfaceDataForTemplate (LATER) - 13. Support microsurveys badge, if sensible on mobile (LATER) - 14. Update columns.tsx:filterBySurface (LATER) - 15. Add l10n (LATER) - - 16. Factor Out NimbusMessageTable (EVEN LATER) - 17. Factor out high-level data fetching (EVEN LATER) - - 18. Pull in Android experiments using that URL - 19. Build dashboard link - 20. How to handle multi types - 21. Build CTR - 22. How to handle multi types - -2. standup 2nd page - - - TDD? clone for mobile - -3. standup 2nd dashboard - - * review mobile telemetry using glean dict - * look at explores available for those tables - * TDD? subclass recipes (desktop & mobile) - - 1. Separate dashboards per surface: onboarding, (messaging genrally - may need to split into message_surface dashboard) - 2. 2.What about QA? - -Later: - -- clean up text on messaging graph -- diff --git a/app/android/page.tsx b/app/android/page.tsx index 1fa3e62e..7caaea97 100644 --- a/app/android/page.tsx +++ b/app/android/page.tsx @@ -13,7 +13,6 @@ export default async function Page() { totalRolloutExperiments, } = await fetchData(platform); - return ( { +const ReleasedTable = async ({ + platformDisplayName, + localData, +}: ReleasedTableProps) => { return (
{ - const platformDisplayName = platformDictionary[platform].displayName; return ( @@ -81,7 +83,10 @@ export const Dashboard = async ({
{localData ? ( - + ) : null}
From 49ee43b5b7053c5b4b5268e189e0c71b04180b75 Mon Sep 17 00:00:00 2001 From: Sarah Chung Date: Tue, 15 Apr 2025 15:33:13 -0400 Subject: [PATCH 35/38] Add more comments and update isCompleted boolean --- app/fetchData.ts | 64 ++++++++++++++++++++++++----------- app/types.ts | 0 lib/nimbusRecipeCollection.ts | 6 ++-- 3 files changed, 48 insertions(+), 22 deletions(-) delete mode 100644 app/types.ts diff --git a/app/fetchData.ts b/app/fetchData.ts index 206552fc..807261d7 100644 --- a/app/fetchData.ts +++ b/app/fetchData.ts @@ -21,8 +21,15 @@ import { Platform } from "@/lib/types"; const isLookerEnabled = process.env.IS_LOOKER_ENABLED === "true"; +/** + * A function to fetch the data to render in Dashboard components in pages. + * @param platform A specified Platform (ie. fenix, ios, or firefox-desktop) + * @returns any local live message data, experiment data, total number of + * experiments, rollout data, and total number of rollouts for a given + * platform. + */ export async function fetchData(platform: Platform) { - const recipeCollection = new NimbusRecipeCollection(true, platform); //XXX YYY + const recipeCollection = new NimbusRecipeCollection(false, platform); // XXX YYY await recipeCollection.fetchRecipes(); console.log("recipeCollection.length = ", recipeCollection.recipes.length); @@ -59,6 +66,13 @@ export async function fetchData(platform: Platform) { totalRolloutExperiments, }; } + +/** + * A function to fetch a collection of Nimbus experiments. + * @param recipeCollection a collection of Nimbus recipes + * @returns recipeCollection after filtering out any rollouts, filtering out + * non accepting feature ids, and sorted based on dates. + */ export async function getMsgExpRecipeCollection( recipeCollection: NimbusRecipeCollection, ): Promise { @@ -79,6 +93,28 @@ export async function getMsgExpRecipeCollection( return msgExpRecipeCollection; } + +/** + * A function to fetch a collection of Nimbus rollouts. + * @param recipeCollection a collection of Nimbus recipes + * @returns recipeCollection after filtering out any experiments, filtering out + * non accepting feature ids, and sorted based on dates. + */ +export async function getMsgRolloutCollection( + recipeCollection: NimbusRecipeCollection, +): Promise { + const msgRolloutRecipeCollection = new NimbusRecipeCollection(); + msgRolloutRecipeCollection.recipes = recipeCollection.recipes + .filter((recipe) => recipe.usesMessagingFeatures() && !recipe.isExpRecipe()) + .sort(compareDatesFn); + console.log( + "msgRolloutRecipeCollection.length = ", + msgRolloutRecipeCollection.recipes.length, + ); + + return msgRolloutRecipeCollection; +} + /** * @returns message data in the form of FxMSMessageInfo from * lib/asrouter-local-prod-messages/data.json and also FxMS telemetry data if @@ -108,6 +144,14 @@ export async function getASRouterLocalMessageInfoFromFile(): Promise< return messages; } + +/** + * Given a message JSON, this function fetches the message data as an + * FxMSMessageInfo object and populating it with surface data, preview links, + * microsurvey tags, CTR data, and dashboard links when available. + * @param messageDef the JSON for a single message collected from local data + * @returns the information in messageDef in FxMSMessageInfo type + */ export async function getASRouterLocalColumnFromJSON( messageDef: any, ): Promise { @@ -147,8 +191,6 @@ export async function getASRouterLocalColumnFromJSON( channel, ); - // dashboard link -> dashboard id -> query id -> query -> ctr_percent_from_lastish_day - // console.log("fxmsMsgInfo: ", fxmsMsgInfo) return fxmsMsgInfo; } @@ -183,7 +225,6 @@ export async function appendFxMSTelemetryData(existingMessageData: any) { * @returns -1 if the start date for message a is after the start date for * message b, zero if they're equal, and 1 otherwise. */ - export function compareDatesFn(a: NimbusRecipe, b: NimbusRecipe): number { if (a._rawRecipe.startDate && b._rawRecipe.startDate) { if (a._rawRecipe.startDate > b._rawRecipe.startDate) { @@ -196,18 +237,3 @@ export function compareDatesFn(a: NimbusRecipe, b: NimbusRecipe): number { // a must be equal to b return 0; } - -export async function getMsgRolloutCollection( - recipeCollection: NimbusRecipeCollection, -): Promise { - const msgRolloutRecipeCollection = new NimbusRecipeCollection(); - msgRolloutRecipeCollection.recipes = recipeCollection.recipes - .filter((recipe) => recipe.usesMessagingFeatures() && !recipe.isExpRecipe()) - .sort(compareDatesFn); - console.log( - "msgRolloutRecipeCollection.length = ", - msgRolloutRecipeCollection.recipes.length, - ); - - return msgRolloutRecipeCollection; -} diff --git a/app/types.ts b/app/types.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/nimbusRecipeCollection.ts b/lib/nimbusRecipeCollection.ts index 096c013e..6d194827 100644 --- a/lib/nimbusRecipeCollection.ts +++ b/lib/nimbusRecipeCollection.ts @@ -51,7 +51,7 @@ export class NimbusRecipeCollection implements NimbusRecipeCollectionType { isCompleted: boolean; platform: Platform; - // XXX XXX remove this default platform, it's a total footgun + // XXX remove this default platform, it's a total footgun constructor( isCompleted: boolean = false, platform: Platform = "firefox-desktop", @@ -63,10 +63,10 @@ export class NimbusRecipeCollection implements NimbusRecipeCollectionType { async fetchRecipes(): Promise> { // XXX should really be using URL.parse and URLSearchParams to manage all - // this stuff + // this stuff let experimenterUrl = `${process.env.EXPERIMENTER_API_PREFIX}?status=Live&application=${this.platform}`; if (this.isCompleted) { - // XXX rename to isComplete for consistency + // XXX rename isCompleted to isComplete for consistency experimenterUrl = `${process.env.EXPERIMENTER_API_PREFIX}?status=Complete&application=${this.platform}`; } From dda789c982e026c91a0c5462c163e67f9b55faf5 Mon Sep 17 00:00:00 2001 From: Sarah Chung Date: Tue, 15 Apr 2025 15:44:37 -0400 Subject: [PATCH 36/38] Update README, CONTRIBUTING, and CHANGELOG --- .env.sample | 2 +- CHANGELOG.md | 6 ++++++ CONTRIBUTING.md | 4 ++-- README.md | 4 ++-- app/fetchData.ts | 7 +++---- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.env.sample b/.env.sample index 5a380ee4..c53bc771 100644 --- a/.env.sample +++ b/.env.sample @@ -1,5 +1,5 @@ # Copy this to .env.local to set local variables -# Preview + # Base URL for the experimenter API (defaults to the production instance) # EXPERIMENTER_API_PREFIX="https://experimenter.services.mozilla.com/api/v7/experiments/" diff --git a/CHANGELOG.md b/CHANGELOG.md index ecdda7cd..513d7c7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Tueday, April 15th, 2025 + +### Updated + +- Updated the environment variables to use an `EXPERIMENTER_API_PREFIX`. Instead of having separately defined environment variables for live and completed experiments, we are now using this prefix and adding on required parameters in the code when necessary. + ## Friday, April 11th, 2025 ### Fixed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 801e9a40..f6e6652a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,8 +9,8 @@ practices solidify. `cp .env.sample .env.local` -1. Modify variables in .env.local, e.g. uncommenting the `EXPERIMENTER_API_CALL` - line will switch from the default of live experiments to preview experiments +1. Modify variables in .env.local + 1. The `EXPERIMENTER_API_PREFIX` environment variable is the base URL for the experimenter API and it defaults to the production instance. Paramaters like `status` and `application` are added to this base URL in the code to specify live/completed status and different platforms (ie. fenix, ios. firefox-desktop). ## Running the development server diff --git a/README.md b/README.md index 23f4a60d..51439bc4 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ A bunch of the code, ideas, and links come from the OMC team's work week Hackath `cp .env.sample .env.local` -1. Modify variables in .env.local, e.g. uncommenting the `EXPERIMENTER_API_CALL` - line will switch from the default of live experiments to preview experiments +1. Modify variables in .env.local + 1. The `EXPERIMENTER_API_PREFIX` environment variable is the base URL for the experimenter API and it defaults to the production instance. Paramaters like `status` and `application` are added to this base URL in the code to specify live/completed status and different platforms (ie. fenix, ios. firefox-desktop). ## Running the development server diff --git a/app/fetchData.ts b/app/fetchData.ts index 807261d7..61c7cf1f 100644 --- a/app/fetchData.ts +++ b/app/fetchData.ts @@ -25,7 +25,7 @@ const isLookerEnabled = process.env.IS_LOOKER_ENABLED === "true"; * A function to fetch the data to render in Dashboard components in pages. * @param platform A specified Platform (ie. fenix, ios, or firefox-desktop) * @returns any local live message data, experiment data, total number of - * experiments, rollout data, and total number of rollouts for a given + * experiments, rollout data, and total number of rollouts for a given * platform. */ export async function fetchData(platform: Platform) { @@ -71,7 +71,7 @@ export async function fetchData(platform: Platform) { * A function to fetch a collection of Nimbus experiments. * @param recipeCollection a collection of Nimbus recipes * @returns recipeCollection after filtering out any rollouts, filtering out - * non accepting feature ids, and sorted based on dates. + * non accepting feature ids, and sorted based on dates. */ export async function getMsgExpRecipeCollection( recipeCollection: NimbusRecipeCollection, @@ -98,7 +98,7 @@ export async function getMsgExpRecipeCollection( * A function to fetch a collection of Nimbus rollouts. * @param recipeCollection a collection of Nimbus recipes * @returns recipeCollection after filtering out any experiments, filtering out - * non accepting feature ids, and sorted based on dates. + * non accepting feature ids, and sorted based on dates. */ export async function getMsgRolloutCollection( recipeCollection: NimbusRecipeCollection, @@ -120,7 +120,6 @@ export async function getMsgRolloutCollection( * lib/asrouter-local-prod-messages/data.json and also FxMS telemetry data if * Looker credentials are enabled. */ - export async function getASRouterLocalMessageInfoFromFile(): Promise< FxMSMessageInfo[] > { From 4774e72bb12b5ffbfeb29c77acfdb1931757a557 Mon Sep 17 00:00:00 2001 From: Sarah Chung Date: Tue, 15 Apr 2025 16:17:07 -0400 Subject: [PATCH 37/38] Make fetchData for mobile default to live --- app/fetchData.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/fetchData.ts b/app/fetchData.ts index 61c7cf1f..717358de 100644 --- a/app/fetchData.ts +++ b/app/fetchData.ts @@ -29,7 +29,10 @@ const isLookerEnabled = process.env.IS_LOOKER_ENABLED === "true"; * platform. */ export async function fetchData(platform: Platform) { - const recipeCollection = new NimbusRecipeCollection(false, platform); // XXX YYY + // XXX at some point, once the completed experiments get ported to use + // the new infra including this, we're going to need to do + // something better than just pass "false" as the first param here. + const recipeCollection = new NimbusRecipeCollection(false, platform); await recipeCollection.fetchRecipes(); console.log("recipeCollection.length = ", recipeCollection.recipes.length); From a460216a7f1db7352fbe57f90f7a2ca433656b09 Mon Sep 17 00:00:00 2001 From: Sarah Chung Date: Tue, 15 Apr 2025 16:31:38 -0400 Subject: [PATCH 38/38] Update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 513d7c7b..0019b44c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Updated -- Updated the environment variables to use an `EXPERIMENTER_API_PREFIX`. Instead of having separately defined environment variables for live and completed experiments, we are now using this prefix and adding on required parameters in the code when necessary. +- Updated the environment variables to use an `EXPERIMENTER_API_PREFIX`. Instead of having separate environment variables for live and completed experiments, we are now using this prefix and adding on required parameters in the code when necessary. ## Friday, April 11th, 2025