<commit_before>/* module make possible connectoin thru socket.io on a server */

(function() {
    'use strict';

    var main            = global.cloudcmd.main,
        DIR             = main.DIR,
        SRVDIR          = main.SRVDIR,

        io              = main.require('socket.io'),
        update          = main.srvrequire('update'),

        exec            = main.child_process.exec,
        spawn           = main.child_process.spawn,

        Util            = main.util,
        path            = main.path,
        mainpackage     = main.mainpackage,
        equalPart       = Util.isContainStrAtBegin,
        CLOUDCMD        = mainpackage.name,
        ClientFuncs     = [],
        ClientDirs      = [],
        Clients         = [],
        INFO_LOG_LEVEL  = 2,
        ENV             = process.env,
        WIN32           = main.WIN32,
        ConNum          = 0,
        HELP            = {
            stdout  : CLOUDCMD + ' exit     \n' +
                      CLOUDCMD + ' update   \n',
        },

        /* windows commands thet require
         * unicode charset on locales
         * different then English
         */
        Win32Commands   = ['ASSOC', 'AT', 'ATTRIB', 'BREAK', 'CACLS', 'CALL',
                            'CD', 'CHCP', 'CHDIR', 'CHKDSK', 'CHKNTFS', 'CLS',
                            'CMD', 'COLOR', 'COMP', 'COMPACT', 'CONVERT', 'COPY',
                            'DATE', 'DEL', 'DIR', 'DISKCOMP', 'DISKCOPY', 'DOSKEY',
                            'ECHO', 'ENDLOCAL', 'ERASE', 'EXIT', 'FC', 'FIND',
                            'FINDSTR', 'FOR', 'FORMAT', 'FTYPE', 'GOTO', 'GRAFTABL',
                            'HELP', 'IF', 'LABEL', 'MD', 'MKDIR', 'MODE', 'MORE',
                            'MOVE', 'PATH', 'PAUSE', 'POPD', 'PRINT', 'PROMPT',
                            'PUSHD', 'RD', 'RECOVER', 'REM', 'REN', 'RENAME',
                            'REPLACE', 'RMDIR', 'SET', 'SETLOCAL', 'SHIFT', 'SORT',
                            'START', 'SUBST', 'TIME', 'TITLE', 'TREE', 'TYPE',
                            'VER', 'VERIFY', 'VOL', 'XCOPY'];

    /**
     * function listen on servers port
     * @pServer {Object} started server object
     */
    exports.listen = function(pServer) {
        var ret;

        if (io) {
            io          = io.listen(pServer);

            io.set('log level', INFO_LOG_LEVEL);

            /*
             * on Win7 application is crashing,
             * when options below is used.
             *
             * https://github.com/LearnBoost/socket.io/issues/1314
             *
             */
            if (!WIN32) {
                io.enable('browser client minification');
                io.enable('browser client gzip');
                io.enable('browser client etag');
            }

            io.set('transports', [
              'websocket',
              'htmlfile',
              'xhr-polling',
              'jsonp-polling'
            ]);

            ret = io.sockets.on('connection', function(socket) {
                onConnection(socket, function(json) {
                    var msg = Util.stringifyJSON(json);
                    socket.send(msg);
                });
            });
        }

        return ret;
    };

    function onConnection(socket, callback) {
        var msg, onDisconect, onMessage;

        ++ConNum;

        if (!Clients[ConNum]) {
            msg = log(ConNum, 'connected\n');

            Util.exec(callback, {
                stdout : msg
            });

            Clients[ConNum]     = true;

            onMessage           = getOnMessage(ConNum, callback);
            onDisconect         = Util.retFunc(function(conNum) {
                disconnect(connNum);
                socket.removeListener('message', onMessage);
                socket.removeListener('disconnect', onDisconect);
            }, ConNum);

            socket.on('message', onMessage);
            socket.on('disconnect', onDisconect);
        } else {
            msg = log(ConNum, ' in use. Reconnecting...\n');

            Util.exec(callback, {
                stdout: msg
            });

            socket.disconnect();
        }
    }

    function disconnect(connNum) {
        OnMessageFuncs  [connNum]  =
        ClientFuncs     [connNum]  = null;

        log(connNum, 'disconnected');
    }

    /**
     * function gets onMessage function
     * that execute needed command
     *
     * @param pConnNum
     * @param callback
     */
    function getOnMessage(pConnNum, callback) {
        return function(pCommand) {
            var lMsg, lWinCommand, lExec_func, firstChar,
                connName,
                lError, lRet, lExecSymbols, isContain,
                dir, options = {};

            dir = ClientDirs[pConnNum];

            if (!dir)
                dir = ClientDirs[pConnNum] = DIR;

            connName = '#' + pConnNum + ': ';
            Util.log(connName + pCommand);

            if (equalPart(pCommand, CLOUDCMD))
                lRet = onCloudCmd(pCommand, callback);
            else if (equalPart(pCommand, 'cd '))  {
                lRet = true;

                onCD(pCommand, dir, function(json) {
                    var error   = json.stderr,
                        stdout  = json.stdout;

                    if (error)
                        Util.exec(callback, json);
                    else
                        ClientDirs[pConnNum] = stdout;
                });
            }

            if (!lRet) {
                /* if we on windows and command is build in
                 * change code page to unicode becouse
                 * windows use unicode on non English versions
                 */
                if (WIN32) {
                    lWinCommand     = pCommand.toUpperCase();

                    if (Win32Commands.indexOf(lWinCommand) >= 0)
                        pCommand    = 'chcp 65001 |' + pCommand;
                }

                if (!ClientFuncs[pConnNum])
                    ClientFuncs[pConnNum] = getExec(function(json, pError, pStderr) {
                        log(pConnNum, pError, 'error');
                        log(pConnNum, pStderr, 'stderror');

                        Util.exec(callback, json);
                    });

                lExec_func      = ClientFuncs[pConnNum];
                lExecSymbols    = ['*', '&', '{', '}', '|', '\'', '"'];
                isContain       = Util.isContainStr(pCommand, lExecSymbols);
                firstChar       = pCommand[0];
                options.cwd     = dir;

                if (firstChar === '#') {
                    pCommand    = pCommand.slice(1);
                    pCommand    = connName + pCommand;
                    pCommand    = Util.addNewLine(pCommand);

                    lMsg    = Util.stringifyJSON({
                        stdout: pCommand
                    });

                    io.sockets.emit('message', lMsg);
                } else if (WIN32 || firstChar === ' ' || isContain)
                    exec(pCommand, options, lExec_func);
                else
                    getSpawn(pCommand, options, callback);
            }
        };
    }

    /**
     * function send result of command to client
     * @param callback
     */
    function getExec(callback) {
        return function(pError, pStdout, pStderr) {
            var lErrorStr, lExecStr, lExec,
                lError          = pStderr || pError;

            if (lError) {
                if (Util.isString(lError))
                    lErrorStr   = lError;
                else
                    lErrorStr   = lError.toString();

                lErrorStr = Util.addNewLine(lErrorStr);
            }

            lExec               = {
                stdout : pStdout,
                stderr : lErrorStr || lError
            };

            Util.exec(callback, lExec, pError, pStderr);
        };
    }

    function getSpawn(pCommand, options, callback) {
        var send, cmd, error,
            args    = pCommand.split(' ');

        pCommand    = args.shift();

        error       = Util.tryCatchLog(function() {
            cmd = spawn(pCommand, args, options);
        });

        if (!cmd)
            send(error + '', null);
        else {
            send        = function(error, data) {
                var exec    = {
                        stderr: error,
                        stdout: data
                    };

                Util.exec(callback, exec);
            };

            cmd.stdout.on('data', function(data) {
                send(null, data + '');
            });

            cmd.stderr.on('data', function(error) {
                send(error + '', null);
            });

            cmd.on('error', Util.retFalse);
            cmd.on('close', function (code) {
                cmd = null;
            });
        }
    }

    function onCloudCmd(pCommand, callback) {
        var lRet;

        pCommand = Util.removeStr(pCommand, CLOUDCMD);

        if (!equalPart(pCommand, ' ')) {
            lRet = true;
            Util.exec(callback, HELP);
        }
        else {
            pCommand = Util.removeStr(pCommand, ' ');

            if (equalPart(pCommand, 'update') && update) {
                lRet = true;
                update.get();

                Util.exec(callback, {
                    stdout: Util.addNewLine('update: ok')
                });
            }

            if (Util.strCmp(pCommand, 'exit'))
                process.exit();
        }

        return lRet;
    }

    function onCD(pCommand, currDir, callback) {
        var dir,
            getDir      = WIN32 ? 'chdir' : 'pwd',
            paramDir    = Util.removeStr(pCommand, 'cd ');

        if (equalPart(paramDir, ['/', '~']))
            dir = paramDir;
        else
            dir = path.join(currDir, paramDir);

        exec('cd ' + dir + ' && ' + getDir, function (error, stdout, stderr) {
            var lRet,
                lMsg    = '',
                lError = error || stderr;

            if (lError) {
                lError  = Util.stringifyJSON(lError);
                lMsg    = lError;
            }

            Util.exec(callback, {
                stderr  : lMsg,
                stdout  : Util.rmNewLine(stdout)
            });
        });
    }

    function log(pConnNum, pStr, pType) {
        var lRet,
            lType       = ' ';

        if (pStr) {

            if (pType)
                lType  += pType + ':';

            lRet        = 'client #' + pConnNum + lType + pStr;

            Util.log(lRet);
        }

        return lRet;
    }
})();
<commit_msg>chore(socket) onDisconect -> onDisconnect<commit_after>/* module make possible connectoin thru socket.io on a server */

