97 lines
3.3 KiB
JavaScript
97 lines
3.3 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/. */
|
|
|
|
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",
|
|
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,
|
|
"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);
|
|
}
|
|
}
|
|
}
|