UNPKG

gpii-universal

Version:

Cross platform, core components of the GPII personalization infrastructure.

391 lines (371 loc) 16 kB
/* * 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" } } });