(function() {
    'use strict';

    var main            = global.cloudcmd.main,
        DIR             = main.DIR,
        SRVDIR          = main.SRVDIR,

        io              = main.require('socket.io'),
        update          = main.srvrequire('update'),

        exec            = main.child_process.exec,
        spawn           = main.child_process.spawn,

        Util            = main.util,
        path            = main.path,
        mainpackage     = main.mainpackage,
        equalPart       = Util.isContainStrAtBegin,
        CLOUDCMD        = mainpackage.name,
        ClientFuncs     = [],
        ClientDirs      = [],
        Clients         = [],
        INFO_LOG_LEVEL  = 2,
        ENV             = process.env,
        WIN32           = main.WIN32,
        ConNum          = 0,
        HELP            = {
            stdout  : CLOUDCMD + ' exit     \n' +
                      CLOUDCMD + ' update   \n',
        },

        /* windows commands thet require
         * unicode charset on locales
         * different then English
         */
        Win32Commands   = ['ASSOC', 'AT', 'ATTRIB', 'BREAK', 'CACLS', 'CALL',
                            'CD', 'CHCP', 'CHDIR', 'CHKDSK', 'CHKNTFS', 'CLS',
                            'CMD', 'COLOR', 'COMP', 'COMPACT', 'CONVERT', 'COPY',
                            'DATE', 'DEL', 'DIR', 'DISKCOMP', 'DISKCOPY', 'DOSKEY',
                            'ECHO', 'ENDLOCAL', 'ERASE', 'EXIT', 'FC', 'FIND',
                            'FINDSTR', 'FOR', 'FORMAT', 'FTYPE', 'GOTO', 'GRAFTABL',
                            'HELP', 'IF', 'LABEL', 'MD', 'MKDIR', 'MODE', 'MORE',
                            'MOVE', 'PATH', 'PAUSE', 'POPD', 'PRINT', 'PROMPT',
                            'PUSHD', 'RD', 'RECOVER', 'REM', 'REN', 'RENAME',
                            'REPLACE', 'RMDIR', 'SET', 'SETLOCAL', 'SHIFT', 'SORT',
                            'START', 'SUBST', 'TIME', 'TITLE', 'TREE', 'TYPE',
                            'VER', 'VERIFY', 'VOL', 'XCOPY'];

    /**
     * function listen on servers port
     * @pServer {Object} started server object
     */
    exports.listen = function(pServer) {
        var ret;

        if (io) {
            io          = io.listen(pServer);

            io.set('log level', INFO_LOG_LEVEL);

            /*
             * on Win7 application is crashing,
             * when options below is used.
             *
             * https://github.com/LearnBoost/socket.io/issues/1314
             *
             */
            if (!WIN32) {
                io.enable('browser client minification');
                io.enable('browser client gzip');
                io.enable('browser client etag');
            }

            io.set('transports', [
              'websocket',
              'htmlfile',
              'xhr-polling',
              'jsonp-polling'
            ]);

            ret = io.sockets.on('connection', function(socket) {
                onConnection(socket, function(json) {
                    var msg = Util.stringifyJSON(json);
                    socket.send(msg);
                });
            });
        }

        return ret;
    };

    function onConnection(socket, callback) {
        var msg, onDisconnect, onMessage;

        ++ConNum;

        if (!Clients[ConNum]) {
            msg = log(ConNum, 'connected\n');

            Util.exec(callback, {
                stdout : msg
            });

            Clients[ConNum]     = true;

            onMessage           = getOnMessage(ConNum, callback);
            onDisconnect        = Util.retFunc(function(conNum) {
                disconnect(connNum);
                socket.removeListener('message', onMessage);
                socket.removeListener('disconnect', onDisconnect);
            }, ConNum);

            socket.on('message', onMessage);
            socket.on('disconnect', onDisconnect);
        } else {
            msg = log(ConNum, ' in use. Reconnecting...\n');

            Util.exec(callback, {
                stdout: msg
            });

            socket.disconnect();
        }
    }

    function disconnect(connNum) {
        OnMessageFuncs  [connNum]  =
        ClientFuncs     [connNum]  = null;

        log(connNum, 'disconnected');
    }

    /**
     * function gets onMessage function
     * that execute needed command
     *
     * @param pConnNum
     * @param callback
     */
    function getOnMessage(pConnNum, callback) {
        return function(pCommand) {
            var lMsg, lWinCommand, lExec_func, firstChar,
                connName,
                lError, lRet, lExecSymbols, isContain,
                dir, options = {};

            dir = ClientDirs[pConnNum];

            if (!dir)
                dir = ClientDirs[pConnNum] = DIR;

            connName = '#' + pConnNum + ': ';
            Util.log(connName + pCommand);

            if (equalPart(pCommand, CLOUDCMD))
                lRet = onCloudCmd(pCommand, callback);
            else if (equalPart(pCommand, 'cd '))  {
                lRet = true;

                onCD(pCommand, dir, function(json) {
                    var error   = json.stderr,
                        stdout  = json.stdout;

                    if (error)
                        Util.exec(callback, json);
                    else
                        ClientDirs[pConnNum] = stdout;
                });
            }

            if (!lRet) {
                /* if we on windows and command is build in
                 * change code page to unicode becouse
                 * windows use unicode on non English versions
                 */
                if (WIN32) {
                    lWinCommand     = pCommand.toUpperCase();

                    if (Win32Commands.indexOf(lWinCommand) >= 0)
                        pCommand    = 'chcp 65001 |' + pCommand;
                }

                if (!ClientFuncs[pConnNum])
                    ClientFuncs[pConnNum] = getExec(function(json, pError, pStderr) {
                        log(pConnNum, pError, 'error');
                        log(pConnNum, pStderr, 'stderror');

                        Util.exec(callback, json);
                    });

                lExec_func      = ClientFuncs[pConnNum];
                lExecSymbols    = ['*', '&', '{', '}', '|', '\'', '"'];
                isContain       = Util.isContainStr(pCommand, lExecSymbols);
                firstChar       = pCommand[0];
                options.cwd     = dir;

                if (firstChar === '#') {
                    pCommand    = pCommand.slice(1);
                    pCommand    = connName + pCommand;
                    pCommand    = Util.addNewLine(pCommand);

                    lMsg    = Util.stringifyJSON({
                        stdout: pCommand
                    });

                    io.sockets.emit('message', lMsg);
                } else if (WIN32 || firstChar === ' ' || isContain)
                    exec(pCommand, options, lExec_func);
                else
                    getSpawn(pCommand, options, callback);
            }
        };
    }

    /**
     * function send result of command to client
     * @param callback
     */
    function getExec(callback) {
        return function(pError, pStdout, pStderr) {
            var lErrorStr, lExecStr, lExec,
                lError          = pStderr || pError;

            if (lError) {
                if (Util.isString(lError))
                    lErrorStr   = lError;
                else
                    lErrorStr   = lError.toString();

                lErrorStr = Util.addNewLine(lErrorStr);
            }

            lExec               = {
                stdout : pStdout,
                stderr : lErrorStr || lError
            };

            Util.exec(callback, lExec, pError, pStderr);
        };
    }

    function getSpawn(pCommand, options, callback) {
        var send, cmd, error,
            args    = pCommand.split(' ');

        pCommand    = args.shift();

        error       = Util.tryCatchLog(function() {
            cmd = spawn(pCommand, args, options);
        });

        if (!cmd)
            send(error + '', null);
        else {
            send        = function(error, data) {
                var exec    = {
                        stderr: error,
                        stdout: data
                    };

                Util.exec(callback, exec);
            };

            cmd.stdout.on('data', function(data) {
                send(null, data + '');
            });

            cmd.stderr.on('data', function(error) {
                send(error + '', null);
            });

            cmd.on('error', Util.retFalse);
            cmd.on('close', function (code) {
                cmd = null;
            });
        }
    }

    function onCloudCmd(pCommand, callback) {
        var lRet;

        pCommand = Util.removeStr(pCommand, CLOUDCMD);

        if (!equalPart(pCommand, ' ')) {
            lRet = true;
            Util.exec(callback, HELP);
        }
        else {
            pCommand = Util.removeStr(pCommand, ' ');

            if (equalPart(pCommand, 'update') && update) {
                lRet = true;
                update.get();

                Util.exec(callback, {
                    stdout: Util.addNewLine('update: ok')
                });
            }

            if (Util.strCmp(pCommand, 'exit'))
                process.exit();
        }

        return lRet;
    }

    function onCD(pCommand, currDir, callback) {
        var dir,
            getDir      = WIN32 ? 'chdir' : 'pwd',
            paramDir    = Util.removeStr(pCommand, 'cd ');

        if (equalPart(paramDir, ['/', '~']))
            dir = paramDir;
        else
            dir = path.join(currDir, paramDir);

        exec('cd ' + dir + ' && ' + getDir, function (error, stdout, stderr) {
            var lRet,
                lMsg    = '',
                lError = error || stderr;

            if (lError) {
                lError  = Util.stringifyJSON(lError);
                lMsg    = lError;
            }

            Util.exec(callback, {
                stderr  : lMsg,
                stdout  : Util.rmNewLine(stdout)
            });
        });
    }

    function log(pConnNum, pStr, pType) {
        var lRet,
            lType       = ' ';

        if (pStr) {

            if (pType)
                lType  += pType + ':';

            lRet        = 'client #' + pConnNum + lType + pStr;

            Util.log(lRet);
        }

        return lRet;
    }
})();
<|endoftext|>


<commit_before>/**
 * @depend ../sinon.js
 * @depend collection.js
 * @depend util/fake_timers.js
 * @depend util/fake_server_with_clock.js
 */
/*jslint indent: 2, eqeqeq: false, onevar: false*/
/*global sinon*/
/**
 * Manages fake collections as well as fake utilities such as Sinon's
 * timers and fake XHR implementation in one convenient object.
 *
 * @author Christian Johansen (christian@cjohansen.no)
 * @license BSD
 *
 * Copyright (c) 2010 Christian Johansen
 */
(function () {
  if (typeof sinon == "undefined") {
    return;
  }

  sinon.sandbox = sinon.extend(sinon.create(sinon.collection), {
    useFakeTimers: function useFakeTimers() {
      this.clock = sinon.useFakeTimers.apply(sinon, arguments);

      return this.add(this.clock);
    },

    serverPrototype: sinon.fakeServer,

    useFakeServer: function useFakeServer() {
      this.server = (this.serverPrototype || sinon.fakeServer).create();

      return this.add(this.server);
    },

    inject: function (obj) {
      sinon.collection.inject.call(this, obj);

      if (this.clock) {
        obj.clock = this.clock;
      }

      if (this.server) {
        obj.server = this.server;
        obj.requests = this.server.requests;
      }

      return obj;
    },

    create: function () {
      return sinon.create(sinon.sandbox);
    }
  });

  sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer;
}());
<commit_msg>Node.js friendly sandbox<commit_after>/**
 * @depend ../sinon.js
 * @depend collection.js
 * @depend util/fake_timers.js
 * @depend util/fake_server_with_clock.js
 */
/*jslint indent: 2, eqeqeq: false, onevar: false*/
/*global sinon*/
/**
 * Manages fake collections as well as fake utilities such as Sinon's
 * timers and fake XHR implementation in one convenient object.
 *
 * @author Christian Johansen (christian@cjohansen.no)
 * @license BSD
 *
 * Copyright (c) 2010 Christian Johansen
 */
if (typeof require == "function") {
  var sinon = require("sinon");
}

sinon.sandbox = sinon.extend(sinon.create(sinon.collection), {
  useFakeTimers: function useFakeTimers() {
    this.clock = sinon.useFakeTimers.apply(sinon, arguments);

    return this.add(this.clock);
  },

  serverPrototype: sinon.fakeServer,

  useFakeServer: function useFakeServer() {
    this.server = (this.serverPrototype || sinon.fakeServer).create();

    return this.add(this.server);
  },

  inject: function (obj) {
    sinon.collection.inject.call(this, obj);

    if (this.clock) {
      obj.clock = this.clock;
    }

    if (this.server) {
      obj.server = this.server;
      obj.requests = this.server.requests;
    }

    return obj;
  },

  create: function () {
    return sinon.create(sinon.sandbox);
  }
});

sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer;

if (typeof module != "undefined") {
  module.exports = sinon.sandbox;
}<|endoftext|>


<commit_before>/* global Log, Config */
'use strict';

const EventEmitter = require('events').EventEmitter;
const consul = require('consul');

const DEFAULT_AGENT_ADDRESS = '127.0.0.1';
const DEFAULT_AGENT_PORT = 8500;

class ConquesoCompatHealthParser {
  constructor() {
    this.properties = {};
  }

  /**
   * Parse data from the Consul health watcher
   *
   * The data returned from Consul looks like this:
   * [{
   *   "Node": {
   *     "Address": "127.0.0.1"
   *   },
   *   "Service": {
   *     "Address": "10.0.0.0"
   *   }
   * }]
   *
   * @param {Array} data
   * @param {String} cluster
   */
  update(data, cluster) {
    const properties = {};
    const addresses = [];

    data.forEach((info) => {
      // Prefer the service address, not the Consul agent address.
      if (info.Service && info.Service.Address) {
        addresses.push(info.Service.Address);
      } else if (info.Node && info.Node.Address) {
        addresses.push(info.Node.Address);
      }
    });

    properties.addresses = addresses.sort();
    properties.cluster = cluster;

    this.properties = properties;
  }
}

class ConquesoCompatServiceParser {
  constructor() {
    this.properties = {};
  }

  /**
   * Parse data from the Consul service list watcher
   *
   * The data returned from Consul looks like this:
   * {
   *   "service-name": ['tag', 'tag'],
   *   "other-service-name": ['other-tag', 'other-tag'],
   * }
   *
   * @param {Object} data
   * @param {Function} callback Optional function to filter the
   */
  update(data, callback) {
    const services = Object.keys(data);
    const watcherInfo = [];

    services.forEach((service) => {
      const tags = data[service];

      tags.forEach((tag) => {
        watcherInfo.push({
          name: `${service}-${tag}`,
          cluster: tag,
          options: {
            passing: true,
            service,
            tag
          }
        });
      });

      if (tags.length <= 0) {
        watcherInfo.push({
          name: service,
          cluster: service,
          options: {
            passing: true,
            service
          }
        });
      }
    });

    this.properties.watcherInfo = watcherInfo.filter(callback);
  }
}

