Source: rest/Session.js

/**
 * @version  1.0.0
 * @author Boris GBAHOUE
 * @file Common Session middleware
 * @module amiwo/rest
*/


// =======================================================================
// BASE SETUP
// =======================================================================
// call the packages we need
var debug                       = require('debug')('amiwo:session');
var u                           = require('../util');

// public static variables
var __sessionHash               = {};

// =======================================================================
// CONSTRUCTOR
// =======================================================================
/**
 * @class
 * 
 * @param {String} app: application name
 * @param {Object} options
 */
function Session(app, options) {
    this._application = app || "amiwo";
    
    __sessionHash[this._application] = this;
    
    // Options processing
    if (options == null) options = {};
}

// =======================================================================
// PUBLIC METHODS
// =======================================================================
/**
 * Middleware to initialize our session logic
 * 
 * @public
 */
Session.prototype.init = function(req, res, next) {
    if (req[this._application]) return next(); // Initialize only once per request
    
    req[this._application] = {};
    
    if (req.session && req.session[this._application]) {
        // load data from existing session
        req[this._application].session = req.session[this._application];
    } else {
        if (req.session) {
            // initialize new session
            req.session[this._application] = {};
            req[this._application].session = req.session[this._application];
        } else {
            // no session is available
            req[this._application].session = {};
        }
        req[this._application].session.request = {};
        req[this._application].session.response = {};
    }
    return next();
}

/**
 * Saves 'value' into our session response object
 * 
 * @param {Request} req
 * @param {String} property property name, can use dot to build a structured JSON
 * @param {any} value
 * 
 * @public
 */
Session.prototype.setResponse = function(req, property, value) {
    $set.call(this, "response", req, property, value);
}

/**
 * Get a value from our session response object
 * 
 * @param {Request} req
 * @param {String} property property name, can use dot to build a structured JSON
 * @param {Boolean} [deleteEntry=true] if set to true, delete response object
 * 
 * @public
 */
Session.prototype.getResponse = function(req, property, deleteEntry) {
    deleteEntry = !(deleteEntry == false);
    var tmp = $get.call(this, "response", req, property);
    if (deleteEntry) this.deleteResponse(req, property);
    return tmp;
}

/**
 * Delete a value from our session response object
 * 
 * @param {Request} req
 * @param {String} property property name, can use dot to build a structured JSON
 * 
 * @returns {any} the deleted entry
 * 
 * @public
 */
Session.prototype.deleteResponse = function(req, property) {
    return $delete.call(this, "response", req, property);
}

/**
 * Clear session response object
 * 
 * @param {Request} req
 * 
 * @public
 */
Session.prototype.clearResponse = function(req) {
    req[this._application].session.response = {};
}

/**
 * Saves 'value' into our session request object
 * 
 * @param {Request} req
 * @param {String} property property name, can use dot to build a structured JSON
 * @param {any} value
 * 
 * @public
 */
Session.prototype.setRequest = function(req, property, value) {
    $set.call(this, "request", req, property, value);
}

/**
 * Get a value from our session request object
 * 
 * @param {Request} req
 * @param {String} property property name, can use dot to build a structured JSON
 * @param {Boolean} [deleteEntry=true] if set to true, delete response object
 * 
 * @public
 */
Session.prototype.getRequest = function(req, property, deleteEntry) {
    deleteEntry = !(deleteEntry == false);
    var tmp = $get.call(this, "request", req, property);
    if (deleteEntry) this.deleteRequest(req, property);
    return tmp;
}

/**
 * Delete a value from our session request object
 * 
 * @param {Request} req
 * @param {String} property property name, can use dot to build a structured JSON
 * 
 * @returns {any} the deleted entry
 * 
 * @public
 */
Session.prototype.deleteRequest = function(req, property) {
    return $delete.call(this, "request", req, property);
}

/**
 * Clear session request object
 * 
 * @param {Request} req
 * 
 * @public
 */
Session.prototype.clearRequest = function(req) {
    req[this._application].session.request = {};
}

/**
 * Create the objects to hold flash messages if needed.
 *
 * @public
 */
Session.prototype.initMessages = function(req) {
    if (u.isEmpty(req[this._application].session.messages)) req[this._application].session.messages = {};
    
    if (u.isEmpty(req[this._application].session.messages.success)) req[this._application].session.messages.success = [];
    if (u.isEmpty(req[this._application].session.messages.info)) req[this._application].session.messages.info = [];
    if (u.isEmpty(req[this._application].session.messages.warning)) req[this._application].session.messages.warning = [];
    if (u.isEmpty(req[this._application].session.messages.error)) req[this._application].session.messages.error = [];
    
    // for backward compatibility
    req.session.messages = req[this._application].session.messages;
}

/**
 * Clear messages (and init messages if they didn't exist)
 * 
 * @public
 */
Session.prototype.clearMessages = function(req) {
    this.initMessages(req);
    
    req[this._application].session.messages.success = [];
    req[this._application].session.messages.info = [];
    req[this._application].session.messages.warning = [];
    req[this._application].session.messages.error = [];
}

