context.js

'use strict';
const EventEmitter = require('eventemitter2').EventEmitter2;

class Context {
    /**
     * constructor
     *
     * @param  {Object} options
     * @param  {Socket} socket
     * @param  {Event} event
     * @param  data
     * @return {Context}
     *
     * @example
     * // ctx.on('before-result', callback)
     * //`before-result` event fire before emit result (success or not);
     * // You may pass function return promise as callback.
     * ctx.on('prepesult', (ctx) => {
     *     return new Promise((resolve) => {
     *         ctx.result.data = 'my data';
     *         setTimeout(resolve, 200);
     *     });
     * });
     *
     * @example
     * // ctx.on('result', callback)
     * // `result` event fire after emit result (success or not)
     * ctx.on('result', (ctx) => {
     *     console.log(\`send result ${ctx.event.name}\`, ctx.result);
     * });
     */
    constructor(options, socket, event, data) {
        this.options = options;
        this.emitter = new EventEmitter();

        this.event = event;

        this.socket = this.s = socket;
        this.data = data;

        this.result = null;
        this.status = null;
    }

    sendResult(postfix, data) {
        if (this._resultLock) {
            throw Error('result function should call once');
        }
        let DELIMITER = this.options.delimiter;
        this.status = postfix;
        this.result = data;
        this._resultLock = true;
        this.emitter.emitAsync('before-result', this)
        .catch((err) => {
            let error = Context._formatError(err, this.options);
            postfix = this.options.errorPostfix;
            this.result = error;
            this.status = postfix;
        })
        .then(() => {
            let responseEventName = this.event.name + DELIMITER + this.status;
            this.socket.emit(responseEventName, this.result);
            this.emitter.emitAsync('result', this);
        });
    }

    static _formatError(err) {
        if (err.code) {
            return err;
        }
        return {
            code: 503,
            description: err
        };
    }

    /**
     * success - success callback. Should call once.
     *
     * @param  data
     * @return {undefined}
     */
    success(data) {
        let SUCCESS_POSTFIX = this.options.successPostfix;
        this.sendResult(SUCCESS_POSTFIX, data);
    }

    /**
     * error - error callback. Should call once.
     *
     * @param  data
     * @return {undefined}
     */
    error(data) {
        let ERROR_POSTFIX = this.options.errorPostfix;
        this.sendResult(ERROR_POSTFIX, data);
    }


    /**
     * emit - emit message with prefix `${uuid}:${requestName}:${path}`
     *
     * @param  {String} path
     * @param  data
     * @return {undefined}
     */
    emit(path, data) {
        let DELIMITER = this.options.delimiter;
        let eventName = this.event.name + DELIMITER + path;
        return this.socket.emit(eventName, data);
    }


    /**
     * broadcast - broadcast message with prefix `${requestName}:${path}`
     *
     * @param  {String} path
     * @param  data
     * @return {undefined}
     */
    broadcast(path, data) {
        let DELIMITER = this.options.delimiter;
        let eventName;
        if (path) {
            eventName = this.event.requestName + DELIMITER + path;
        } else {
            eventName = this.event.requestName;
        }
        return this.socket.broadcast.emit(eventName, data);
    }


    /**
     * to - broadcast message to spicific room
     *
     * @param  {String} roomName
     * @return {object}
     */
    to(roomName) {
        let DELIMITER = this.options.delimiter;
        let requestName = this.event.requestName;
        let room = this.socket.broadcast.to(roomName);
        return {
            emit: (path, data) => {
                let eventName = requestName;
                if (path) {
                    eventName = requestName + DELIMITER + path;
                }
                eventName = eventName.trim(DELIMITER);
                return room.emit(eventName, data);
            }
        };
    }
};

module.exports = Context;