class Consul extends EventEmitter {
  /**
   * Creates a new instance of a Consul souce plugin
   * See the Consul#configure method for a list of valid options
   *
   * @param {Object} options  Options that can be set for the plugin
   */
  constructor(options) {
    super();
    this.configure(options);
    this.clear();

    this.type = 'consul';
    this.name = 'consul';
    this._okay = false;
    this._updated = null;
    this._serviceParser = new ConquesoCompatServiceParser();
  }

  /**
   * Start watching the Consul agent for changes
   */
  initialize() {
    if (this._consul) {
      return;
    }

    Log.info(`Initializing ${this.type} source ${this.name}`, {
      source: this.name,
      type: this.type
    });

    this._consul = consul({
      host: this.host,
      port: this.port,
      secure: this.secure
    });

    this._serviceWatcher = this._consul.watch({
      method: this._consul.catalog.service.list
    });

    this._serviceWatcher.on('error', (error) => {
      this._error(error);
    });

    this._serviceWatcher.on('change', (serviceListData) => {
      this._onServiceListChange(serviceListData);
    });

    this._okay = true;
    this.emit('startup');
  }

  /**
   * Stop watching the Consul agent for changes
   */
  shutdown() {
    this._shutdownHealthWatchers();
    this._shutdownServiceWatcher();
    delete this._consul;

    Log.info(`Shutting down ${this.type} source ${this.name}`, {
      source: this.name,
      type: this.type
    });

    this._okay = false;
    this.emit('shutdown');
  }

  /**
   * Return an object describing the Consul plugin's current status
   *
   * @return {Object}
   */
  status() {
    return {
      ok: this._okay,
      updated: this._updated,
      running: this._running
    };
  }

  /**
   * Configures the Consul plugin
   *
   * The following options are valid:
   * - host (String, default 127.0.0.1): Consul agent's address
   * - port (Number, default 8500) Consul agent's HTTP(S) port
   * - secure (Boolean, default: true): enable HTTPS
   *
   * @param {Object} options  Options that can be set for the plugin
   */
  configure(options) {
    const params = options || {};

    this.host = params.host || DEFAULT_AGENT_ADDRESS;
    this.port = params.port || DEFAULT_AGENT_PORT;
    this.secure = params.secure !== false;
  }

  /**
   * Clear the underlying properties object
   */
  clear() {
    this.properties = {};
    this.properties.consul = {};
  }

  /**
   * Check the status of the Consul watcher
   *
   * @return {Boolean} True if the pooling loop is running
   * @private
   */
  get _running() {
    return !!this._consul;
  }

  /* Process updates from Consul's service/list API.
   *
   * The data returned from Consul looks like this:
   * [
   *   "consul": [],
   *   "service": ["production", "develop"]
   * ]
   *
   * @param {Object} data  The JSON data from the service/list API.
   * @private
   */
  _onServiceListChange(data) {
    this._serviceParser.update(data, (info) => !this._hasHealthWatcher(info.name));
    this._serviceParser.properties.watcherInfo.forEach((info) => {
      const watcher = this._consul.watch({
        method: this._consul.health.service,
        options: info.options
      });

      watcher.on('error', (error) => {
        this._error(error);
      });

      watcher.on('change', (healthServiceData) => {
        this._onHealthServiceChange(info.name, info.cluster, healthServiceData);
      });

      this._registerHealthWatcher(info.name, watcher);
    });

    this._okay = true;
  }

  /**
   * Process updates from Consul's health/service API.
   *
   * @param {String} name     The name of the watcher
   * @param {String} cluster  The name of the clustered service
   * @param {Array} data      The JSON data from the health/service API
   * @private
   */
  _onHealthServiceChange(name, cluster, data) {
    if (data.length > 0) {
      this._healthWatchers[name].parser.update(data, cluster);

      if (!this.properties.consul[name]) {
        this.properties.consul[name] = {};
      }
      this.properties.consul[name] = this._healthWatchers[name].parser.properties;
    } else {
      // Empty data means the service has been deregistered.
      this._unregisterHealthWatcher(name);
      delete this.properties.consul[name];
    }

    this._okay = true;
    this._updated = new Date();

    Log.info(`Updated source ${this.name}`, {
      source: this.name,
      type: this.type,
      name,
      cluster
    });

    this.emit('update', this);
  }

  /**
   * Checks if there's alread a watcher for the Consul health/service API
   *
   * @param {String}    name The name of the watcher to check
   * @return {Boolean}  true if there's a watcher with the name; false otherwise
   * @private
   */
  _hasHealthWatcher(name) {
    return this._healthWatchers && this._healthWatchers[name] && this._healthWatchers[name].watcher;
  }

  /**
   * Register a watcher that's tracking a Consul health/service API
   * @param {String} name           The name of the watcher
   * @param {Consul.Watch} watcher  The watcher returned by `consul.watch()`
   * @private
   */
  _registerHealthWatcher(name, watcher) {
    if (!this._healthWatchers) {
      this._healthWatchers = {};
    }
    this._healthWatchers[name] = {
      watcher,
      parser: new ConquesoCompatHealthParser()
    };
  }

  /**
   * Unregister a watcher that's tracking a Consul health/service API
   * @param {String} name  The name of the watcher
   * @private
   */
  _unregisterHealthWatcher(name) {
    if (this._hasHealthWatcher(name)) {
      this._healthWatchers[name].watcher.end();
      delete this._healthWatchers[name];
    }
  }

  /**
   * Shut down everything tracking the Consul health/service API.
   * @private
   */
  _shutdownHealthWatchers() {
    /* eslint-disable guard-for-in */
    // There's already a guard in Consul#_unregisterHealthWatchers
    for (const name in this._healthWatchers) {
      this._unregisterHealthWatcher(name);
    }

    /* eslint-enable guard-for-in */
    delete this._healthWatchers;
  }

  /**
   * Shut down everything tracking the Consul service/list API.
   * @private
   */
  _shutdownServiceWatcher() {
    if (this._serviceWatcher) {
      this._serviceWatcher.end();
    }
    delete this._serviceWatcher;
  }

  /**
   * Generic handler for all errors
   *
   * @param {Error} error
   * @private
   */
  _error(error) {
    this._okay = false;

    Log.error(error, {
      source: this.name,
      type: this.type
    });

    if (this.listeners('error').length > 0) {
      this.emit('error', error);
    }
  }
}

module.exports = Consul;
<commit_msg>Added guard around callback existence to make it optional. We don't want to enforce filtering behavior if there's no need to filter.<commit_after>/* global Log, Config */
'use strict';

const EventEmitter = require('events').EventEmitter;
const consul = require('consul');

const DEFAULT_AGENT_ADDRESS = '127.0.0.1';
const DEFAULT_AGENT_PORT = 8500;

class ConquesoCompatHealthParser {
  constructor() {
    this.properties = {};
  }

  /**
   * Parse data from the Consul health watcher
   *
   * The data returned from Consul looks like this:
   * [{
   *   "Node": {
   *     "Address": "127.0.0.1"
   *   },
   *   "Service": {
   *     "Address": "10.0.0.0"
   *   }
   * }]
   *
   * @param {Array} data
   * @param {String} cluster
   */
  update(data, cluster) {
    const properties = {};
    const addresses = [];

    data.forEach((info) => {
      // Prefer the service address, not the Consul agent address.
      if (info.Service && info.Service.Address) {
        addresses.push(info.Service.Address);
      } else if (info.Node && info.Node.Address) {
        addresses.push(info.Node.Address);
      }
    });

    properties.addresses = addresses.sort();
    properties.cluster = cluster;

    this.properties = properties;
  }
}

class ConquesoCompatServiceParser {
  constructor() {
    this.properties = {};
  }

  /**
   * Parse data from the Consul service list watcher
   *
   * The data returned from Consul looks like this:
   * {
   *   "service-name": ['tag', 'tag'],
   *   "other-service-name": ['other-tag', 'other-tag'],
   * }
   *
   * @param {Object} data
   * @param {Function} callback Optional function to filter the
   */
  update(data, callback) {
    const services = Object.keys(data);
    let watcherInfo = [];

    services.forEach((service) => {
      const tags = data[service];

      tags.forEach((tag) => {
        watcherInfo.push({
          name: `${service}-${tag}`,
          cluster: tag,
          options: {
            passing: true,
            service,
            tag
          }
        });
      });

      if (tags.length <= 0) {
        watcherInfo.push({
          name: service,
          cluster: service,
          options: {
            passing: true,
            service
          }
        });
      }
    });

    if (callback && callback instanceof Function) {
      watcherInfo = watcherInfo.filter(callback);
    }
    this.properties.watcherInfo = watcherInfo;
  }
}

class Consul extends EventEmitter {
  /**
   * Creates a new instance of a Consul souce plugin
   * See the Consul#configure method for a list of valid options
   *
   * @param {Object} options  Options that can be set for the plugin
   */
  constructor(options) {
    super();
    this.configure(options);
    this.clear();

    this.type = 'consul';
    this.name = 'consul';
    this._okay = false;
    this._updated = null;
    this._serviceParser = new ConquesoCompatServiceParser();
  }

  /**
   * Start watching the Consul agent for changes
   */
  initialize() {
    if (this._consul) {
      return;
    }

    Log.info(`Initializing ${this.type} source ${this.name}`, {
      source: this.name,
      type: this.type
    });

    this._consul = consul({
      host: this.host,
      port: this.port,
      secure: this.secure
    });

    this._serviceWatcher = this._consul.watch({
      method: this._consul.catalog.service.list
    });

    this._serviceWatcher.on('error', (error) => {
      this._error(error);
    });

    this._serviceWatcher.on('change', (serviceListData) => {
      this._onServiceListChange(serviceListData);
    });

    this._okay = true;
    this.emit('startup');
  }

  /**
   * Stop watching the Consul agent for changes
   */
  shutdown() {
    this._shutdownHealthWatchers();
    this._shutdownServiceWatcher();
    delete this._consul;

    Log.info(`Shutting down ${this.type} source ${this.name}`, {
      source: this.name,
      type: this.type
    });

    this._okay = false;
    this.emit('shutdown');
  }

  /**
   * Return an object describing the Consul plugin's current status
   *
   * @return {Object}
   */
  status() {
    return {
      ok: this._okay,
      updated: this._updated,
      running: this._running
    };
  }

  /**
   * Configures the Consul plugin
   *
   * The following options are valid:
   * - host (String, default 127.0.0.1): Consul agent's address
   * - port (Number, default 8500) Consul agent's HTTP(S) port
   * - secure (Boolean, default: true): enable HTTPS
   *
   * @param {Object} options  Options that can be set for the plugin
   */
  configure(options) {
    const params = options || {};

    this.host = params.host || DEFAULT_AGENT_ADDRESS;
    this.port = params.port || DEFAULT_AGENT_PORT;
    this.secure = params.secure !== false;
  }

  /**
   * Clear the underlying properties object
   */
  clear() {
    this.properties = {};
    this.properties.consul = {};
  }

  /**
   * Check the status of the Consul watcher
   *
   * @return {Boolean} True if the pooling loop is running
   * @private
   */
  get _running() {
    return !!this._consul;
  }

