All files / universal.klown/gpii/node_modules/contextManager/src ContextManager.js

88% Statements 44/50
75% Branches 6/8
77.78% Functions 7/9
88% Lines 44/50

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228                                    1x 1x     1x                               1x                                         1x 12x 12x     1x                                                                           1x 12x 12x 12x                           1x 172x                                 1x 172x                                         1x   13x   13x 2x   11x 11x 11x     11x   11x       11x       11x   11x 11x   11x         11x   11x 11x       11x                     1x 129x   129x 128x     128x   128x     1x 1x   129x 129x    
/*
* Context Manager
*
* Copyright 2014-2017 Raising the Floor - International
*
* 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");
 
 
fluid.defaults("gpii.contextManager.app", {
    gradeNames: "kettle.app",
    components: {
        contextManager: {
            type: "gpii.contextManager"
        }
    },
    requestHandlers: {
        environmentChanged: {
            route: "/environmentChanged",
            method: "put",
            type: "gpii.contextManager.environmentChanged.handler"
        }
    }
});
 
fluid.defaults("gpii.contextManager.environmentChanged.handler", {
    gradeNames: ["kettle.request.http"],
    invokers: {
        handleRequest: {
            funcName: "gpii.contextManager.environmentChanged.handleRequest",
            args: ["{flowManager}.contextManager", "{request}.req.body", "{request}.events.onSuccess"]
        }
 
    }
});
 
/**
 * Request handler for URL /environmentChanged. Forwards the content to environmentChanged
 * function and fires the onSuccess event.
 *
 * @param that {Component} The context manager component
 * @param body {Object} the body of the request. It is expected to contain only key-value pairs
 *      of context variables, and optionally a 'timestamp' key (and associated value) which
 *      will not be considered part of the current context
 * @param onSuccess {Event} onSuccess event - will be fired with empty content
 **/
gpii.contextManager.environmentChanged.handleRequest = function (that, body, onSuccess) {
    gpii.contextManager.updateCurrentEnvironment(that, body);
    onSuccess.fire();
};
 
fluid.defaults("gpii.contextManager", {
    gradeNames: "fluid.modelComponent",
    members: {
        temporalIntervalID: null
    },
    invokers: {
        updateActiveContextName: {
            funcName: "gpii.contextManager.updateActiveContextName",
            args: ["{that}", "{arguments}.0", "{arguments}.1"] // fullPayload, forceContextName
        },
        contextChanged: {
            funcName: "gpii.contextManager.contextChanged",
            args: ["{that}", "{lifecycleManager}", "{arguments}.0"] // forceContextName
        },
        environmentChanged: "gpii.contextManager.updateCurrentEnvironment({that}, {arguments}.0)" // body
    },
    modelListeners: {
        currentEnvironment: {
            func: "{that}.contextChanged",
            args: []
        }
    },
    listeners: {
        onCreate: "gpii.contextManager.startTemporalEnvironmentReporter",
        onDestroy: "gpii.contextManager.stopTemporalEnvironmentReporter"
    }
});
 
 
/**
 * Responsible for weeding out any information from the request body that is not strictly
 * environmental variables and then notifying the applier.
 *
 * @param that {Component} The context manager component
 * @param body {Object} the body of the request. It is expected to contain only key-value pairs
 *      of environmental variables, and optionally a 'timestamp' key (and associated value) which
 *      will not be considered part of the current context
 **/
gpii.contextManager.updateCurrentEnvironment = function (that, body) {
    var newEnvironment = fluid.censorKeys(body, ["timestamp"]);
    fluid.log("contextManager: Updating environment with: " + JSON.stringify(newEnvironment, null, 4));
    that.applier.change("currentEnvironment", newEnvironment);
};
 
 
/**
 * Function to start the "Temporal reporter". Will report timerelated contexts every 20 seconds.
 * It reports both the timestamp and timeOfDay context variables via manual calls to the
 * gpii.contextManager.environmentChanged function
 *
 * TODO: The timezone here is retrieved based on the system time - the geographical location
 * of the user is not taken into account (unless the OS does so by default) - see GPII-1105
 *
 * @param {Component} The context manager object
 **/
gpii.contextManager.startTemporalEnvironmentReporter = function (that) {
    that.temporalIntervalID = setInterval(function () {
        var date = new Date();
        gpii.contextManager.updateCurrentEnvironment(that, {
            // ms since UTC epoch, eg: 1421066816897
            "http://registry.gpii.net/common/environment/timestamp": date.getTime(),
            // time of day, eg: "18:30"
            "http://registry.gpii.net/common/environment/timeOfDay": date.getHours() + ":" + date.getMinutes()
        });
    }, 20000); // report time every 20 seconds
};
 
