From 9d046208886441061358f517dc555382064e0691 Mon Sep 17 00:00:00 2001
From: Barret Rennie <barret@brennie.ca>
Date: Wed, 26 Oct 2022 19:00:03 +0000
Subject: [PATCH] Bug 1797202 - Ingest messages from Nimbus rollouts in
 ASRouter r=dmose

Differential Revision: https://phabricator.services.mozilla.com/D160263
---
 browser/components/newtab/lib/ASRouter.jsm    | 60 +++++++++++--------
 .../browser_asrouter_experimentsAPILoader.js  | 22 +++++++
 2 files changed, 56 insertions(+), 26 deletions(-)

diff --git a/browser/components/newtab/lib/ASRouter.jsm b/browser/components/newtab/lib/ASRouter.jsm
index 52e98962e9e9f..2629cac773ac6 100644
--- a/browser/components/newtab/lib/ASRouter.jsm
+++ b/browser/components/newtab/lib/ASRouter.jsm
@@ -335,17 +335,16 @@ const MessageLoaderUtils = {
   },
 
   /**
-   * Return messages from active Nimbus experiments.
+   * Return messages from active Nimbus experiments and rollouts.
    *
    * @param {object} provider A messaging experiments provider.
    * @param {string[]?} provider.featureIds
    *                    An optional array of Nimbus feature IDs to check for
-   *                    messaging experiments. If not provided, we will fall
-   *                    back to the set of default features. Otherwise, if
-   *                    provided and empty, we will not ingest messages from any
-   *                    features.
+   *                    enrollments. If not provided, we will fall back to the
+   *                    set of default features. Otherwise, if provided and
+   *                    empty, we will not ingest messages from any features.
    *
-   * @return {object[]} The list of messages from active experiments, as well as
+   * @return {object[]} The list of messages from active enrollments, as well as
    *                    the messages defined in unenrolled branches so that they
    *                    reach events can be recorded (if we record reach events
    *                    for that feature).
@@ -361,8 +360,13 @@ const MessageLoaderUtils = {
       let experimentData = lazy.ExperimentAPI.getExperimentMetaData({
         featureId,
       });
-      // Not enrolled in any experiment for this feature, we can skip
-      if (!experimentData) {
+
+      // We are not enrolled in any experiment or rollout for this feature, so
+      // we can skip the feature.
+      if (
+        !experimentData &&
+        !lazy.ExperimentAPI.getRolloutMetaData({ featureId })
+      ) {
         continue;
       }
 
@@ -379,24 +383,28 @@ const MessageLoaderUtils = {
       if (!REACH_EVENT_GROUPS.includes(featureId)) {
         continue;
       }
-      // Check other sibling branches for triggers, add them to the return
-      // array if found any. The `forReachEvent` label is used to identify
-      // those branches so that they would only used to record the Reach
-      // event.
-      const branches =
-        (await lazy.ExperimentAPI.getAllBranches(experimentData.slug)) || [];
-      for (const branch of branches) {
-        let branchValue = branch[featureId].value;
-        if (
-          branch.slug !== experimentData.branch.slug &&
-          branchValue?.trigger
-        ) {
-          experiments.push({
-            forReachEvent: { sent: false, group: featureId },
-            experimentSlug: experimentData.slug,
-            branchSlug: branch.slug,
-            ...branchValue,
-          });
+
+      // If we are in a rollout, we do not have sibling branches.
+      if (experimentData) {
+        // Check other sibling branches for triggers, add them to the return
+        // array if found any. The `forReachEvent` label is used to identify
+        // those branches so that they would only used to record the Reach
+        // event.
+        const branches =
+          (await lazy.ExperimentAPI.getAllBranches(experimentData.slug)) || [];
+        for (const branch of branches) {
+          let branchValue = branch[featureId].value;
+          if (
+            branch.slug !== experimentData.branch.slug &&
+            branchValue?.trigger
+          ) {
+            experiments.push({
+              forReachEvent: { sent: false, group: featureId },
+              experimentSlug: experimentData.slug,
+              branchSlug: branch.slug,
+              ...branchValue,
+            });
+          }
         }
       }
     }
diff --git a/browser/components/newtab/test/browser/browser_asrouter_experimentsAPILoader.js b/browser/components/newtab/test/browser/browser_asrouter_experimentsAPILoader.js
index 27528bf6d75d4..67717245b1679 100644
--- a/browser/components/newtab/test/browser/browser_asrouter_experimentsAPILoader.js
+++ b/browser/components/newtab/test/browser/browser_asrouter_experimentsAPILoader.js
@@ -251,6 +251,28 @@ add_task(async function test_loading_experimentsAPI_legacy() {
   await cleanup();
 });
 
+add_task(async function test_loading_experimentsAPI_rollout() {
+  const rollout = await getCFRExperiment();
+  rollout.isRollout = true;
+  rollout.branches.pop();
+
+  await setup(rollout);
+  await RemoteSettingsExperimentLoader.updateRecipes();
+  await BrowserTestUtils.waitForCondition(() =>
+    ExperimentAPI.getRolloutMetaData({ featureId: "cfr" })
+  );
+
+  await ASRouter._updateMessageProviders();
+  await ASRouter.loadMessagesFromAllProviders();
+
+  Assert.ok(
+    ASRouter.state.messages.find(m => m.id === "xman_test_message"),
+    "Found rollout message in ASRouter state"
+  );
+
+  await cleanup();
+});
+
 add_task(async function test_exposure_ping() {
   // Reset this check to allow sending multiple exposure pings in tests
   NimbusFeatures.cfr._didSendExposureEvent = false;
-- 
GitLab