  /* Process updates from Consul's service/list API.
   *
   * The data returned from Consul looks like this:
   * [
   *   "consul": [],
   *   "service": ["production", "develop"]
   * ]
   *
   * @param {Object} data  The JSON data from the service/list API.
   * @private
   */
  _onServiceListChange(data) {
    this._serviceParser.update(data, (info) => !this._hasHealthWatcher(info.name));
    this._serviceParser.properties.watcherInfo.forEach((info) => {
      const watcher = this._consul.watch({
        method: this._consul.health.service,
        options: info.options
      });

      watcher.on('error', (error) => {
        this._error(error);
      });

      watcher.on('change', (healthServiceData) => {
        this._onHealthServiceChange(info.name, info.cluster, healthServiceData);
      });

      this._registerHealthWatcher(info.name, watcher);
    });

    this._okay = true;
  }

  /**
   * Process updates from Consul's health/service API.
   *
   * @param {String} name     The name of the watcher
   * @param {String} cluster  The name of the clustered service
   * @param {Array} data      The JSON data from the health/service API
   * @private
   */
  _onHealthServiceChange(name, cluster, data) {
    if (data.length > 0) {
      this._healthWatchers[name].parser.update(data, cluster);

      if (!this.properties.consul[name]) {
        this.properties.consul[name] = {};
      }
      this.properties.consul[name] = this._healthWatchers[name].parser.properties;
    } else {
      // Empty data means the service has been deregistered.
      this._unregisterHealthWatcher(name);
      delete this.properties.consul[name];
    }

    this._okay = true;
    this._updated = new Date();

    Log.info(`Updated source ${this.name}`, {
      source: this.name,
      type: this.type,
      name,
      cluster
    });

    this.emit('update', this);
  }

  /**
   * Checks if there's alread a watcher for the Consul health/service API
   *
   * @param {String}    name The name of the watcher to check
   * @return {Boolean}  true if there's a watcher with the name; false otherwise
   * @private
   */
  _hasHealthWatcher(name) {
    return this._healthWatchers && this._healthWatchers[name] && this._healthWatchers[name].watcher;
  }

  /**
   * Register a watcher that's tracking a Consul health/service API
   * @param {String} name           The name of the watcher
   * @param {Consul.Watch} watcher  The watcher returned by `consul.watch()`
   * @private
   */
  _registerHealthWatcher(name, watcher) {
    if (!this._healthWatchers) {
      this._healthWatchers = {};
    }
    this._healthWatchers[name] = {
      watcher,
      parser: new ConquesoCompatHealthParser()
    };
  }

  /**
   * Unregister a watcher that's tracking a Consul health/service API
   * @param {String} name  The name of the watcher
   * @private
   */
  _unregisterHealthWatcher(name) {
    if (this._hasHealthWatcher(name)) {
      this._healthWatchers[name].watcher.end();
      delete this._healthWatchers[name];
    }
  }

  /**
   * Shut down everything tracking the Consul health/service API.
   * @private
   */
  _shutdownHealthWatchers() {
    /* eslint-disable guard-for-in */
    // There's already a guard in Consul#_unregisterHealthWatchers
    for (const name in this._healthWatchers) {
      this._unregisterHealthWatcher(name);
    }

    /* eslint-enable guard-for-in */
    delete this._healthWatchers;
  }

  /**
   * Shut down everything tracking the Consul service/list API.
   * @private
   */
  _shutdownServiceWatcher() {
    if (this._serviceWatcher) {
      this._serviceWatcher.end();
    }
    delete this._serviceWatcher;
  }

  /**
   * Generic handler for all errors
   *
   * @param {Error} error
   * @private
   */
  _error(error) {
    this._okay = false;

    Log.error(error, {
      source: this.name,
      type: this.type
    });

    if (this.listeners('error').length > 0) {
      this.emit('error', error);
    }
  }
}

module.exports = Consul;
<|endoftext|>


<commit_before>/**
 * Creates data for a spectrum chart
 * Created by kc on 21.03.16.
 */

const BigNumber = require('bignumber.js');
const GaussianElimination = require('na-gaussian-elimination');
const zero = new BigNumber(0);
GaussianElimination.defaultOptions.zero = zero;

/**
 * Calculates a spectrum chart
 * @param options see below
 *
 * Options parameters
 *   .frequency:     Center frequency of the signal
 *   .bandwith:      Bandwith of the spectrum
 *   .amplitude:     Level of the signal (should be positive for nice graphs)
 *   .samples:       Number of samples to take (optional, default: 10)
 *
 *   .freqColName:   Name of the frequency (x) column
 *   .amplColName:   Name of the data (y) column
 *
 *  Returns:
 *    An object with two arrays, ready to be used in the D3 charts
 *
 *    Sample:
 *    { x:
         [ 'x1',
           2401,
           2403.2,
           2405.4,
           2407.6,
           2409.8,
           2412,
           2414.2,
           2416.4,
           2418.6,
           2420.8,
           2423 ],
        data: [ 'data1', -0, 7.2, 12.8, 16.8, 19.2, 20, 19.2, 16.8, 12.8, 7.2, 0 ] }
 */
module.exports = function (options) {
  console.log('New chart', options);

  var x;
  var lowerX = options.frequency - options.bandwith / 2;
  var upperX = options.frequency + options.bandwith / 2;
  var samples = options.samples || 10;

  var gaussianElimination = new GaussianElimination();

  // Create the matrix in order to get a,b and c of "a*x^2+bx+c" where x are the known points of the graph
  var matrix = [
    [new BigNumber(Math.pow(lowerX, 2)), new BigNumber(lowerX), new BigNumber(1)],
    [new BigNumber(Math.pow(options.frequency, 2)), new BigNumber(options.frequency), new BigNumber(1)],
    [new BigNumber(Math.pow(upperX, 2)), new BigNumber(upperX), new BigNumber(1)]
  ];
  var result = [
    zero, new BigNumber(options.amplitude), zero
  ];

  var system = gaussianElimination.solve(matrix, result);

  var retVal = {
    x: [options.freqColName],
    data: [options.amplColName]
  };

  for (x = lowerX; x < upperX; x += (upperX - lowerX) / samples) {
    var y = system.solution[0].toNumber() * Math.pow(x, 2) + system.solution[1].toNumber() * x + system.solution[2].toNumber();

    retVal.x.push(Math.round(x * 100) / 100);
    retVal.data.push(Math.round(y * 100) / 100);
  }

  return retVal;
};
<commit_msg>No negative charts<commit_after>/**
 * Creates data for a spectrum chart
 * Created by kc on 21.03.16.
 */

const BigNumber = require('bignumber.js');
const GaussianElimination = require('na-gaussian-elimination');
const zero = new BigNumber(0);
GaussianElimination.defaultOptions.zero = zero;

/**
 * Calculates a spectrum chart
 * @param options see below
 *
 * Options parameters
 *   .frequency:     Center frequency of the signal
 *   .bandwith:      Bandwith of the spectrum
 *   .amplitude:     Level of the signal (should be positive for nice graphs)
 *   .samples:       Number of samples to take (optional, default: 10)
 *
 *   .freqColName:   Name of the frequency (x) column
 *   .amplColName:   Name of the data (y) column
 *
 *  Returns:
 *    An object with two arrays, ready to be used in the D3 charts
 *
 *    Sample:
 *    { x:
         [ 'x1',
           2401,
           2403.2,
           2405.4,
           2407.6,
           2409.8,
           2412,
           2414.2,
           2416.4,
           2418.6,
           2420.8,
           2423 ],
        data: [ 'data1', -0, 7.2, 12.8, 16.8, 19.2, 20, 19.2, 16.8, 12.8, 7.2, 0 ] }
 */
module.exports = function (options) {
  console.log('New chart', options);

  var x;
  var lowerX = options.frequency - options.bandwith / 2;
  var upperX = options.frequency + options.bandwith / 2;
  var samples = options.samples || 10;

  var gaussianElimination = new GaussianElimination();

  // Create the matrix in order to get a,b and c of "a*x^2+bx+c" where x are the known points of the graph
  var matrix = [
    [new BigNumber(Math.pow(lowerX, 2)), new BigNumber(lowerX), new BigNumber(1)],
    [new BigNumber(Math.pow(options.frequency, 2)), new BigNumber(options.frequency), new BigNumber(1)],
    [new BigNumber(Math.pow(upperX, 2)), new BigNumber(upperX), new BigNumber(1)]
  ];
  var result = [
    zero, new BigNumber(options.amplitude), zero
  ];

  var system = gaussianElimination.solve(matrix, result);

  var retVal = {
    x: [options.freqColName],
    data: [options.amplColName]
  };

  for (x = lowerX; x < upperX; x += (upperX - lowerX) / samples) {
    var y = Math.abs(system.solution[0].toNumber() * Math.pow(x, 2) + system.solution[1].toNumber() * x + system.solution[2].toNumber());

    retVal.x.push(Math.round(x * 100) / 100);
    retVal.data.push(Math.round(y * 100) / 100);
  }

  return retVal;
};
<|endoftext|>


<commit_before>var conn = require('../common/connection.js'),
    util = require('util'),
    stream = require('stream'),
    events = require('events');

var SCOPES = [
    'https://www.googleapis.com/auth/devstorage.full_control'];

var STORAGE_BASE_URL = 'https://www.googleapis.com/storage/v1/b';
    STORAGE_UPLOAD_BASE_URL = 'https://www.googleapis.com/upload/storage/v1/b';

function ReadStream(conn, url) {
  var that = this;
  events.EventEmitter.call(this);

  this.conn = conn;
  this.url = url;

  this.remoteStream = null;
  this.errored = false;
}

util.inherits(ReadStream, events.EventEmitter);

ReadStream.prototype.open = function() {
  var that = this;
  this.conn.createAuthorizedReq({ uri: this.url }, function(err, req) {
    if (err) {
      that.emit('error', err);
      return;
    }
    that.remoteStream = that.conn.requester(req);
    that.remoteStream.on('response', function(resp) {
      if (resp.statusCode != 200) {
        that.errored = true;
        // TODO(jbd): Emit error
      }
    });
    that.remoteStream.on('complete', function(resp) {
      that.emit('complete', resp);
    });
    that.emit('readable');
  });
};

ReadStream.prototype.pipe = function(dest, opts) {
  var that = this;
  if (!that.remoteStream) {
    return that.once('readable', function() {
      that.pipe(dest, opts);
    });
  }
  // Register an on-data listener instead of
  // piping, so we can avoid writing if request
  // ends up with a non-200 response.
  that.remoteStream.on('data', function(data) {
    if(!that.errored) {
      that.emit('data', data);
      dest.write(data);
    }
  });
  return dest;
};

function WriteStream(conn, uploadUrl, name, metadata) {
  events.EventEmitter.call(this);
  var that = this;

  this.conn = conn;
  this.url = uploadUrl;
  this.name = name;
  this.metadata = metadata || {};
  this.contentType = this.metadata.contentType || 'application/octet-stream';
  delete this.metadata.contentType;

  this.remoteStream = null;
  this.metadataWritten = false;

  this.open();
}

util.inherits(WriteStream, events.EventEmitter);

WriteStream.prototype.open = function() {
  var that = this;
  this.conn.createAuthorizedReq({
    method: 'POST',
    uri: this.url,
    qs: { uploadType: 'media', name: this.name },
    headers: {
      'Content-Type': this.contentType
    }
  }, function(err, req) {
    if (err) {
      that.emit('error', err);
      return;
    }
    that.remoteStream = that.conn.requester(req);
    that.remoteStream.on('response', function(resp) {
      that.emit('response', resp);
    });
    that.remoteStream.on('complete', function(resp) {
      that.emit('complete', resp);
    });
    that.emit('writable');
  });
};

WriteStream.prototype.write = function(chunk, encoding, callback) {
  this.remoteStream.write(chunk, encoding, callback);
};

WriteStream.prototype.end = function(chunk, encoding, callback) {
  this.remoteStream.end(chunk, encoding, callback);
};

function Bucket(opts) {
  this.bucketName = opts.bucketName;
  this.conn = new conn.Connection({
    email: opts.email,
    privateKey: opts.pemFilePath,
    scopes: SCOPES
  });
}