/**
 * Triggered on destruction of the context manager component. Stops the temporal reporters
 * time reports
 *
 * @param that {Component} The context manager object
 **/
gpii.contextManager.stopTemporalEnvironmentReporter = function (that) {
    clearInterval(that.temporalIntervalID);
};
 
/**
 * This function is listening to the changeApplier of the contextManager ("currentEnvironment" path)
 *
 * On changes to the currentEnvironment part of the model it will:
 * 1) parse the context against the currently logged in user's conditions
 * 2) if the new calculated active context is different from the currently applied context,
 *    the LifecycleManager's update functionality will be called and the applied context and
 *    configuration will be stored in the active session of the lifecycle manager.
 *
 * @param that {Object} The context manager object
 * @param lifecycleManager {Component} The lifecycleManager component from which the current
 *    session will be retrieved
 * @param forceContextName {String} [optional] A context name to be unconditionally selected
 * @return {Promise} If the context change causes an actual change in the system (i.e. change of
 *    active context and changes to the applied configuration) a promise of an update to the system
 *    configuration is returned. If the call to contextChanged does not result in a different
 *    active context or no user is logged in, undefined will be returned.
 **/
gpii.contextManager.contextChanged = function (that, lifecycleManager, forceContextName) {
    // find logged in users
    var activeSessions = lifecycleManager.getActiveSessionTokens();
    // if noone is logged in, do nothing
    if (activeSessions.length === 0) {
        return;
    }
    var activeSession = lifecycleManager.getSession(activeSessions);
    var fullPayload = fluid.extend(true, {}, activeSession.model);
    var oldActiveContext = fullPayload.activeContextName;
 
    // find and update context:
    that.updateActiveContextName(fullPayload, forceContextName);
 
    Iif (oldActiveContext === fullPayload.activeContextName) {
        fluid.log(fluid.logLevel.TRACE, "contextManager: Same context as before (" + oldActiveContext + ") so doing nothing");
        return;
    }
    fluid.log("contextManager: New active contexts: " + fullPayload.activeContextName);
    // Update the context name immediately so we don't produce a model update from the lifecycleManager with a
    // partially applied set of solutions from the new context, confusing, e.g., the PCPChannel
    // This is required because of https://issues.fluidproject.org/browse/FLUID-6208
    activeSession.applier.change("activeContextName", fullPayload.activeContextName);
 
    lifecycleManager.addLifecycleInstructionsToPayload(fullPayload);
    var response = lifecycleManager.update(fullPayload);
 
    response.then(function () {
        // Delete these this to avoid the 'lifecycleInstructions' block being attached
        // to the sessionState in the next line. LifecycleInstructions is only used in the
        // "finalPayload" as instructions on how to configure the system. once configured,
        // the applied settings are stored in 'activeConfiguration'
        delete fullPayload.lifecycleInstructions;
        // TODO: This is pretty rubbish, why isn't the natural action of lifecycleManager.update good enough here?
        activeSession.applier.change("", fullPayload); // TODO: this will need to be a "MERGE" in time
        fluid.log("contextManager: Successfully updated configuration triggered by context changes");
    }, function () {
        fluid.log(fluid.logLevel.ERROR, "contextManager: Failed to apply newly evaluated conditions");
    });
    return response;
};
 
/**
 * Function to add the current context to the full payload which is output from the matchmaking process.
 *
 * @param that {Component} - gpii.contextManager component
 * @param fullPayload {Object} - the full structure output from the matchmaking process. NOTE: This object will be modified
 * @param forceContextName {String} [optional] - A context name to be selected, overriding environment conditions
 * @return {Object} - the modified fullPayload, now including activeContextName, activeConfiguration
 */
gpii.contextManager.updateActiveContextName = function (that, fullPayload, forceContextName) {
    var matchMakerOutput = fullPayload.matchMakerOutput;
    var newActiveContextName;
    if (forceContextName === undefined) {
        Iif (fullPayload.forcedContext) { // Don't compute conditions if context has been forced
            newActiveContextName = fullPayload.activeContextName;
        } else {
            var activeContexts = gpii.contextManager.utils.findActiveContexts(fluid.get(that.model, "currentEnvironment"), matchMakerOutput);
            // save highest priority context as the applied one
            newActiveContextName = activeContexts[0];
        }
    } else {
        newActiveContextName = forceContextName;
        fullPayload.forcedContext = true;
    }
    fullPayload.activeContextName = newActiveContextName;
    return fullPayload;
};