BelirafoN/asterisk-ami-events-stream

View on GitHub
lib/AmiEventsStream.js

Summary

Maintainability
A
55 mins
Test Coverage
/**
 * Developer: Alex Voronyansky <belirafon@gmail.com>
 * Date: 14.11.2014
 * Time: 12:28
 */

"use strict";

const Transform = require('stream').Transform;
const eventUtils = require('asterisk-ami-event-utils');
const COMMAND_END = '--END COMMAND--';

/**
 * Ami Event Emitter
 */
class AmiEventsStream extends Transform{

    constructor(){
        super();
        Object.assign(this, {
            _parser: eventUtils.toObject,
            _rawData: [],
            _sawFirstCrLf: false,
            _buffer: null,
            _lastAmiEvent: null,
            _lastAmiResponse: null,
            _lastAmiAction: null,
            _isEmitted: true
        });
    }

    /**
     *
     * @returns {*}
     */
    getLastEvent(){
        return this._lastAmiEvent;
    }

    /**
     *
     * @returns {*}
     */
    get lastEvent(){
        return this.getLastEvent();
    }

    /**
     *
     * @returns {null}
     */
    getLastResponse(){
        return this._lastAmiResponse;
    }

    /**
     *
     * @returns {null}
     */
    get lastResponse(){
        return this.getLastResponse();
    }

    /**
     *
     * @returns {*}
     */
    getLastAction(){
        return this._lastAmiAction;
    }

    /**
     *
     * @returns {null}
     */
    get lastAction(){
        return this.getLastAction();
    }

    /**
     *
     * @param chunk
     * @param encoding
     * @param done
     * @private
     */
    _transform(chunk, encoding, done){
        let chunkSlice = chunk;

        if(this._rawData.length){
            chunkSlice = Buffer.concat(this._rawData.concat([chunkSlice]));
            this._rawData = [];
        }

        let parseGen = this._parse(chunkSlice);

        while(chunkSlice){
            chunkSlice = parseGen.next(chunkSlice).value;
        }
        done();
    }

    /**
     *
     * @param chunk
     * @private
     */
    _analyzeSimple(chunk){
        let chunkLength = chunk.length;

        for (let i = 0; i < chunkLength; i++) {
            if (chunk[i] === 13 && i + 1 < chunkLength && chunk[i + 1] === 10){
                i++;

                if (this._sawFirstCrLf){
                    this._buffer = chunk.slice(0, i);
                    this._emission(this._buffer);
                    this._sawFirstCrLf = false;
                    return chunk.slice(i);

                }else{
                    this._sawFirstCrLf = true;
                }

            }else{
                this._sawFirstCrLf = false;
            }
        }
        this._rawData.push(chunk.slice(0));
        return null;
    }

    /**
     *
     * @param chunk
     * @private
     */
    _analyzeExtend(chunk){
        let chunkStr = eventUtils.toString(chunk),
            indexOfEnd = chunkStr.indexOf(COMMAND_END);

        if(chunkStr === ''){ return null; }
        
        if(~indexOfEnd){
            this._emission(chunk.slice(0, indexOfEnd + COMMAND_END.length + 1));
            return chunk.slice(indexOfEnd + COMMAND_END.length + 1);
        }
        this._rawData.push(chunk);
        return null;
    }

    /**
     *
     * @returns {null}
     * @private
     */
    * _parse(chunk){
        let chunkSlice = chunk;
        while(chunkSlice){
            chunkSlice = this._isEmitted && !/^Response:\sFollows/i.test(eventUtils.toString(chunkSlice)) ?
                yield this._analyzeSimple(chunkSlice) : yield this._analyzeExtend(chunkSlice);
        }
        return null;
    }

    /**
     *
     * @param eventBuffer
     * @private
     */
    _emission(eventBuffer){
        let eventStr = eventUtils.toString(eventBuffer);

        if(eventStr.length){
            if(/^Event/i.test(eventStr)){
                this._lastAmiEvent = this._parser(eventBuffer);
                this.emit('amiEvent', this._lastAmiEvent);

            }else if(/^Action/i.test(eventStr)){
                this._lastAmiAction = this._parser(eventBuffer);
                this.emit('amiAction', this._lastAmiAction);

            }else{
                this._lastAmiResponse = this._parser(eventBuffer);
                this.emit('amiResponse', this._lastAmiResponse);
            }
            this.push(eventUtils.fromString(eventStr));
        }

        this._isEmitted = true;
        return this;
    }
}

module.exports = AmiEventsStream;