Bucket.prototype.list = function(q, callback) {
  // TODO(jbd): Make q optional.
  this.makeReq('get', 'o', q, null, function(err, resp) {
    if (err) {
      return callback(err);
    }
    var nextQuery = null;
    if (resp.nextPageToken) {
      nextQuery = q;
      nextQuery.pageToken = nextPageToken;
    }
    callback(null, resp.items, nextQuery);
  });
};

Bucket.prototype.get = function(name, callback) {
  this.makeReq('get', ['o', name].join('/'), null, null, callback);
};

Bucket.prototype.copy = function(srcName, destName, acl, opt_callback) {
  // TODO(jbd): Make acl optional.
  var path = ['o', srcName, 'copyTo', 'b', this.bucketName, destName].join('/');
  this.makeReq('post', path, { destinationPredefinedAcl: acl }, null, callback);
};

Bucket.prototype.del = function(name, callback) {
  this.makeReq('delete', ['o', name].join('/'), null, null, callback);
};

Bucket.prototype.createReadStream = function(name) {
  return new ReadStream(this.conn, STORAGE_BASE_URL + '/' + [this.bucketName, 'o', name].join('/'));
};

Bucket.prototype.createWriteStream = function(name, metadata) {
  return new WriteStream(this.conn, STORAGE_UPLOAD_BASE_URL + '/' + this.bucketName + '/o', name, metadata);
};

Bucket.prototype.makeReq = function(method, path, q, body, callback) {
  this.conn.req({
    method: method,
    query: q,
    uri: STORAGE_BASE_URL + '/' + [this.bucketName, path].join('/'),
    json: body || true
  }, function(err, res, body) {
    if (body && body.error) {
      return callback(body.error);
    }
    callback(err, body);
  });
};

module.exports.Bucket = Bucket;

<commit_msg>Avoid writable streaming API for bucket operations.<commit_after>/**
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var conn = require('../common/connection.js'),
    util = require('util'),
    events = require('events'),
    fs = require('fs');

var SCOPES = [
    'https://www.googleapis.com/auth/devstorage.full_control'];

var STORAGE_BASE_URL = 'https://www.googleapis.com/storage/v1/b';
    STORAGE_UPLOAD_BASE_URL = 'https://www.googleapis.com/upload/storage/v1/b';

var reqStreamToCallback = function(st, callback) {
  st.on('error', function(err) {
    callback(err);
  });
  st.on('complete', function(resp) {
    // TODO(jbd): Buffer the response to pass the resp body
    // to the callback.
    if (resp.statusCode < 200 || resp.statusCode > 299) {
      return callback(new Error('error during request, statusCode: ' + resp.statusCode));
    }
    callback(null, resp);
  });
};

function ReadStream(conn, url) {
  var that = this;
  events.EventEmitter.call(this);

  this.conn = conn;
  this.url = url;

  this.remoteStream = null;
  this.errored = false;
}

util.inherits(ReadStream, events.EventEmitter);

ReadStream.prototype.open = function() {
  var that = this;
  this.conn.createAuthorizedReq({ uri: this.url }, function(err, req) {
    if (err) {
      that.emit('error', err);
      return;
    }
    that.remoteStream = that.conn.requester(req);
    that.remoteStream.on('response', function(resp) {
      if (resp.statusCode != 200) {
        that.errored = true;
        // TODO(jbd): Emit error
      }
    });
    that.remoteStream.on('complete', function(resp) {
      that.emit('complete', resp);
    });
    that.emit('readable');
  });
};

ReadStream.prototype.pipe = function(dest, opts) {
  var that = this;
  if (!that.remoteStream) {
    return that.once('readable', function() {
      that.pipe(dest, opts);
    });
  }
  // Register an on-data listener instead of
  // piping, so we can avoid writing if request
  // ends up with a non-200 response.
  that.remoteStream.on('data', function(data) {
    if(!that.errored) {
      that.emit('data', data);
      dest.write(data);
    }
  });
  return dest;
};

function Bucket(opts) {
  this.bucketName = opts.bucketName;
  this.conn = new conn.Connection({
    email: opts.email,
    privateKey: opts.pemFilePath,
    scopes: SCOPES
  });
}

Bucket.prototype.list = function(q, callback) {
  // TODO(jbd): Make q optional.
  this.makeReq('get', 'o', q, true, function(err, resp) {
    if (err) {
      return callback(err);
    }
    var nextQuery = null;
    if (resp.nextPageToken) {
      nextQuery = q;
      nextQuery.pageToken = nextPageToken;
    }
    callback(null, resp.items, nextQuery);
  });
};

// TODO: Bucket should implement writable Streaming API.

Bucket.prototype.stat = function(name, callback) {
  this.makeReq('get', ['o', name].join('/'), null, true, callback);
};

Bucket.prototype.copy = function(name, metadata, opt_callback) {
  if (!metadata.bucketName) {
    metadata.bucketName = this.bucketName;
  }
  var path = ['o', name, 'copyTo', 'b', metadata.bucketName, metadata.name].join('/');
  this.makeReq('post', path, null, metadata, callback);
};

Bucket.prototype.remove = function(name, callback) {
  this.makeReq('delete', ['o', name].join('/'), null, true, callback);
};

Bucket.prototype.writeFile = function(name, filename, metadata, callback) {
  // TODO(jbd): metadata should be optional.
  var that = this;
  var uri = STORAGE_UPLOAD_BASE_URL + '/' + this.bucketName + '/o';
  this.conn.createAuthorizedReq({
    method: 'POST',
    uri: uri,
    qs: { name: name, uploadType: 'media' }
  }, function(err, req) {
    if (err) {
      callback && callback(err);
      return;
    }
    req.headers['Content-Type'] = metadata.contentType || 'text/plain';
    var remoteStream = that.conn.requester(req);
    var fsStream = fs.createReadStream(filename);
    reqStreamToCallback(fsStream, callback);
    fsStream.pipe(remoteStream);
    reqStreamToCallback(remoteStream, callback);
  });
};

Bucket.prototype.writeBuffer = function(name, buffer, metadata, callback) {
  // TODO(jbd): metadata should be optional.
  var that = this;
  var uri = STORAGE_UPLOAD_BASE_URL + '/' + this.bucketName + '/o';
  this.conn.createAuthorizedReq({
    method: 'POST',
    uri: uri,
    qs: { name: name, uploadType: 'media' }
  }, function(err, req) {
    if (err) {
      callback && callback(err);
      return;
    }
    req.headers['Content-Type'] = metadata.contentType || 'text/plain';
    var remoteStream = that.conn.requester(req);
    remoteStream.write(buffer);
    reqStreamToCallback(remoteStream, callback);
  });
};

Bucket.prototype.createReadStream = function(name) {
  return new ReadStream(this.conn, STORAGE_BASE_URL + '/' + [this.bucketName, 'o', name].join('/'));
};

Bucket.prototype.makeReq = function(method, path, q, body, callback) {
  var reqOpts = {
    method: method,
    qs: q,
    uri: STORAGE_BASE_URL + '/' + [this.bucketName, path].join('/')
  };
  if (body) {
    reqOpts.json = body;
  }
  this.conn.req(reqOpts, function(err, res, body) {
    if (body && body.error) {
      return callback(body.error);
    }
    if (res.statusCode < 200 || res.statusCode > 299) {
      return callback(new Error('error during request, statusCode: ' + res.statusCode));
    }
    callback(err, body);
  });
};

module.exports.Bucket = Bucket;

<|endoftext|>


<commit_before>'use strict';

var util = require('util')
  , EventEmitter = require('events').EventEmitter
  , ltx = require('ltx')
  , Stanza = require('./stanza').Stanza

/**
 * Recognizes <stream:stream> and collects stanzas used for ordinary
 * TCP streams and Websockets.
 *
 * API: write(data) & end(data)
 * Events: streamStart, stanza, end, error
 */
function StreamParser(maxStanzaSize) {
    EventEmitter.call(this)

    var self = this
    this.parser = new ltx.bestSaxParser()

    /* Count traffic for entire life-time */
    this.bytesParsed = 0
    this.maxStanzaSize = maxStanzaSize
    /* Will be reset upon first stanza, but enforce maxStanzaSize until it is parsed */
    this.bytesParsedOnStanzaBegin = 0

    this.parser.on('startElement', function(name, attrs) {
            // TODO: refuse anything but <stream:stream>
            if (!self.element && (name === 'stream:stream')) {
                self.emit('streamStart', attrs)
            } else {
                var child
                if (!self.element) {
                    /* A new stanza */
                    child = new Stanza(name, attrs)
                    self.element = child
                      /* For maxStanzaSize enforcement */
                    self.bytesParsedOnStanzaBegin = self.bytesParsed
                } else {
                    /* A child element of a stanza */
                    child = new ltx.Element(name, attrs)
                    self.element = self.element.cnode(child)
                }
            }
        }
    )

    this.parser.on('endElement', function(name) {
        if (!self.element && (name === 'stream:stream')) {
            self.end()
        } else if (self.element && (name === self.element.name)) {
            if (self.element.parent) {
                self.element = self.element.parent
            } else {
                /* Stanza complete */
                self.emit('stanza', self.element)
                delete self.element
                /* maxStanzaSize doesn't apply until next startElement */
                delete self.bytesParsedOnStanzaBegin
            }
        } else {
            self.error('xml-not-well-formed', 'XML parse error')
        }
    })

    this.parser.on('text', function(str) {
        if (self.element)
            self.element.t(str)
    })

    this.parser.on('entityDecl', function() {
        /* Entity declarations are forbidden in XMPP. We must abort to
         * avoid a billion laughs.
         */
        self.error('xml-not-well-formed', 'No entity declarations allowed')
        self.end()
    })

    this.parser.on('error', this.emit.bind(this, 'error'))
}

util.inherits(StreamParser, EventEmitter)

StreamParser.prototype.write = function(data) {
    /*if (/^<stream:stream [^>]+\/>$/.test(data)) {
    data = data.replace(/\/>$/, ">")
    }*/
    if (this.parser) {
    /* If a maxStanzaSize is configured, the current stanza must consist only of this many bytes */
        if (this.bytesParsedOnStanzaBegin && this.maxStanzaSize &&
            this.bytesParsed > this.bytesParsedOnStanzaBegin + this.maxStanzaSize) {

            this.error('policy-violation', 'Maximum stanza size exceeded')
            return
        }
        this.bytesParsed += data.length

        this.parser.write(data)
    }
}

StreamParser.prototype.end = function(data) {
    if (data) {
        this.write(data)
    }
    /* Get GC'ed */
    delete this.parser
    this.emit('end')
}

StreamParser.prototype.error = function(condition, message) {
    var e = new Error(message)
    e.condition = condition
    this.emit('error', e)
}

exports.StreamParser = StreamParser<commit_msg>extract xml tag<commit_after>'use strict';

var util = require('util')
  , EventEmitter = require('events').EventEmitter
  , ltx = require('ltx')
  , Stanza = require('./stanza').Stanza

/**
 * Recognizes <stream:stream> and collects stanzas used for ordinary
 * TCP streams and Websockets.
 *
 * API: write(data) & end(data)
 * Events: streamStart, stanza, end, error
 */
