328 lines
9.1 KiB
JavaScript
328 lines
9.1 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.sys.mjs",
|
|
});
|
|
|
|
const EXPERIMENT_ACTIVE_PREFIX = "nimbus-";
|
|
|
|
const EnrollmentStatus = Object.freeze({
|
|
ENROLLED: "Enrolled",
|
|
NOT_ENROLLED: "NotEnrolled",
|
|
DISQUALIFIED: "Disqualified",
|
|
WAS_ENROLLED: "WasEnrolled",
|
|
ERROR: "Error",
|
|
});
|
|
|
|
const EnrollmentStatusReason = Object.freeze({
|
|
CHANGED_PREF: "ChangedPref",
|
|
QUALIFIED: "Qualified",
|
|
OPT_IN: "OptIn",
|
|
OPT_OUT: "OptOut",
|
|
NOT_SELECTED: "NotSelected",
|
|
NOT_TARGETED: "NotTargeted",
|
|
ENROLLMENTS_PAUSED: "EnrollmentsPaused",
|
|
FEATURE_CONFLICT: "FeatureConflict",
|
|
FORCE_ENROLLMENT: "ForceEnrollment",
|
|
NAME_CONFLICT: "NameConflict",
|
|
PREF_FLIPS_CONFLICT: "PrefFlipsConflict",
|
|
ERROR: "Error",
|
|
});
|
|
|
|
const EnrollmentFailureReason = Object.freeze({
|
|
FEATURE_CONFLICT: "feature-conflict",
|
|
NAME_CONFLICT: "name-conflict",
|
|
});
|
|
|
|
const EnrollmentSource = Object.freeze({
|
|
RS_LOADER: "rs-loader",
|
|
FORCE_ENROLLMENT: "force-enrollment",
|
|
});
|
|
|
|
const UnenrollmentFailureReason = Object.freeze({
|
|
ALREADY_UNENROLLED: "already-unenrolled",
|
|
DOES_NOT_EXIST: "does-not-exist",
|
|
});
|
|
|
|
const ValidationFailureReason = Object.freeze({
|
|
INVALID_BRANCH: "invalid-branch",
|
|
INVALID_FEATURE: "invalid-feature",
|
|
INVALID_RECIPE: "invalid-recipe",
|
|
L10N_MISSING_ENTRY: "l10n-missing-entry",
|
|
L10N_MISSING_LOCALE: "l10n-missing-locale",
|
|
UNSUPPORTED_FEATURES: "unsupported-feature",
|
|
});
|
|
|
|
const UnenrollReason = Object.freeze({
|
|
BRANCH_REMOVED: "branch-removed",
|
|
BUCKETING: "bucketing",
|
|
CHANGED_PREF: "changed-pref",
|
|
FORCE_ENROLLMENT: "force-enrollment",
|
|
INDIVIDUAL_OPT_OUT: "individual-opt-out",
|
|
LABS_OPT_OUT: "labs-opt-out",
|
|
PREF_FLIPS_CONFLICT: "prefFlips-conflict",
|
|
PREF_FLIPS_FAILED: "prefFlips-failed",
|
|
PREF_VARIABLE_CHANGED: "pref-variable-changed",
|
|
PREF_VARIABLE_MISSING: "pref-variable-missing",
|
|
PREF_VARIABLE_NO_LONGER: "pref-variable-no-longer",
|
|
RECIPE_NOT_SEEN: "recipe-not-seen",
|
|
STUDIES_OPT_OUT: "studies-opt-out",
|
|
TARGETING_MISMATCH: "targeting-mismatch",
|
|
UNKNOWN: "unknown",
|
|
|
|
// Validation failure can cause unenrollment.
|
|
...ValidationFailureReason,
|
|
});
|
|
|
|
const EXPERIMENT_TYPE_ROLLOUT = "rollout";
|
|
const EXPERIMENT_TYPE_NIMBUS = "nimbus";
|
|
|
|
export const NimbusTelemetry = {
|
|
EnrollmentFailureReason,
|
|
EnrollmentSource,
|
|
EnrollmentStatus,
|
|
EnrollmentStatusReason,
|
|
UnenrollReason,
|
|
UnenrollmentFailureReason,
|
|
ValidationFailureReason,
|
|
|
|
recordEnrollment(enrollment) {
|
|
this.setExperimentActive(enrollment);
|
|
|
|
const experimentType = enrollment.isRollout
|
|
? EXPERIMENT_TYPE_ROLLOUT
|
|
: EXPERIMENT_TYPE_NIMBUS;
|
|
|
|
Glean.normandy.enrollNimbusExperiment.record({
|
|
value: enrollment.slug,
|
|
branch: enrollment.branch.slug,
|
|
experimentType,
|
|
});
|
|
Glean.nimbusEvents.enrollment.record({
|
|
experiment: enrollment.slug,
|
|
branch: enrollment.branch.slug,
|
|
experiment_type: experimentType,
|
|
});
|
|
|
|
this.recordEnrollmentStatus({
|
|
slug: enrollment.slug,
|
|
branch: enrollment.branch.slug,
|
|
status: EnrollmentStatus.ENROLLED,
|
|
reason:
|
|
enrollment.isFirefoxLabsOptIn ||
|
|
enrollment.source === EnrollmentSource.FORCE_ENROLLMENT
|
|
? EnrollmentStatusReason.OPT_IN
|
|
: EnrollmentStatusReason.QUALIFIED,
|
|
});
|
|
},
|
|
|
|
recordEnrollmentFailure(slug, reason) {
|
|
Glean.normandy.enrollFailedNimbusExperiment.record({
|
|
value: slug,
|
|
reason,
|
|
});
|
|
Glean.nimbusEvents.enrollFailed.record({
|
|
experiment: slug,
|
|
reason,
|
|
});
|
|
},
|
|
|
|
recordEnrollmentStatus({
|
|
slug,
|
|
status,
|
|
reason,
|
|
branch,
|
|
error_string,
|
|
conflict_slug,
|
|
}) {
|
|
Glean.nimbusEvents.enrollmentStatus.record({
|
|
slug,
|
|
status,
|
|
reason,
|
|
branch,
|
|
error_string,
|
|
conflict_slug,
|
|
});
|
|
},
|
|
|
|
recordExposure(slug, branchSlug, featureId) {
|
|
Glean.normandy.exposeNimbusExperiment.record({
|
|
value: slug,
|
|
branchSlug,
|
|
featureId,
|
|
});
|
|
Glean.nimbusEvents.exposure.record({
|
|
experiment: slug,
|
|
branch: branchSlug,
|
|
feature_id: featureId,
|
|
});
|
|
},
|
|
|
|
recordMigration(migration, error) {
|
|
Glean.nimbusEvents.migration.record(
|
|
Object.assign(
|
|
{
|
|
migration_id: migration,
|
|
success: typeof error === "undefined",
|
|
},
|
|
typeof error !== "undefined" ? { error_reason: error } : {}
|
|
)
|
|
);
|
|
},
|
|
|
|
recordUnenrollment(enrollment, cause) {
|
|
lazy.TelemetryEnvironment.setExperimentInactive(enrollment.slug);
|
|
Services.fog.setExperimentInactive(enrollment.slug);
|
|
|
|
const legacyEvent = {
|
|
value: enrollment.slug,
|
|
branch: enrollment.branch.slug,
|
|
reason: cause.reason,
|
|
};
|
|
const gleanEvent = {
|
|
experiment: enrollment.slug,
|
|
branch: enrollment.branch.slug,
|
|
reason: cause.reason,
|
|
};
|
|
|
|
switch (cause.reason) {
|
|
case UnenrollReason.CHANGED_PREF:
|
|
legacyEvent.changedPref = cause.changedPref.name;
|
|
gleanEvent.changed_pref = cause.changedPref.name;
|
|
break;
|
|
|
|
case UnenrollReason.PREF_FLIPS_CONFLICT:
|
|
legacyEvent.conflictingSlug = cause.conflictingSlug;
|
|
gleanEvent.conflicting_slug = cause.conflictingSlug;
|
|
break;
|
|
|
|
case UnenrollReason.PREF_FLIPS_FAILED:
|
|
legacyEvent.prefType = cause.prefType;
|
|
gleanEvent.pref_type = cause.prefType;
|
|
|
|
legacyEvent.prefName = cause.prefName;
|
|
gleanEvent.pref_name = cause.prefName;
|
|
break;
|
|
}
|
|
|
|
Glean.normandy.unenrollNimbusExperiment.record(legacyEvent);
|
|
Glean.nimbusEvents.unenrollment.record(gleanEvent);
|
|
|
|
const enrollmentStatus = {
|
|
slug: enrollment.slug,
|
|
branch: enrollment.branch.slug,
|
|
};
|
|
|
|
switch (cause.reason) {
|
|
case UnenrollReason.BUCKETING:
|
|
enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED;
|
|
enrollmentStatus.reason = EnrollmentStatusReason.NOT_SELECTED;
|
|
break;
|
|
|
|
case UnenrollReason.RECIPE_NOT_SEEN:
|
|
enrollmentStatus.status = EnrollmentStatus.WAS_ENROLLED;
|
|
break;
|
|
|
|
case UnenrollReason.TARGETING_MISMATCH:
|
|
enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED;
|
|
enrollmentStatus.reason = EnrollmentStatusReason.NOT_TARGETED;
|
|
break;
|
|
|
|
case UnenrollReason.INDIVIDUAL_OPT_OUT:
|
|
case UnenrollReason.LABS_OPT_OUT:
|
|
case UnenrollReason.STUDIES_OPT_OUT:
|
|
enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED;
|
|
enrollmentStatus.reason = EnrollmentStatusReason.OPT_OUT;
|
|
break;
|
|
|
|
case UnenrollReason.CHANGED_PREF:
|
|
enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED;
|
|
enrollmentStatus.reason = EnrollmentStatusReason.CHANGED_PREF;
|
|
break;
|
|
|
|
case UnenrollReason.FORCE_ENROLLMENT:
|
|
enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED;
|
|
enrollmentStatus.reason = EnrollmentStatusReason.FORCE_ENROLLMENT;
|
|
break;
|
|
|
|
case UnenrollReason.PREF_FLIPS_CONFLICT:
|
|
enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED;
|
|
enrollmentStatus.reason = EnrollmentStatusReason.PREF_FLIPS_CONFLICT;
|
|
enrollmentStatus.conflict_slug = cause.conflictingSlug;
|
|
break;
|
|
|
|
default:
|
|
enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED;
|
|
enrollmentStatus.reason = EnrollmentStatusReason.ERROR;
|
|
enrollmentStatus.error_string = cause.reason;
|
|
}
|
|
|
|
this.recordEnrollmentStatus(enrollmentStatus);
|
|
},
|
|
|
|
recordUnenrollmentFailure(slug, reason) {
|
|
Glean.normandy.unenrollFailedNimbusExperiment.record({
|
|
value: slug,
|
|
reason,
|
|
});
|
|
Glean.nimbusEvents.unenrollFailed.record({
|
|
experiment: slug,
|
|
reason,
|
|
});
|
|
},
|
|
|
|
setExperimentActive(enrollment) {
|
|
const experimentType = enrollment.isRollout
|
|
? EXPERIMENT_TYPE_ROLLOUT
|
|
: EXPERIMENT_TYPE_NIMBUS;
|
|
const type = `${EXPERIMENT_ACTIVE_PREFIX}${experimentType}`;
|
|
lazy.TelemetryEnvironment.setExperimentActive(
|
|
enrollment.slug,
|
|
enrollment.branch.slug,
|
|
{ type }
|
|
);
|
|
|
|
Services.fog.setExperimentActive(enrollment.slug, enrollment.branch.slug, {
|
|
type,
|
|
});
|
|
},
|
|
|
|
recordValidationFailure(
|
|
slug,
|
|
reason,
|
|
{ branch, locale, l10nIds: l10n_ids } = {}
|
|
) {
|
|
// Do not record invalid feature telemetry.
|
|
if (reason === ValidationFailureReason.INVALID_FEATURE) {
|
|
return;
|
|
}
|
|
|
|
// Do not record unsupported feature telemetry.
|
|
if (reason === ValidationFailureReason.UNSUPPORTED_FEATURES) {
|
|
return;
|
|
}
|
|
|
|
const extra = Object.assign(
|
|
{ reason },
|
|
reason === ValidationFailureReason.INVALID_BRANCH ? { branch } : {},
|
|
reason === ValidationFailureReason.L10N_MISSING_ENTRY
|
|
? { l10n_ids, locale }
|
|
: {},
|
|
reason === ValidationFailureReason.L10N_MISSING_LOCALE ? { locale } : {}
|
|
);
|
|
|
|
Glean.normandy.validationFailedNimbusExperiment.record({
|
|
value: slug,
|
|
...extra,
|
|
});
|
|
|
|
Glean.nimbusEvents.validationFailed.record({
|
|
experiment: slug,
|
|
...extra,
|
|
});
|
|
},
|
|
};
|