gpii-universal
Version:
Cross platform, core components of the GPII personalization infrastructure.
391 lines (371 loc) • 16 kB
JavaScript
/*
* GPII Flow Manager
*
* Copyright 2012 OCAD University
* Copyright 2017-2019 OCAD University
*
* Licensed under the New BSD license. You may not use this file except in
* compliance with this License.
*
* The research leading to these results has received funding from the European Union's
* Seventh Framework Programme (FP7/2007-2013)
* under grant agreement no. 289016.
*
* You may obtain a copy of the License at
* https://github.com/GPII/universal/blob/master/LICENSE.txt
*/
"use strict";
var fluid = require("infusion"),
gpii = fluid.registerNamespace("gpii");
require("kettle");
require("./UserLogonHandlers.js");
require("./BrowserChannel.js");
require("./DefaultSettingsLoader.js");
require("./PSPChannel.js");
require("./SettingsDataSource.js");
require("./UntrustedFlowManager.js");
require("./Capture.js");
require("preferencesServer");
require("lifecycleManager");
require("deviceReporter");
require("transformer");
require("journal");
require("singleInstance");
require("eventLog");
require("gpii-user-errors");
fluid.defaults("gpii.flowManager", {
gradeNames: ["kettle.app"],
components: {
// TODO: Make this use the solutions registry data source by default?
solutionsRegistryDataSource: {
type: "kettle.dataSource",
options: {
termMap: {
"os": "%os",
"version": "%version"
},
components: {
encoding: {
type: "kettle.dataSource.encoding.none"
}
}
}
},
matchMakerFramework: {
type: "gpii.matchMakerFramework"
},
browserChannel: {
type: "gpii.flowManager.browserChannel"
},
// persistent validation component for settings.
prefsValidator: {
type: "gpii.universal.solutionsRegistry.validators.preferences"
}
}
});
// Mixin grades for the FlowManager
fluid.defaults("gpii.flowManager.local", {
gradeNames: ["fluid.contextAware"],
// The "resetAtStart" flag is to indicate whether to reset the system to the default settings on system startup.
// It is turned on in production configs.
resetAtStart: false,
// The "suppressHttpEndpoints" flag is to indicate whether to hide un-needed localhost http endpoints.
// It is turned on in production configs.
suppressHttpEndpoints: false,
members: {
/* The resolved value is an object containing data for performing reset actions:
{
defaultSettings: {Object}, // The content of the reset to default file
defaultLifecycleInstructions: {Object}, // For the reset action at key-in
defaultSnapshot: {Object} // For reset actions at system startup and explicit reset request
}
"defaultSnapshot" and "defaultLifecycleInstructions" structures are generated for performing
"reset to system default settings" functionality that is applied when:
1. reset to default settings on system startup using lifecycleManager.restoreSnapshot();
2. at receiving request "/user/reset/login" to logout the current key and reset to default settings.
This reset also uses lifecycleManager.restoreSnapshot();
3. at any keyin. The settings for the keyin will be combined with default settings, then apply using
lifecycleManager.start(lifecycleInstructions).
"defaultSnapshot" is used at case 1 & 2 while "defaultLifecycleInstructions" is used at case 3.
The difference between "defaultLifecycleInstructions" and "defaultSnapshot" is the "settings" block for each
solution:
* "defaultSnapshot": the snapshot structure expected by lifecycleManager.restoreSnapshot():
{
"settings": {
"masterVolume": {
"type": "ADD",
"value": 75
}
}
}
* "defaultLifecycleInstructions": the lifecycle instruction structure expected by lifecycleManager.start():
{
"settings": {
"masterVolume": 75
}
}
*/
defaultSettingsDataPromise: "@expand:fluid.promise()"
},
contextAwareness: {
suppressHttpEndpoints: {
checks: {
addHttpEndpointsOption: {
contextValue: "{gpii.flowManager.local}.options.suppressHttpEndpoints",
gradeNames: null
}
},
defaultGradeNames: "gpii.flowManager.local.requestHandlers"
}
},
distributeOptions: {
// Distribute the grade to handle user logon requests.
// In all-in-local config, the handler is "gpii.lifecycleManager.userLogonHandling.matchMakingStateChangeHandler".
// In untrusted config, the handler should be "gpii.lifecycleManager.untrusted.stateChangeHandler".
"lifecycleManager.loginRequestHandler": {
record: "gpii.lifecycleManager.userLogonHandling.matchMakingStateChangeHandler",
target: "{that gpii.lifecycleManager.loginRequest}.options.gradeNames"
},
"userLogonHandling.defaultSettingsDataPromise": {
record: "{gpii.flowManager.local}.defaultSettingsDataPromise",
target: "{that gpii.lifecycleManager.userLogonHandling.stateChangeHandler}.options.members.defaultSettingsDataPromise"
},
"lifecycleManager.privateMatchMaker": {
record: {
privateMatchMaker: "{gpii.flowManager.local}.privateMatchMaker"
},
target: "{that lifecycleManager}.options.components"
},
"userListeners.lifecycleManager": {
record: "{gpii.flowManager.local}.lifecycleManager",
target: "{that gpii.userListener}.options.components.lifecycleManager"
},
"lifecycleManager.pspChannel.sessionBinder": {
record: "gpii.pspChannel.sessionBinder",
target: "{that lifecycleManager gpii.lifecycleManager.userSession}.options.gradeNames"
}
},
components: {
lifecycleManager: {
type: "gpii.lifecycleManager",
options: {
components: {
userErrors: "{gpii.flowManager.local}.userErrors"
}
}
},
privateMatchMaker: {
type: "gpii.lifecycleManager.privateMatchMaker"
},
deviceReporter: {
type: "gpii.deviceReporter"
},
journal: {
type: "gpii.journal",
options: {
gradeNames: ["gpii.journalLifecycleManager", "gpii.journalApp"]
}
},
eventLog: {
type: "gpii.eventLog",
options: {
gradeNames: ["gpii.metrics", "gpii.metrics.lifecycle"]
}
},
capture: {
type: "gpii.flowManager.capture"
},
userListeners: {
type: "gpii.userListeners"
},
webSocketsSettingsHandlerComponent: {
type: "gpii.settingsHandlers.webSockets.component"
},
pspChannel: {
type: "gpii.pspChannel"
},
defaultSettingsLoader: {
type: "gpii.defaultSettingsLoader"
},
userErrors: {
type: "gpii.userErrors"
}
},
requestHandlers: {
browserChannel: {
route: "/browserChannel",
type: "gpii.flowManager.browserChannel.handler"
},
pspChannel: {
route: "/pspChannel",
type: "gpii.pspChannel.handler"
}
},
invokers: {
savePreferences: {
funcName: "gpii.flowManager.local.savePreferences",
args: ["{that}", "{arguments}.0", "{arguments}.1"]
}
},
events: {
preferencesSavedSuccess: null,
preferencesSavedError: null,
noUserLoggedIn: null,
getDefaultSettingsData: null,
defaultSettingsDataLoaded: null,
flowManagerReady: {
events: {
"defaultSettingsDataLoaded": "defaultSettingsDataLoaded",
"kettleReady": "{kettle.server}.events.onListen"
}
},
// Fired only when:
// 1. The flag for resetting to default settings on system start, that.options.resetAtStart, is turned on;
// 2. The default settings are defined. See documentation/ResetComputer.md.
resetAtStartSuccess: null,
resetAtStartError: null
},
listeners: {
"onCreate.registerInstance": "gpii.singleInstance.registerInstance",
"onCreate.mountWebSocketsSettingsHandler": {
funcName: "gpii.flowManager.local.mountWebSocketsSettingsHandler",
args: ["{webSocketsSettingsHandlerComponent}"]
},
// Fire "getDefaultSettingsData" event to trigger promise transform chain that does:
// 1. Read default settings from the reset to default file;
// 2. Calculate defaultLifecycleInstructions and defaultSnapshot based on the default settings;
// 3. All default settings data are held in flowManager.defaultSettingsDataPromise.
// Note: all of this machinery should be removed once we can update to the FLUID-6148 framework and we can
// put all of this I/O into the "resources" block of a fluid.resourceLoader.
"onCreate.calculateDefaultSettingsData": {
listener: "fluid.promise.fireTransformEvent",
args: ["{that}.events.getDefaultSettingsData"]
},
"getDefaultSettingsData.loadDefaultSettngs": {
listener: "{defaultSettingsLoader}.get",
priority: "first"
},
"getDefaultSettingsData.convertDefaultSettings": {
listener: "gpii.flowManager.convertDefaultSettingsToSnapshot",
args: ["{that}", "{arguments}.0", "{that}.events.defaultSettingsDataLoaded"],
priority: "after:loadDefaultSettngs"
},
"flowManagerReady.loginWithNoUser": {
listener: "gpii.flowManager.local.noUserLoggedIn",
args: ["{lifecycleManager}", "{that}.events.noUserLoggedIn"]
},
"noUserLoggedIn.resetAtStart": {
listener: "gpii.flowManager.local.resetAtStart",
args: ["{lifecycleManager}", "{that}.options.resetAtStart", "{that}.defaultSettingsDataPromise", "{that}.events.resetAtStartSuccess", "{that}.events.resetAtStartError"]
},
"afterDestroy.deregisterInstance": "gpii.singleInstance.deregisterInstance",
"{lifecycleManager}.events.onAutoSaveRequired": {
namespace: "savePreferences",
listener: "{that}.savePreferences"
}
}
});
gpii.flowManager.local.mountWebSocketsSettingsHandler = function (webSocketsSettingsHandlerComponent) {
gpii.settingsHandlers.webSockets.instance = webSocketsSettingsHandlerComponent;
};
gpii.flowManager.local.noUserLoggedIn = function (lifecycleManager, noUserLoggedInEvent) {
var promise = lifecycleManager.addToUserLogonRequestQueue({
gpiiKey: "noUser",
logonState: "login"
});
promise.then(noUserLoggedInEvent.fire);
};
gpii.flowManager.convertDefaultSettingsToSnapshot = function (that, defaultSettings, defaultSettingsDataCreatedEvent) {
var promiseTogo = fluid.promise();
if (defaultSettings) {
var matchMakingPromise = that.privateMatchMaker.doMatch("noUser", defaultSettings);
matchMakingPromise.then(function (payload) {
var activePrefsSetName = payload.activePrefsSetName || "gpii-default";
// convert the matchmaker output to lifecycle instructions
var defaultLifecycleInstructions = gpii.transformer.configurationToSettings(payload.matchMakerOutput.inferredConfiguration[activePrefsSetName], payload.solutionsRegistryEntries);
// convert lifecycle instructions to snapshots
var defaultSnapshot = fluid.transform(defaultLifecycleInstructions, function (handlerResponse) {
return gpii.settingsHandlers.settingsPayloadToChanges(handlerResponse);
});
promiseTogo.resolve({
defaultSettings: defaultSettings,
defaultLifecycleInstructions: defaultLifecycleInstructions,
defaultSnapshot: defaultSnapshot
});
defaultSettingsDataCreatedEvent.fire();
});
} else {
promiseTogo.resolve({});
defaultSettingsDataCreatedEvent.fire();
}
fluid.promise.follow(promiseTogo, that.defaultSettingsDataPromise);
};
gpii.flowManager.local.resetAtStart = function (lifecycleManager, resetAtStart, defaultSettingsDataPromise, resetSuccessEvent, resetErrorEvent) {
defaultSettingsDataPromise.then(function (defaultSettingsData) {
var defaultSnapshot = defaultSettingsData.defaultSnapshot;
if (resetAtStart && defaultSnapshot) {
fluid.log("Resetting on system startup to default settings: ", defaultSnapshot);
var resetPromise = lifecycleManager.restoreSnapshot(defaultSnapshot);
resetPromise.then(function () {
fluid.log("Resetting on system startup completes successfully.");
resetSuccessEvent.fire();
}, function (error) {
fluid.log("Resetting on system startup fails with the error: ", error);
resetErrorEvent.fire(error);
});
} else {
resetSuccessEvent.fire();
}
});
};
/**
* Auto save updated preferences received from PSP or QSS.
* @param {Component} that - An instance of gpii.flowManager.local.
* @param {String} [gpiiKey] - [optional] The GPII key that the auto-saved preferences associates with.
* @param {Object} [preferences] - [optional] The preferences to be auto saved to the cloud.
* Note: when there isn't any preference to be auto saved, gpiiKey and preferences should not be provided.
* In this case, preferencesSavedSuccess event will still be fired, which will trigger the firing of its parent
* aggregate event preferencesApplied for the auto save process to proceed.
*/
gpii.flowManager.local.savePreferences = function (that, gpiiKey, preferences) {
// Don't support the save when the active user is "noUser"
if (gpiiKey && preferences && gpiiKey !== "noUser") {
if (that.setSettings) {
// With untrusted config where that.setSettings() is defined, auto save updated preferences that are allowed
// to be auto saved.
var savePromise = that.setSettings(gpiiKey, preferences);
savePromise.then(that.events.preferencesSavedSuccess.fire, that.events.preferencesSavedError.fire);
}
else {
// With trusted config where that.setSettings() is not defined, fire preferencesSavedSuccess() for the
// workflow to proceed.
that.events.preferencesSavedSuccess.fire();
}
} else {
// When the updated preferences are not allowed to be auto saved, fire success event automatically
that.events.preferencesSavedSuccess.fire();
}
};
/**
* The add-on grade containing http endpoints that will be attached to the local flow manager when the
* "suppressHttpEndpoints" flag is turned on.
*/
fluid.defaults("gpii.flowManager.local.requestHandlers", {
requestHandlers: {
userLogin: {
route: "/user/:gpiiKey/login",
method: "get",
type: "gpii.flowManager.userLogin.handler"
},
userLogout: {
route: "/user/:gpiiKey/logout",
method: "get",
type: "gpii.flowManager.userLogout.handler"
},
proximityTriggered: {
route: "/user/:gpiiKey/proximityTriggered",
method: "get",
type: "gpii.flowManager.proximityTrigger.handler"
}
}
});