summaryrefslogtreecommitdiffstats
path: root/toolkit/components/nimbus/lib/ExperimentManager.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/nimbus/lib/ExperimentManager.sys.mjs')
-rw-r--r--toolkit/components/nimbus/lib/ExperimentManager.sys.mjs76
1 files changed, 74 insertions, 2 deletions
diff --git a/toolkit/components/nimbus/lib/ExperimentManager.sys.mjs b/toolkit/components/nimbus/lib/ExperimentManager.sys.mjs
index 8e1acc4803..d0f313a2ae 100644
--- a/toolkit/components/nimbus/lib/ExperimentManager.sys.mjs
+++ b/toolkit/components/nimbus/lib/ExperimentManager.sys.mjs
@@ -32,6 +32,25 @@ const STUDIES_OPT_OUT_PREF = "app.shield.optoutstudies.enabled";
const STUDIES_ENABLED_CHANGED = "nimbus:studies-enabled-changed";
+const ENROLLMENT_STATUS = {
+ ENROLLED: "Enrolled",
+ NOT_ENROLLED: "NotEnrolled",
+ DISQUALIFIED: "Disqualified",
+ WAS_ENROLLED: "WasEnrolled",
+ ERROR: "Error",
+};
+
+const ENROLLMENT_STATUS_REASONS = {
+ QUALIFIED: "Qualified",
+ OPT_IN: "OptIn",
+ OPT_OUT: "OptOut",
+ NOT_SELECTED: "NotSelected",
+ NOT_TARGETED: "NotTargeted",
+ ENROLLMENTS_PAUSED: "EnrollmentsPaused",
+ FEATURE_CONFLICT: "FeatureConflict",
+ ERROR: "Error",
+};
+
function featuresCompat(branch) {
if (!branch || (!branch.feature && !branch.features)) {
return [];
@@ -182,6 +201,14 @@ export class _ExperimentManager {
}
this.observe();
+
+ lazy.NimbusFeatures.nimbusTelemetry.onUpdate(() => {
+ const cfg =
+ lazy.NimbusFeatures.nimbusTelemetry.getVariable(
+ "gleanMetricConfiguration"
+ ) ?? {};
+ Services.fog.setMetricsFeatureConfig(JSON.stringify(cfg));
+ });
}
/**
@@ -223,16 +250,22 @@ export class _ExperimentManager {
missingL10nIds
) {
for (const enrollment of enrollments) {
- const { slug, source } = enrollment;
+ const { slug, source, branch } = enrollment;
if (sourceToCheck !== source) {
continue;
}
+ const statusTelemetry = {
+ slug,
+ branch: branch.slug,
+ };
if (!this.sessions.get(source)?.has(slug)) {
lazy.log.debug(`Stopping study for recipe ${slug}`);
try {
let reason;
if (recipeMismatches.includes(slug)) {
reason = "targeting-mismatch";
+ statusTelemetry.status = ENROLLMENT_STATUS.DISQUALIFIED;
+ statusTelemetry.reason = ENROLLMENT_STATUS_REASONS.NOT_TARGETED;
} else if (invalidRecipes.includes(slug)) {
reason = "invalid-recipe";
} else if (invalidBranches.has(slug) || invalidFeatures.has(slug)) {
@@ -243,12 +276,23 @@ export class _ExperimentManager {
reason = "l10n-missing-entry";
} else {
reason = "recipe-not-seen";
+ statusTelemetry.status = ENROLLMENT_STATUS.WAS_ENROLLED;
+ statusTelemetry.branch = branch.slug;
+ }
+ if (!statusTelemetry.status) {
+ statusTelemetry.status = ENROLLMENT_STATUS.DISQUALIFIED;
+ statusTelemetry.reason = ENROLLMENT_STATUS_REASONS.ERROR;
+ statusTelemetry.error_string = reason;
}
this.unenroll(slug, reason);
} catch (err) {
console.error(err);
}
+ } else {
+ statusTelemetry.status = ENROLLMENT_STATUS.ENROLLED;
+ statusTelemetry.reason = ENROLLMENT_STATUS_REASONS.QUALIFIED;
}
+ this.sendEnrollmentStatusTelemetry(statusTelemetry);
}
}
@@ -685,7 +729,7 @@ export class _ExperimentManager {
/**
* Unenroll from all active studies if user opts out.
*/
- observe(aSubject, aTopic, aPrefName) {
+ observe() {
if (!this.studiesEnabled) {
for (const { slug } of this.store.getAllActiveExperiments()) {
this.unenroll(slug, "studies-opt-out");
@@ -756,6 +800,34 @@ export class _ExperimentManager {
}
/**
+ *
+ * @param {object} enrollmentStatus
+ * @param {string} enrollmentStatus.slug
+ * @param {string} enrollmentStatus.status
+ * @param {string?} enrollmentStatus.reason
+ * @param {string?} enrollmentStatus.branch
+ * @param {string?} enrollmentStatus.error_string
+ * @param {string?} enrollmentStatus.conflict_slug
+ */
+ sendEnrollmentStatusTelemetry({
+ slug,
+ status,
+ reason,
+ branch,
+ error_string,
+ conflict_slug,
+ }) {
+ Glean.nimbusEvents.enrollmentStatus.record({
+ slug,
+ status,
+ reason,
+ branch,
+ error_string,
+ conflict_slug,
+ });
+ }
+
+ /**
* Sets Telemetry when activating an experiment.
*
* @param {Enrollment} experiment