function StreamParser(maxStanzaSize) {
    EventEmitter.call(this)

    var self = this
    this.parser = new ltx.bestSaxParser()

    /* Count traffic for entire life-time */
    this.bytesParsed = 0
    this.maxStanzaSize = maxStanzaSize
    /* Will be reset upon first stanza, but enforce maxStanzaSize until it is parsed */
    this.bytesParsedOnStanzaBegin = 0

    this.parser.on('startElement', function(name, attrs) {
            // TODO: refuse anything but <stream:stream>
            if (!self.element && (name === 'stream:stream')) {
                self.emit('streamStart', attrs)
            } else {
                var child
                if (!self.element) {
                    /* A new stanza */
                    child = new Stanza(name, attrs)
                    self.element = child
                      /* For maxStanzaSize enforcement */
                    self.bytesParsedOnStanzaBegin = self.bytesParsed
                } else {
                    /* A child element of a stanza */
                    child = new ltx.Element(name, attrs)
                    self.element = self.element.cnode(child)
                }
            }
        }
    )

    this.parser.on('endElement', function(name) {
        if (!self.element && (name === 'stream:stream')) {
            self.end()
        } else if (self.element && (name === self.element.name)) {
            if (self.element.parent) {
                self.element = self.element.parent
            } else {
                /* Stanza complete */
                self.emit('stanza', self.element)
                delete self.element
                /* maxStanzaSize doesn't apply until next startElement */
                delete self.bytesParsedOnStanzaBegin
            }
        } else {
            self.error('xml-not-well-formed', 'XML parse error')
        }
    })

    this.parser.on('text', function(str) {
        if (self.element)
            self.element.t(str)
    })

    this.parser.on('entityDecl', function() {
        /* Entity declarations are forbidden in XMPP. We must abort to
         * avoid a billion laughs.
         */
        self.error('xml-not-well-formed', 'No entity declarations allowed')
        self.end()
    })

    this.parser.on('error', this.emit.bind(this, 'error'))
}

util.inherits(StreamParser, EventEmitter)

/*
 * hack for most usecases, do we have a better idea?
 *   catch the following:
 *   <?xml version="1.0"?>
 *   <?xml version="1.0" encoding="UTF-8"?>
 *   <?xml version="1.0" encoding="UTF-16" standalone="yes"?>
 */
StreamParser.prototype.checkXMLHeader = function (data) {
    // check for xml tag
    var index = data.indexOf('<?xml');

    if (index !== -1) {
        var end = data.indexOf('?>');
        if (index >= 0 && end >= 0 && index < end+2) {
            var search = data.substring(index,end+2);
            data = data.replace(search, '');
        }
    }

    return data;
}

StreamParser.prototype.write = function(data) {
    /*if (/^<stream:stream [^>]+\/>$/.test(data)) {
    data = data.replace(/\/>$/, ">")
    }*/
    if (this.parser) {

        data = data.toString('utf8')
        data = this.checkXMLHeader(data)

    /* If a maxStanzaSize is configured, the current stanza must consist only of this many bytes */
        if (this.bytesParsedOnStanzaBegin && this.maxStanzaSize &&
            this.bytesParsed > this.bytesParsedOnStanzaBegin + this.maxStanzaSize) {

            this.error('policy-violation', 'Maximum stanza size exceeded')
            return
        }
        this.bytesParsed += data.length

        this.parser.write(data)
    }
}

StreamParser.prototype.end = function(data) {
    if (data) {
        this.write(data)
    }
    /* Get GC'ed */
    delete this.parser
    this.emit('end')
}

StreamParser.prototype.error = function(condition, message) {
    var e = new Error(message)
    e.condition = condition
    this.emit('error', e)
}

exports.StreamParser = StreamParser<|endoftext|>


<commit_before>var https = require("https");
var q = require("q");
var request = require('request');

module.exports = function (nigel) {
    var projectBuildInfoUrl = "/job/{{project}}/{{buildId}}/api/json";
    var urlRegex = /\{\{(\w+)\}\}/g;

    function replaceUrlParams(url, params) {
        return url.replace(urlRegex, function(match, p1) {
            return params[p1] || "";
        });
    }

    function buildInfo(nigel, params) {
        var deferred = q.defer();
        var auth = params.auth;
        params.buildId = params.buildId || "lastSuccessfulBuild";
        params.project = params.project || "bti-svc-integ";
        params.url = params.url;
        var url = replaceUrlParams(params.url + projectBuildInfoUrl, params);
        request({
            url: url,
            auth: {
                user: params.user,
                pass: params.pass,
                sendImmediately: true
            }
        }, function (error, response, body) {
          if (!error && response.statusCode == 200) {
            deferred.resolve(body);
          } else if (error) {
            deferred.reject(error);
          }
        });
        return deferred.promise;
    }

    nigel.registerTask("info", "Gets a specific builds information", buildInfo);
};
<commit_msg>Jenkins refactor<commit_after>var https = require("https");
var q = require("q");
var request = require('request');
var _ = require('lodash');

module.exports = function (nigel) {
    var projectBuildInfoUrl = "/job/{{project}}/{{buildId}}/api/json";
    var urlRegex = /\{\{(\w+)\}\}/g;

    function replaceUrlParams(url, params) {
        return url.replace(urlRegex, function(match, p1) {
            return params[p1] || "";
        });
    }

    function buildInfo(nigel, params) {
        var deferred = q.defer();
        params = _.extend({}, {
            buildId: "lastSuccessfulBuild",
            project: "bti-svc-integ"
        }, params);
        params.url = replaceUrlParams(params.url + projectBuildInfoUrl, params);
        console.log({
            url: params.url,
            auth: {
                user: params.user,
                pass: params.pass,
                sendImmediately: true
            }
        });
        request({
            url: params.url,
            auth: {
                user: params.user,
                pass: params.pass,
                sendImmediately: true
            }
        }, function (error, response, body) {
          if (!error && response.statusCode == 200) {
            deferred.resolve(body);
          } else if (error) {
            deferred.reject(error);
          }
        });
        return deferred.promise;
    }

    nigel.registerTask("info", "Gets a specific builds information", buildInfo);
};
<|endoftext|>


<commit_before>const { v4: uuidv4 } = require('uuid');
let VError = require('verror');
let util = require('util');
const _ = require("lodash");

const EarlyTerminationMsg = 'Connection closed early when test was stopped';

class TestSteps {

  requestsInProgress = 0;
  scriptRunning = false;
  activeResults = [];
  resultsById = new Map();

  constructor() {
    this.cb = this.callback;
    console.log(this.cb);
    this.finish();
  }

  get currentSuite() {
    return this.suites.length > 0 ? this.suites[this.suites.length - 1] : null;
  }

  get currentTest() {
    const currentSuite = this.currentSuite;
    if ( !currentSuite || currentSuite.tests.length === 0 )
      return null;
    return currentSuite.tests[currentSuite.tests.length - 1];
  }

  async finish() {
    if ( this.suites )
      for ( const suite of this.suites ) {
        await this._onSuiteFinish(suite);
      }

    this.suites = [];
    this._before = null;
    this._beforeEach = null;
    this._after = null;
    this._afterEach = null;
  }

  async describe(suiteName, fn) {
    const suite = this._createSuite(this._getSuitePrefix() + suiteName, false);
    fn();
    await this._executeSuite(suite);
  }

  async it(testName, fn) {
    let suite = this.currentSuite;
    if ( !suite ) {
      suite = this._createSuite('Default Suite', true);
      this._onSuiteStart(suite);
    }
    let test = this._createTest(suite, testName, fn);
    if ( suite.executeTestsImmediately ) {
      await this._executeTest(suite, test);
      this._onSuiteFinish(suite);
    } else
      suite.tests.push(test);
  }

  before(fn) {
    this._register('_before', fn);
  }

  beforeEach(fn) {
    this._register('_beforeEach', fn);
  }

  after(fn) {
    this._register('_after', fn);
  }

  afterEach(fn) {
    this._register('_afterEach', fn);
  }

  _getSuitePrefix() {
    if ( this.suites.length === 0 )
      return '';
    let answer = '';
    for ( const suite of this.suites )
      answer += `${suite.name} => `;
    return answer;
  }

  _register(hook, fn) {
    const suite = this.currentSuite;
    if ( suite )
      suite[hook] = fn;
    else
      this[hook] = fn;
  }

  _createSuite(suiteName, isDefault) {
    const suite = {
      uuid: uuidv4(),
      name: suiteName,
      tests: [],
      started: Date.now(),
      hasError: false,
      executeTestsImmediately: isDefault
    };
    this.suites.push(suite);
    return suite;
  }

  _createTest(suite, name, fn) {
    return {
      suiteUuid: suite.uuid,
      suiteName: suite.name,
      uuid: uuidv4(),
      name: name,
      fn: fn,
      started: Date.now()
    };
  }

  async _executeSuite(suite) {
    this._onSuiteStart(suite);

    if ( suite.tests )
      for ( const test of suite.tests ) {
        await this._executeTest(suite, test);
      }

    this._onSuiteFinish(suite);
  }

  async _onSuiteStart(suite) {
    const before = suite._before || this._before;
    if ( before )
      await this._executeFn(before);

    if ( this.cb.startSuite )
      this.cb.startSuite(suite);
  }

  async _onSuiteFinish(suite) {
    suite.finished = Date.now();
    suite.duration = Date.now() - suite.started;

    const after = suite._after || this._after;
    if ( after )
      await this._executeFn(after);

    this.suites.pop();
    if ( this.cb.finishSuite )
      this.cb.finishSuite(suite);
  }

  async _executeTest(suite, test) {
    const beforeEach = suite._beforeEach || this._beforeEach;
    if ( beforeEach )
      await this._executeFn(beforeEach);

    await this._executeStep(test.fn, suite, test);

    const afterEach = suite._afterEach || this._afterEach;
    if ( afterEach )
      await this._executeFn(afterEach);
  }

  async _executeStep(fn, suite, test) {
    if ( this.cb.startTest )
      this.cb.startTest(test, suite);

    try {
      await this._executeFn(fn);
      test.state = 'passed';
    } catch (err) {
      suite.hasError = true;
      test.state = 'failed';
      test.errorType = err.name;
      if ( err.message )
        test.error = err.message;
      if ( err.stack )
        test.errorTrace = this.toErrorContent(err);
    }
    test.finished = Date.now();
    test.duration = Date.now() - test.started;

    if ( this.cb.finishTest )
      this.cb.finishTest(test, suite);
  }

  async _executeFn(fn) {
    const isAsync = fn.constructor.name === 'AsyncFunction';
    if ( isAsync )
      await fn();
    else if ( fn.length === 1 )
      await new Promise(function (resolve, reject) {
        fn(function (err) {
          if ( err )
            reject(new Error(err));
          else
            resolve();
        });
      });
    else
      fn();
  }

  filterStackTrace(stack) {
    const lines = stack.split('\n');
    var answer = '';
    var hitScriptLine = false;
    _.forEach(lines, function (line) {
      var trimmedLine = line.trim();
      if (!hitScriptLine)
        answer += line + '\n';
      if (trimmedLine.indexOf('at script.js') === 0 || trimmedLine.indexOf('(script.js') > 0)
        hitScriptLine = true;
    });
    return answer;
  }

  toErrorContent(error, msgOnly) {
    if (_.isUndefined(error) || error === null)
      return "";
    if (_.isString(error))
      return error;
    if (!_.isError(error) && _.isBuffer(error))
      return error.toString();
    if (!_.isError(error) && _.isObject(error))
      return util.inspect(error);
    if (!_.isError(error))
      return '' + error;
    if (_.isUndefined(error.message))
      return "Unknown error";
    switch (error.message) {
      case "Error: read ECONNRESET":
      case "Error: socket hang up":
        return "Connection closed unexpectedly";
      case "connect ETIMEDOUT":
        return "Connect timed out";
      default:
        return msgOnly ? error.message : this.filterStackTrace(VError.fullStack(error));
    }
  }

  callback = function (){
    const self = this;
    return {
      startSuite: function(suite) {
        self.reportTestSuiteActions([ { type: 'StartSuite', data: suite }]);
        self._requestStarted();
      },
      finishSuite: function(suite) {
        self.reportTestSuiteActions([ { type: 'FinishSuite', data: suite }]);
        self._requestFinished();
      },
      startTest: function(test) {
        self.reportTestSuiteActions([ { type: 'StartSuiteTest', data: test }]);
        self._requestStarted();
      },
      finishTest: function(test) {
        self.reportTestSuiteActions([ { type: 'FinishSuiteTest', data: test }]);
        self._requestFinished();
      }
    }
  }