/**
 * Get message objects
 */
Session.prototype.getMessages = function(req) {
    this.initMessages(req);
    
    var messages = u.clone(u.fuse(req[this._application].session.messages, req.session.messages));
    this.clearMessages(req);
    
    return messages;
}

/**
 * Set a new message
 */
Session.prototype.setMessage = function(req, type, message) {
    if ((req == null) || u.isEmpty(type) || u.isEmpty(message)) return;
    if ((req[this._application] == null) || (req[this._application].session == null) || (req[this._application].session.messages == null)) this.initMessages(req);
    
    if (req[this._application].session.messages[type].indexOf(message) == -1) {
        req[this._application].session.messages[type].push(message);
    } else {
        // Already existing message => don't create a duplicate
    }
}

/**
 * Saves an info message
 */
Session.prototype.setInfoMessage = function(req, message) {
    this.setMessage(req, "info", message);
}

/**
 * Saves a warning message
 */
Session.prototype.setWarningMessage = function(req, message) {
    this.setMessage(req, "warning", message);
}

/**
 * Saves an error message
 */
Session.prototype.setErrorMessage = function(req, message) {
    this.setMessage(req, "error", message);
}

/**
 * Saves a success message
 */
Session.prototype.setSuccessMessage = function(req, message) {
    this.setMessage(req, "success", message);
}

// =======================================================================
// PUBLIC STATIC METHODS
// =======================================================================
/**
 * Get the Session object for an application
 */
Session.getSession = function(app) {
    var session = __sessionHash[app];
    if (session == null) {
        session = new Session(app);
        __sessionHash[app] = session;
    }
 
    return session;
}

// =======================================================================
// PRIVATE METHODS
// =======================================================================
/**
 * Saves 'value' into our session object 'object'
 * 
 * @param {String} object 'response' or 'request'
 * @param {Request} req
 * @param {String} property property name, can use dot to build a structured JSON
 * @param {any} value
 * @memberOf Session
 * @private
 */
function $set(object, req, property, value) {
    if (u.isEmpty(object)) throw new ReferenceError("object can't be null");
    
    if ((property == null) || (req == null)) return;

    if (req && req[this._application]) {
        if (req[this._application].session == null) req[this._application].session = {};
        if (req[this._application].session[object] == null) req[this._application].session[object] = {};

        if (property.indexOf(".") > -1) {
            var propSplit = property.split(".");
            var lastProp = propSplit.pop();
            var obj = req[this._application].session[object];
            
            for (var i = 0; i < propSplit.length; i++) {
                if (obj[propSplit[i]] == null) {
                    obj[propSplit[i]] = {};
                }
                obj = obj[propSplit[i]];
            }
            obj[lastProp] = value;
        } else {
            req[this._application].session[object][property] = value;
        }
    } else {
        debug("::AMIWO::SESSION::SET%s::%s::ERROR Session object has not been created",object.toUpperCase(), this._application.toUpperCase());
    }
}

/**
 * Get a value of 'property' from our session object 'object'
 * 
 * @param {String} object 'response' or 'request'
 * @param {Request} req
 * @param {String} property property name, can use dot to use structured JSON path
 * @memberOf Session
 * @private
 */
function $get(object, req, property) {
    if (req && req[this._application] && req[this._application].session && req[this._application].session[object]) {
        return u.getProperty(req[this._application].session[object], property);
    }
    
    // else
    return null;
}

/**
 * Delete 'property' from our session object 'object'
 * 
 * @param {String} object 'response' or 'request'
 * @param {Request} req
 * @param {String} property property name, can use dot to build a structured JSON
 * 
 * @returns {any} the deleted entry
 * @memberOf Session
 * @private
 */
function $delete(object, req, property) {
    if (u.isEmpty(object)) throw new ReferenceError("object can't be null");
    
    if ((property == null) || (req == null)) return;
    
    var deletedEntry = null;

    if (req && req[this._application]) {
        if (req[this._application].session == null) req[this._application].session = {};
        if (req[this._application].session[object] == null) req[this._application].session[object] = {};

        if (property.indexOf(".") > -1) {
            var propSplit = property.split(".");
            var lastProp = propSplit.pop();
            var obj = req[this._application].session[object];
            
            for (var i = 0; i < propSplit.length; i++) {
                if (obj[propSplit[i]] == null) {
                    obj[propSplit[i]] = {};
                }
                obj = obj[propSplit[i]];
            }
            deletedEntry = obj[lastProp];
            delete obj[lastProp];
        } else {
            deletedEntry = req[this._application].session[object][property];
            delete req[this._application].session[object][property];
        }
    } else {
        debug("::AMIWO::SESSION::DELETE%s::%s::ERROR Session object has not been created",object.toUpperCase(), this._application.toUpperCase());
    }
    
    return deletedEntry;
}

module.exports = Session;