UNPKG

gpii-universal

Version:

Cross platform, core components of the GPII personalization infrastructure.

222 lines (205 loc) 10.5 kB
/** * GPII Flow Manager Requests * * Copyright 2012 OCAD University * Copyright 2015 Raising the Floor - International * Copyright 2018 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 */ /* eslint-env browser */ /* eslint strict: ["error", "function"] */ (function () { "use strict"; var fluid = require("infusion"), gpii = fluid.registerNamespace("gpii"); fluid.registerNamespace("gpii.flowManager"); gpii.logFully = function () { var oldRenderChars = fluid.logObjectRenderChars; fluid.logObjectRenderChars = 2 << 20; fluid.log.apply(null, arguments); fluid.logObjectRenderChars = oldRenderChars; }; /** Render an instance of the "megapayload" which circulates amongst the FlowManager, MatchMaker, LifecycleManager etc. methods. * This builds up incrementally starting with the GPII key during logon until we reach the "finalPayload" dispatched to the * lifecycleManager. This method transforms the payload into a form appropriate for logging to the console as an aid * to diagnosing and debugging issues encountered in the field. Currently it only performs the action of suppressing the * body of any entries of `solutionsRegistryEntries` since these are bulky and can easily be recovered from the * solutions registry itself. * @param {Object} megapayload - A megapayload record. * @return {Object} The megapayload transformed to a more appropriate form for logging. */ gpii.renderMegapayload = function (megapayload) { var solutionsRegistryEntries = fluid.transform(megapayload.solutionsRegistryEntries, function () { return "<consult solutions registry for full contents>"; }); return fluid.extend({}, megapayload, { solutionsRegistryEntries: solutionsRegistryEntries }); }; gpii.flowManager.getPreferences = function (that, prefsServerDataSource) { var gpiiKey = that.gpiiKey; if (gpiiKey !== "noUser") { fluid.log("gpii.flowManager.getPreferences called - fetching preferences from URL " + prefsServerDataSource.options.prefsServerURL); } var noPrefs = {}; var promise = gpiiKey === "noUser" ? fluid.promise().resolve(noPrefs) : prefsServerDataSource.get(gpiiKey); promise.then(function (data) { that.events.onPreferences.fire(data.preferences || data); }, function (err) { // GPII-3721: Return an empty payload for nonexistent GPII keys if (err.errorCode === "GPII_ERR_NO_GPIIKEY") { that.events.onPreferences.fire(noPrefs); } else { that.events.onError.fire({ isError: true, message: "Error when retrieving preferences: " + err.message, statusCode: 404 }); } }); }; /** * Asynchronous/Promise returning function which makes a get call to the solutions registry (1st parameter) to * retrieve the solutions registry matching what is passed in the `device` parameter. * This is appended to the matchmaker payload (mmpayload) parameter, which in turn is passed * as parameter in the event fired. * * This function can be used with either (or both) asyncronously with an `event` and `onError` handlers * passed in, or with the returned `fluid.promise`. * * @param {Object} solutionsRegistryDataSource - a solutions registry data source * @param {Object} deviceContext - output from a device reporter. Used to filter solutions registry entries * @param {Object} onSuccessEvent - Optional: Event to be fired when the solutionsRegistry entry has been retrieved * @param {Object} onErrorEvent - Optional: Event to be fired when an error occurs * @return {fluid.promise} - Returns a promise resolving with the mmpayload. Optionally if provided, the events * are also fired with the modified mmpayload. */ gpii.flowManager.getSolutions = function (solutionsRegistryDataSource, deviceContext, onSuccessEvent, onErrorEvent) { var promiseTogo = fluid.promise(); var os = fluid.get(deviceContext, "OS.id"); var promise = solutionsRegistryDataSource.get({}); promise.then(function (solutions) { var solutionsRegistryEntries = gpii.matchMakerFramework.filterSolutions(solutions[os], deviceContext); fluid.log("Fetched filtered solutions registry entries: ", gpii.renderMegapayload({solutionsRegistryEntries: solutionsRegistryEntries})); promiseTogo.resolve({ solutionsRegistryEntries: solutionsRegistryEntries, solutions: solutions }); if (onSuccessEvent) { onSuccessEvent.fire(solutionsRegistryEntries, solutions); } }, function (error) { promiseTogo.reject(error); if (onErrorEvent) { onErrorEvent.fire(error); } }); return promiseTogo; }; // initialPayload contains fields // gpiiKey, preferences, deviceContext, solutionsRegistryEntry // resulting from the initial fetch process gpii.flowManager.processMatch = function (that, initialPayload) { var promise = fluid.promise.fireTransformEvent(that.events.processMatch, initialPayload, {}); promise.then(function (finalPayload) { that.events.onMatchDone.fire(finalPayload); }, function (error) { // TODO: This rejection handler is untested that.handlerPromise.reject(error); }); }; // This is a table of priorities for handlers in the "processMatch" filter chain governing the MatchMaking process. // This will be removed in favour of a relative constraints system once we have FLUID-5506 completed // Higher priority numbers are handled earlier than lower ones // This process is kicked off by the "onReadyToMatch" event defined in the "gpii.flowManager.matchMaking" grade gpii.flowManager.processMatch.priorities = { // onReadyToMatch event signals start of process preProcess: 100, matchMakerDispatcher: 90 // onMatchDone event fired to lifecycleManager or cloud-based settings endpoint }; /* This component orchestrates the lifecycle for a component which assembles the raw materials required for the matchmaking * process, invokes it, and distributes the results. These raw materials are the gpiiKey (of the user whose preferences are to * be fetched), the device information for the current platform, and the registry of solutions which are available to be * invoked. It is a request-scoped grade and intended as a mixin for a matchmaking request such as gpii.lifecycleManager.loginRequest * and gpii.flowManager.cloudBased.settings.get.handler. * This grade is used in the following places: * UserLogonStateChange.js - where it coordinates the standard lifecycle for a user logging on to a local FlowManager * CloudBasedFlowManager.js - where it coordinates the lifecycle for a user requesting settings from a cloud-based FlowManager */ fluid.defaults("gpii.flowManager.matchMaking", { invokers: { getPreferences: { funcName: "gpii.flowManager.getPreferences", args: ["{that}", "{flowManager}.prefsServerDataSource"] }, processMatch: { funcName: "gpii.flowManager.processMatch", args: [ "{that}", "{arguments}.0"] // initial payload }, getSolutions: { funcName: "gpii.flowManager.getSolutions", args: [ "{flowManager}.solutionsRegistryDataSource", "{arguments}.0", "{that}.events.onSolutions", "{that}.events.onError"] } }, events: { // Four pre-requisites for the match process to begin onGpiiKey: null, onPreferences: null, onDeviceContext: null, onSolutions: null, // The "pseudo-event" whose handlers govern the match processing chain processMatch: null, // Output of the matching process - listeners in derived grades onMatchDone: null, // Boiled event which initiates the match process onReadyToMatch: { events: { preferences: "onPreferences", deviceContext: "onDeviceContext", solutions: "onSolutions" }, args: [{ gpiiKey: "{that}.gpiiKey", preferences: "{arguments}.preferences.0", deviceContext: "{arguments}.deviceContext.0", solutionsRegistryEntries: "{arguments}.solutions.0", fullSolutionsRegistry: "{arguments}.solutions.1" }] } }, listeners: { "onGpiiKey.setGpiiKey": { listener: "gpii.flowManager.setGpiiKey", args: ["{that}", "{arguments}.0"] }, "onGpiiKey.getPreferences": { func: "{that}.getPreferences", priority: "after:setGpiiKey" }, "onDeviceContext.getSolutions": "{that}.getSolutions", processMatch: [{ // Definition of the MatchMaking processing chain priority: gpii.flowManager.processMatch.priorities.preProcess, namespace: "preProcess", listener: "gpii.matchMakerFramework.utils.preProcess" }, { priority: gpii.flowManager.processMatch.priorities.matchMakerDispatcher, namespace: "matchMakerDispatcher", listener: "{flowManager}.matchMakerFramework.matchMakerDispatcher" }], "onReadyToMatch.processMatch": "{that}.processMatch" } }); gpii.flowManager.setGpiiKey = function (that, gpiiKey) { that.gpiiKey = gpiiKey; }; })();