  _requestStarted(){
    console.log('---------')
    console.log('requestStarted');
    this.requestsInProgress = this.requestsInProgress + 1;
  }
  reportTestSuiteActions(suiteActions){
    console.log('---------')
    console.log('reportTestSuiteActions');
    this.sendMsg({
      type: "TestSuiteActions",
      data: suiteActions
    });
  }
  _requestFinished(result){
    console.log('---------')
    console.log('_requestFinished');
    this.requestsInProgress = this.requestsInProgress - 1;
    this.resultFinished(result);
    if (this.requestsInProgress === 0 && !this.scriptRunning) {
      // this._done();
    }
  }

  resultFinished(result, earlyTermination) {
    console.log('---------')
    console.log('resultFinished');
    if (_.isObject(result) && !result.isFinished) {
      if (earlyTermination && result.data.url) {
        result.setTraceStatus('Terminated Early', EarlyTerminationMsg);
        // result.addTrace('Error', {}, EarlyTerminationMsg);
      }
      result.finished();
      if (!result.isEmpty()) {
        this.sendMsg({
          type: "Result",
          data: result.data
        });
      }
      let index = this.activeResults.indexOf(result);
      if (index !== -1)
        this.activeResults.splice(index, 1);
      if (result.id)
        this.resultsById.delete(result.id);
    }
  }

  sendMsg(result){
    console.log('---------')
    console.log('sendMsg');
    return new Promise(function (resolve, reject) {
      if (process.env.TESTABLE_RESULT_FILE) {
        fs.writeFile(process.env.TESTABLE_RESULT_FILE, JSON.stringify(result) + '\n', { encoding: 'utf8', flag: 'a' }, resolve);
      } else {
        console.log('[Result] ' + JSON.stringify(result, null, '\t'));
        resolve();
      }
    });
  }

}

module.exports = TestSteps;

/*

const webdriver = require('selenium-webdriver');
const util = require('util');
const fs = require('fs');
const writeFile = util.promisify(fs.writeFile);
const path = require('path');
const { expect } = require('chai');

async function takeScreenshot(driver, file){
  let image = await driver.takeScreenshot();
  await writeFile(file, image, 'base64');
}

describe('DefaultTest', () => {
  let driver = new webdriver.Builder()
    .withCapabilities(webdriver.Capabilities.chrome())
    .build();

  it('should go to google.com and check title', async () => {
    await driver.get('https://www.google.com');
    await takeScreenshot(driver, path.join(process.env.OUTPUT_DIR || '.', 'HomePage.png'));
    const title = await driver.getTitle();
    expect(title).to.equal('Google');
  });

  after(async () => driver.quit());
});

{
  "name": "webdriver-testable-demo",
  "version": "0.0.1",
  "description": "Simple Webdriver Javascript Example",
  "author": "Testable",
  "devDependencies": {
  "testable-utils": "0.5.9",
    "selenium-webdriver": "3.6.0",
    "mocha": "8.1.3",
    "chai": "4.2.0"
}
}*/
<commit_msg>more consoles<commit_after>const { v4: uuidv4 } = require('uuid');
let VError = require('verror');
let util = require('util');
const _ = require("lodash");

const EarlyTerminationMsg = 'Connection closed early when test was stopped';

class TestSteps {

  requestsInProgress = 0;
  scriptRunning = false;
  activeResults = [];
  resultsById = new Map();

  constructor() {
    this.cb = this.callback;
    console.log(this.cb);
    this.finish();
  }

  get currentSuite() {
    return this.suites.length > 0 ? this.suites[this.suites.length - 1] : null;
  }

  get currentTest() {
    const currentSuite = this.currentSuite;
    if ( !currentSuite || currentSuite.tests.length === 0 )
      return null;
    return currentSuite.tests[currentSuite.tests.length - 1];
  }

  async finish() {
    if ( this.suites )
      for ( const suite of this.suites ) {
        await this._onSuiteFinish(suite);
      }

    this.suites = [];
    this._before = null;
    this._beforeEach = null;
    this._after = null;
    this._afterEach = null;
  }

  async describe(suiteName, fn) {
    console.log('------------')
    console.log('describe')
    const suite = this._createSuite(this._getSuitePrefix() + suiteName, false);
    fn();
    await this._executeSuite(suite);
  }

  async it(testName, fn) {
    console.log('------------')
    console.log('it')
    let suite = this.currentSuite;
    if ( !suite ) {
      suite = this._createSuite('Default Suite', true);
      this._onSuiteStart(suite);
    }
    let test = this._createTest(suite, testName, fn);
    if ( suite.executeTestsImmediately ) {
      await this._executeTest(suite, test);
      this._onSuiteFinish(suite);
    } else
      suite.tests.push(test);
  }

  before(fn) {
    this._register('_before', fn);
  }

  beforeEach(fn) {
    this._register('_beforeEach', fn);
  }

  after(fn) {
    this._register('_after', fn);
  }

  afterEach(fn) {
    this._register('_afterEach', fn);
  }

  _getSuitePrefix() {
    if ( this.suites.length === 0 )
      return '';
    let answer = '';
    for ( const suite of this.suites )
      answer += `${suite.name} => `;
    return answer;
  }

  _register(hook, fn) {
    const suite = this.currentSuite;
    if ( suite )
      suite[hook] = fn;
    else
      this[hook] = fn;
  }

  _createSuite(suiteName, isDefault) {
    const suite = {
      uuid: uuidv4(),
      name: suiteName,
      tests: [],
      started: Date.now(),
      hasError: false,
      executeTestsImmediately: isDefault
    };
    this.suites.push(suite);
    return suite;
  }

  _createTest(suite, name, fn) {
    return {
      suiteUuid: suite.uuid,
      suiteName: suite.name,
      uuid: uuidv4(),
      name: name,
      fn: fn,
      started: Date.now()
    };
  }

  async _executeSuite(suite) {
    this._onSuiteStart(suite);

    if ( suite.tests )
      for ( const test of suite.tests ) {
        await this._executeTest(suite, test);
      }

    this._onSuiteFinish(suite);
  }

  async _onSuiteStart(suite) {
    const before = suite._before || this._before;
    if ( before )
      await this._executeFn(before);

    if ( this.cb.startSuite )
      this.cb.startSuite(suite);
  }

  async _onSuiteFinish(suite) {
    suite.finished = Date.now();
    suite.duration = Date.now() - suite.started;

    const after = suite._after || this._after;
    if ( after )
      await this._executeFn(after);

    this.suites.pop();
    if ( this.cb.finishSuite )
      this.cb.finishSuite(suite);
  }

  async _executeTest(suite, test) {
    const beforeEach = suite._beforeEach || this._beforeEach;
    if ( beforeEach )
      await this._executeFn(beforeEach);

    await this._executeStep(test.fn, suite, test);

    const afterEach = suite._afterEach || this._afterEach;
    if ( afterEach )
      await this._executeFn(afterEach);
  }

  async _executeStep(fn, suite, test) {
    if ( this.cb.startTest )
      this.cb.startTest(test, suite);

    try {
      await this._executeFn(fn);
      test.state = 'passed';
    } catch (err) {
      suite.hasError = true;
      test.state = 'failed';
      test.errorType = err.name;
      if ( err.message )
        test.error = err.message;
      if ( err.stack )
        test.errorTrace = this.toErrorContent(err);
    }
    test.finished = Date.now();
    test.duration = Date.now() - test.started;

    if ( this.cb.finishTest )
      this.cb.finishTest(test, suite);
  }

  async _executeFn(fn) {
    const isAsync = fn.constructor.name === 'AsyncFunction';
    if ( isAsync )
      await fn();
    else if ( fn.length === 1 )
      await new Promise(function (resolve, reject) {
        fn(function (err) {
          if ( err )
            reject(new Error(err));
          else
            resolve();
        });
      });
    else
      fn();
  }

  filterStackTrace(stack) {
    const lines = stack.split('\n');
    var answer = '';
    var hitScriptLine = false;
    _.forEach(lines, function (line) {
      var trimmedLine = line.trim();
      if (!hitScriptLine)
        answer += line + '\n';
      if (trimmedLine.indexOf('at script.js') === 0 || trimmedLine.indexOf('(script.js') > 0)
        hitScriptLine = true;
    });
    return answer;
  }

  toErrorContent(error, msgOnly) {
    if (_.isUndefined(error) || error === null)
      return "";
    if (_.isString(error))
      return error;
    if (!_.isError(error) && _.isBuffer(error))
      return error.toString();
    if (!_.isError(error) && _.isObject(error))
      return util.inspect(error);
    if (!_.isError(error))
      return '' + error;
    if (_.isUndefined(error.message))
      return "Unknown error";
    switch (error.message) {
      case "Error: read ECONNRESET":
      case "Error: socket hang up":
        return "Connection closed unexpectedly";
      case "connect ETIMEDOUT":
        return "Connect timed out";
      default:
        return msgOnly ? error.message : this.filterStackTrace(VError.fullStack(error));
    }
  }

  callback = function (){
    const self = this;
    return {
      startSuite: function(suite) {
        self.reportTestSuiteActions([ { type: 'StartSuite', data: suite }]);
        self._requestStarted();
      },
      finishSuite: function(suite) {
        self.reportTestSuiteActions([ { type: 'FinishSuite', data: suite }]);
        self._requestFinished();
      },
      startTest: function(test) {
        self.reportTestSuiteActions([ { type: 'StartSuiteTest', data: test }]);
        self._requestStarted();
      },
      finishTest: function(test) {
        self.reportTestSuiteActions([ { type: 'FinishSuiteTest', data: test }]);
        self._requestFinished();
      }
    }
  }

  _requestStarted(){
    console.log('---------')
    console.log('requestStarted');
    this.requestsInProgress = this.requestsInProgress + 1;
  }
  reportTestSuiteActions(suiteActions){
    console.log('---------')
    console.log('reportTestSuiteActions');
    this.sendMsg({
      type: "TestSuiteActions",
      data: suiteActions
    });
  }
  _requestFinished(result){
    console.log('---------')
    console.log('_requestFinished');
    this.requestsInProgress = this.requestsInProgress - 1;
    this.resultFinished(result);
    if (this.requestsInProgress === 0 && !this.scriptRunning) {
      // this._done();
    }
  }

  resultFinished(result, earlyTermination) {
    console.log('---------')
    console.log('resultFinished');
    if (_.isObject(result) && !result.isFinished) {
      if (earlyTermination && result.data.url) {
        result.setTraceStatus('Terminated Early', EarlyTerminationMsg);
        // result.addTrace('Error', {}, EarlyTerminationMsg);
      }
      result.finished();
      if (!result.isEmpty()) {
        this.sendMsg({
          type: "Result",
          data: result.data
        });
      }
      let index = this.activeResults.indexOf(result);
      if (index !== -1)
        this.activeResults.splice(index, 1);
      if (result.id)
        this.resultsById.delete(result.id);
    }
  }

  sendMsg(result){
    console.log('---------')
    console.log('sendMsg');
    return new Promise(function (resolve, reject) {
      if (process.env.TESTABLE_RESULT_FILE) {
        fs.writeFile(process.env.TESTABLE_RESULT_FILE, JSON.stringify(result) + '\n', { encoding: 'utf8', flag: 'a' }, resolve);
      } else {
        console.log('[Result] ' + JSON.stringify(result, null, '\t'));
        resolve();
      }
    });
  }

}

module.exports = TestSteps;

