summaryrefslogtreecommitdiffstats
path: root/toolkit/components/normandy/lib/ActionsManager.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/normandy/lib/ActionsManager.sys.mjs')
-rw-r--r--toolkit/components/normandy/lib/ActionsManager.sys.mjs100
1 files changed, 100 insertions, 0 deletions
diff --git a/toolkit/components/normandy/lib/ActionsManager.sys.mjs b/toolkit/components/normandy/lib/ActionsManager.sys.mjs
new file mode 100644
index 0000000000..6f811613af
--- /dev/null
+++ b/toolkit/components/normandy/lib/ActionsManager.sys.mjs
@@ -0,0 +1,100 @@
+/* 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/. */
+
+import { LogManager } from "resource://normandy/lib/LogManager.sys.mjs";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ AddonRollbackAction:
+ "resource://normandy/actions/AddonRollbackAction.sys.mjs",
+ AddonRolloutAction: "resource://normandy/actions/AddonRolloutAction.sys.mjs",
+ BaseAction: "resource://normandy/actions/BaseAction.sys.mjs",
+ BranchedAddonStudyAction:
+ "resource://normandy/actions/BranchedAddonStudyAction.sys.mjs",
+ ConsoleLogAction: "resource://normandy/actions/ConsoleLogAction.sys.mjs",
+ MessagingExperimentAction:
+ "resource://normandy/actions/MessagingExperimentAction.sys.mjs",
+ PreferenceExperimentAction:
+ "resource://normandy/actions/PreferenceExperimentAction.sys.mjs",
+ PreferenceRollbackAction:
+ "resource://normandy/actions/PreferenceRollbackAction.sys.mjs",
+ PreferenceRolloutAction:
+ "resource://normandy/actions/PreferenceRolloutAction.sys.mjs",
+ ShowHeartbeatAction:
+ "resource://normandy/actions/ShowHeartbeatAction.sys.mjs",
+ Uptake: "resource://normandy/lib/Uptake.sys.mjs",
+});
+
+const log = LogManager.getLogger("recipe-runner");
+
+/**
+ * A class to manage the actions that recipes can use in Normandy.
+ */
+export class ActionsManager {
+ constructor() {
+ this.finalized = false;
+
+ this.localActions = {};
+ for (const [name, Constructor] of Object.entries(
+ ActionsManager.actionConstructors
+ )) {
+ this.localActions[name] = new Constructor();
+ }
+ }
+
+ static actionConstructors = {
+ "addon-rollback": lazy.AddonRollbackAction,
+ "addon-rollout": lazy.AddonRolloutAction,
+ "branched-addon-study": lazy.BranchedAddonStudyAction,
+ "console-log": lazy.ConsoleLogAction,
+ "messaging-experiment": lazy.MessagingExperimentAction,
+ "multi-preference-experiment": lazy.PreferenceExperimentAction,
+ "preference-rollback": lazy.PreferenceRollbackAction,
+ "preference-rollout": lazy.PreferenceRolloutAction,
+ "show-heartbeat": lazy.ShowHeartbeatAction,
+ };
+
+ static getCapabilities() {
+ // Prefix each action name with "action." to turn it into a capability name.
+ let capabilities = new Set();
+ for (const actionName of Object.keys(ActionsManager.actionConstructors)) {
+ capabilities.add(`action.${actionName}`);
+ }
+ return capabilities;
+ }
+
+ async processRecipe(recipe, suitability) {
+ let actionName = recipe.action;
+
+ if (actionName in this.localActions) {
+ log.info(`Executing recipe "${recipe.name}" (action=${recipe.action})`);
+ const action = this.localActions[actionName];
+ await action.processRecipe(recipe, suitability);
+
+ // If the recipe doesn't have matching capabilities, then a missing action
+ // is expected. In this case, don't send an error
+ } else if (
+ suitability !== lazy.BaseAction.suitability.CAPABILITIES_MISMATCH
+ ) {
+ log.error(
+ `Could not execute recipe ${recipe.name}:`,
+ `Action ${recipe.action} is either missing or invalid.`
+ );
+ await lazy.Uptake.reportRecipe(recipe, lazy.Uptake.RECIPE_INVALID_ACTION);
+ }
+ }
+
+ async finalize(options) {
+ if (this.finalized) {
+ throw new Error("ActionsManager has already been finalized");
+ }
+ this.finalized = true;
+
+ // Finalize local actions
+ for (const action of Object.values(this.localActions)) {
+ action.finalize(options);
+ }
+ }
+}