/*

const webdriver = require('selenium-webdriver');
const util = require('util');
const fs = require('fs');
const writeFile = util.promisify(fs.writeFile);
const path = require('path');
const { expect } = require('chai');

async function takeScreenshot(driver, file){
  let image = await driver.takeScreenshot();
  await writeFile(file, image, 'base64');
}

describe('DefaultTest', () => {
  let driver = new webdriver.Builder()
    .withCapabilities(webdriver.Capabilities.chrome())
    .build();

  it('should go to google.com and check title', async () => {
    await driver.get('https://www.google.com');
    await takeScreenshot(driver, path.join(process.env.OUTPUT_DIR || '.', 'HomePage.png'));
    const title = await driver.getTitle();
    expect(title).to.equal('Google');
  });

  after(async () => driver.quit());
});

{
  "name": "webdriver-testable-demo",
  "version": "0.0.1",
  "description": "Simple Webdriver Javascript Example",
  "author": "Testable",
  "devDependencies": {
  "testable-utils": "0.5.9",
    "selenium-webdriver": "3.6.0",
    "mocha": "8.1.3",
    "chai": "4.2.0"
}
}*/
<|endoftext|>


<commit_before>/**

  Import a data file into MongoDB

**/

var fs = require("fs");
var async = require("async");
var _ = require("underscore");
var Utils = require("../utils");
var keepRegex = new RegExp("\{keep\}", "i");
var filterRegex = /\{\s*\^(\w+)\(([\w<>,\s]*)\)\s*\}/i;
var debug = require("debug")("Importer");

module.exports = function(factSystem, Topic, Gambit, Reply) {

  return function(path, callback) {

    var that = this;
    var data = JSON.parse(fs.readFileSync(path, 'utf8'));

    var eachTopicItor = function(topicName, next) {
      debug("Find or create", topicName);
      var properties = {
        name:topicName,
        keep: (data.gTopicFlags[topicName].indexOf('keep') !== -1),
        system: (data.gTopicFlags[topicName].indexOf('system') !== -1),
        keywords: (data.keywords[topicName]) ? data.keywords[topicName] : []
      };

      Topic.findOrCreate({name:topicName}, properties, function(err, topic) {

        var eachGambitItor = function(gambitId, next2) {
          var gambitData = rawToGambitData(gambitId, data.gTopics[topicName][gambitId]);
          topic.createGambit(gambitData, function(err, gambit){
            // add reply
            var itemData = data.gTopics[topicName][gambitId];
            var replyItor = function(replyId, cb) {

              var replyString = itemData.reply[replyId];
              var properties = { id: replyId, reply: itemData.reply[replyId] };

              var match = replyString.match(keepRegex);
              if (match) {
                properties.keep = true;
                properties.reply = Utils.trim(replyString.replace(match[0], ""));
              }

              match = replyString.match(filterRegex);
              if (match) {
                properties.filter = "^" + match[1] + "(" + match[2] + ")";
                properties.reply = Utils.trim(replyString.replace(match[0], ""));
              }

              gambit.addReply(properties, function(err, reply){
                cb();
              });
            };

            async.each(Object.keys(itemData.reply), replyItor, function(err, res){
              next2();
            });

          });
        };

        async.each(Object.keys(data.gTopics[topicName]), eachGambitItor, function(){
          next();
        });
      });
    };

    async.each(Object.keys(data.gTopics), eachTopicItor, function(){

      var eachTopicPrevItor = function(topicName, next) {
        var eachGambitSetPrevItor = function(replyId, next2) {
          Reply.findOne({id:replyId}, function(err, reply) {
            debug('eachGambitSetPrevItor: ' + replyId);
            if (reply) {
              debug('Parent reply found');
              var gambitSet = data.gPrevTopics[topicName][replyId];

              var eachGambitPrevItor = function(trigger, next3) {
                debug('eachGambitPrevItor: ' + trigger);
                createGambit(trigger, gambitSet[trigger], function(err, res){
                  // Add to the replySet.
                  debug('Gambi created: ' + res.id);

                  reply.gambits.addToSet(res._id);
                  reply.save(function(){
                    next3();
                  });
                });
              };
              // I want to fetch the reply first
              async.eachSeries(Object.keys(gambitSet), eachGambitPrevItor, function(){
                next2();
              });

            } else {
              // No Reply found, lets skip it.
              next2();
            }
          });

        };

        async.eachSeries(Object.keys(data.gPrevTopics[topicName]), eachGambitSetPrevItor, function(){
          next();
        });
      };

      async.eachSeries(Object.keys(data.gPrevTopics), eachTopicPrevItor, function(){
        console.log("doneeeee");
        callback(null, "done");
      });
    });
  };
};

var rawToGambitData = function(gambitId, itemData) {
  var gambitData = {
    id: gambitId,
    isQuestion: itemData.options.isQuestion,
    qType: (itemData.options.qType === false)? "" : itemData.options.qType,
    qSubType: (itemData.options.qSubType === false) ? "" : itemData.options.qSubType,
    filter: (itemData.options.filter === false) ? "" : itemData.options.filter
  };

  // This is to capture anything pre 5.1
  if (itemData.raw) {
    gambitData.input = itemData.raw;
  } else {
    gambitData.input = itemData.trigger;
    gambitData.trigger = itemData.trigger;
  }

  if (itemData.redirect && itemData.redirect !== "") {
    gambitData.redirect = itemData.redirect;
  }
  return gambitData;
};

var createGambit = function(gambitId, itemData, callback) {
  var gambitData = rawToGambitData(gambitId, itemData);

  console.log(gambitData);

  Gambit.findOrCreate({id:gambitId}, gambitData, function(err, gambit) {

    var replyItor = function(replyId, cb) {

      // TODO: Replies from the old system could have filter functions here
      // Lets strip them off and pass them in like normal properties

      var replyString = itemData.reply[replyId];
      var properties = { id: replyId, reply: itemData.reply[replyId] };

      var match = replyString.match(keepRegex);
      if (match) {
        properties.keep = true;
        properties.reply = Utils.trim(replyString.replace(match[0], ""));
      }

      match = replyString.match(filterRegex);
      if (match) {
        properties.filter = "^" + match[1] + "(" + match[2] + ")";
        properties.reply = Utils.trim(replyString.replace(match[0], ""));
      }

      Reply.create(properties, function(err,res){
        res.save(function(err){
          var addMe = {$addToSet: {replies: res._id}};
          Gambit.findByIdAndUpdate(gambit._id, addMe, function(err, me) {
            cb();
          });
        });
      });
    };

    async.each(Object.keys(itemData.reply), replyItor, function(err, res){
      gambit.save(callback);
    });

  });
};
<commit_msg>clean console.log<commit_after>/**

  Import a data file into MongoDB

**/

var fs = require("fs");
var async = require("async");
var _ = require("underscore");
var Utils = require("../utils");
var keepRegex = new RegExp("\{keep\}", "i");
var filterRegex = /\{\s*\^(\w+)\(([\w<>,\s]*)\)\s*\}/i;
var debug = require("debug")("Importer");

module.exports = function(factSystem, Topic, Gambit, Reply) {

  return function(path, callback) {

    var that = this;
    var data = JSON.parse(fs.readFileSync(path, 'utf8'));

    var eachTopicItor = function(topicName, next) {
      debug("Find or create", topicName);
      var properties = {
        name:topicName,
        keep: (data.gTopicFlags[topicName].indexOf('keep') !== -1),
        system: (data.gTopicFlags[topicName].indexOf('system') !== -1),
        keywords: (data.keywords[topicName]) ? data.keywords[topicName] : []
      };

      Topic.findOrCreate({name:topicName}, properties, function(err, topic) {

        var eachGambitItor = function(gambitId, next2) {
          var gambitData = rawToGambitData(gambitId, data.gTopics[topicName][gambitId]);
          topic.createGambit(gambitData, function(err, gambit){
            // add reply
            var itemData = data.gTopics[topicName][gambitId];
            var replyItor = function(replyId, cb) {

              var replyString = itemData.reply[replyId];
              var properties = { id: replyId, reply: itemData.reply[replyId] };

              var match = replyString.match(keepRegex);
              if (match) {
                properties.keep = true;
                properties.reply = Utils.trim(replyString.replace(match[0], ""));
              }

              match = replyString.match(filterRegex);
              if (match) {
                properties.filter = "^" + match[1] + "(" + match[2] + ")";
                properties.reply = Utils.trim(replyString.replace(match[0], ""));
              }

              gambit.addReply(properties, function(err, reply){
                cb();
              });
            };

            async.each(Object.keys(itemData.reply), replyItor, function(err, res){
              next2();
            });

          });
        };

        async.each(Object.keys(data.gTopics[topicName]), eachGambitItor, function(){
          next();
        });
      });
    };

    async.each(Object.keys(data.gTopics), eachTopicItor, function(){

      var eachTopicPrevItor = function(topicName, next) {
        var eachGambitSetPrevItor = function(replyId, next2) {
          Reply.findOne({id:replyId}, function(err, reply) {
            debug('eachGambitSetPrevItor: ' + replyId);
            if (reply) {
              debug('Parent reply found');
              var gambitSet = data.gPrevTopics[topicName][replyId];

              var eachGambitPrevItor = function(trigger, next3) {
                debug('eachGambitPrevItor: ' + trigger);
                createGambit(trigger, gambitSet[trigger], function(err, res){
                  // Add to the replySet.
                  debug('Gambi created: ' + res.id);

                  reply.gambits.addToSet(res._id);
                  reply.save(function(){
                    next3();
                  });
                });
              };
              // I want to fetch the reply first
              async.eachSeries(Object.keys(gambitSet), eachGambitPrevItor, function(){
                next2();
              });

            } else {
              // No Reply found, lets skip it.
              next2();
            }
          });

        };

        async.eachSeries(Object.keys(data.gPrevTopics[topicName]), eachGambitSetPrevItor, function(){
          next();
        });
      };

      async.eachSeries(Object.keys(data.gPrevTopics), eachTopicPrevItor, function(){
        debug('All topics processed.');
        callback(null, "done");
      });
    });
  };
};

var rawToGambitData = function(gambitId, itemData) {
  var gambitData = {
    id: gambitId,
    isQuestion: itemData.options.isQuestion,
    qType: (itemData.options.qType === false)? "" : itemData.options.qType,
    qSubType: (itemData.options.qSubType === false) ? "" : itemData.options.qSubType,
    filter: (itemData.options.filter === false) ? "" : itemData.options.filter
  };

  // This is to capture anything pre 5.1
  if (itemData.raw) {
    gambitData.input = itemData.raw;
  } else {
    gambitData.input = itemData.trigger;
    gambitData.trigger = itemData.trigger;
  }

  if (itemData.redirect && itemData.redirect !== "") {
    gambitData.redirect = itemData.redirect;
  }
  return gambitData;
};

var createGambit = function(gambitId, itemData, callback) {
  var gambitData = rawToGambitData(gambitId, itemData);

  console.log(gambitData);

  Gambit.findOrCreate({id:gambitId}, gambitData, function(err, gambit) {

    var replyItor = function(replyId, cb) {

      // TODO: Replies from the old system could have filter functions here
      // Lets strip them off and pass them in like normal properties

      var replyString = itemData.reply[replyId];
      var properties = { id: replyId, reply: itemData.reply[replyId] };

      var match = replyString.match(keepRegex);
      if (match) {
        properties.keep = true;
        properties.reply = Utils.trim(replyString.replace(match[0], ""));
      }

      match = replyString.match(filterRegex);
      if (match) {
        properties.filter = "^" + match[1] + "(" + match[2] + ")";
        properties.reply = Utils.trim(replyString.replace(match[0], ""));
      }

      Reply.create(properties, function(err,res){
        res.save(function(err){
          var addMe = {$addToSet: {replies: res._id}};
          Gambit.findByIdAndUpdate(gambit._id, addMe, function(err, me) {
            cb();
          });
        });
      });
    };

    async.each(Object.keys(itemData.reply), replyItor, function(err, res){
      gambit.save(callback);
    });

  });
};
<|endoftext|>