<commit_before>'use strict';
var Q = require('q');
var debug = require('debug')('dynamoose:table');
var util = require('util');
var _ = require('underscore');

function Table(name, schema, options, base) {
  debug('new Table (%s)', name, schema);
  this.name = name;
  this.schema = schema;
  this.options = options || {};
  this.base = base;

  if (this.options.create === undefined || this.options.create === null) {
    this.options.create = true;
  }
}

var compareIndexes = function compareIndexes(local, remote) {
  var indexes = {
    delete: [],
    create: [],
    both: []
  };
  var localTableReq = local;
  var remoteTableReq = remote;
  var i;
  var j;

  var localIndexes = localTableReq.GlobalSecondaryIndexes || [];
  var remoteIndexes = remoteTableReq.GlobalSecondaryIndexes || [];

  debug('compareIndexes');
  // let's see what remote indexes we need to sync or create
  for (i = 0; i < localIndexes.length; i++) {
    var remoteIndexFound = false;
    for (j = 0; j < remoteIndexes.length; j++) {
      if (remoteIndexes[j].IndexName === localIndexes[i].IndexName) {
        // let's see if the core data matches. if it doesn't,
        // we may need to delete the remote GSI and rebuild.
        var localIndex = _.pick(localIndexes[i], 'IndexName', 'KeySchema', 'Projection', 'ProvisionedThroughput');
        var remoteIndex = _.pick(remoteIndexes[j], 'IndexName', 'KeySchema', 'Projection', 'ProvisionedThroughput');
        if (remoteIndex.hasOwnProperty('ProvisionedThroughput')) {
          delete remoteIndex.ProvisionedThroughput.NumberOfDecreasesToday;
        }

        if (!_.isEqual(remoteIndex, localIndex)) {
          indexes.both.push(localIndex);
          remoteIndexFound = true;
        } else {
          remoteIndexFound = true;
        }
      }
    }
    if (!remoteIndexFound) {
      indexes.create.push(localIndexes[i]);
    }
  }
  for (j = 0; j < remoteIndexes.length; j++) {
    var localExists = false;
    for (i = 0; i < localIndexes.length; i++) {
      if (remoteIndexes[j].IndexName === localIndexes[i].IndexName) {
        localExists = true;
      }
    }
    if (!localExists) {
      indexes.delete.push(remoteIndexes[j]);
    }
  }
  // now let's see what remote indexes exist that shouldn't exist

  return indexes;
};

Table.prototype.deleteIndex = function deleteIndex(indexName) {
  var deferred = Q.defer();
  var table = this;
  table.active = false;
  var params = {
    TableName: table.name,
    GlobalSecondaryIndexUpdates: [
      {
        Delete: {
          IndexName: indexName
        }
      }
    ]
  };
  table.base.ddb().updateTable(params, function (err, data) {
    debug('deleteIndex handler running');
    if (err) {
      deferred.reject(err);
    }
    else {
      setTimeout(function () {
        table.waitForActive()
          .then(function () {
            deferred.resolve(data);
          });
      }, 300);
    }
  });
  return deferred.promise;
};

Table.prototype.createIndex = function createIndex(attributes, indexSpec) {
  var deferred = Q.defer();
  var table = this;
  table.active = false;
  var params = {
    TableName: this.name,
    AttributeDefinitions: attributes,
    GlobalSecondaryIndexUpdates: [
      {
        Create: indexSpec
      }
    ]
  };
  this.base.ddb().updateTable(params, function (err, data) {
    if (err) {
      deferred.reject(err);
    }
    else {
      setTimeout(function () {
        table.waitForActive()
          .then(function () {
            deferred.resolve(data);
          });
      }, 300);
    }
  });
  return deferred.promise;
};

Table.prototype.init = function (next) {
  debug('initializing table, %s, %j', this.name, this.options);
  var deferred = Q.defer();

  var table = this;
  var localTableReq;

  if (this.options.create) {
    this.describe()
      .then(function (data) {
        debug('table exist -- initialization done');
        localTableReq = buildTableReq(table.name, table.schema);
        var indexes = compareIndexes(localTableReq, data.Table);

        debug('%s', JSON.stringify(indexes, null, 2));
        if (table.options.update) {
          debug('checking indexes');
          for (var deleteIdx in indexes.delete) {
            table.deleteIndex(indexes.delete[deleteIdx].IndexName);
          }
          for (var bothIdx in indexes.both) {
            /*jshint loopfunc: true */
            table.deleteIndex(indexes.both[bothIdx].IndexName)
              .then(function () {
                table.createIndex(localTableReq.AttributeDefinitions, indexes.both[bothIdx]);
              });
          }
          for (var createIdx in indexes.create) {
            table.createIndex(localTableReq.AttributeDefinitions, indexes.create[createIdx]);
          }
        } else {
          if (indexes.delete.length > 0 || indexes.create.length > 0) {
            debug('indexes are not synchronized and update flag is set to false');
            deferred.reject(new Error('indexes are not synchronized and update flag is set to false'));
          }
        }
        table.initialized = true;
        return table.waitForActive()
          .then(function () {
            //table.active = data.Table.TableStatus === 'ACTIVE';

            return deferred.resolve();
          });
      })
      .catch(function (err) {
        if (err && err.code === 'ResourceNotFoundException') {
          debug('table does not exist -- creating');
          return deferred.resolve(
            table.create()
              .then(function () {
                table.initialized = true;
              })
              .then(function() {
                if(table.options.waitForActive) {
                  return table.waitForActive();
                }
              })
          );
        }
        if (err) {
          debug('error initializing', err.stack);
          return deferred.reject(err);
        }
      });
  } else {
    table.initialized = true;
    return deferred.resolve();
  }
  return deferred.promise.nodeify(next);
};

Table.prototype.waitForActive = function (timeout, next) {
  debug('Waiting for Active table, %s, %j', this.name, this.options);
  var deferred = Q.defer();

  if (typeof timeout === 'function') {
    next = timeout;
    timeout = null;
  }

  if (!timeout) {
    timeout = this.options.waitForActiveTimeout;
  }

  var table = this;

  var timeoutAt = Date.now() + timeout;

  function waitForActive() {
    debug('Waiting...');
    /*
     if (table.active) {
     debug('Table flag is set to Active - %s', table.name);
     return deferred.resolve();
     }*/
    if (Date.now() > timeoutAt) {
      return deferred.reject(
        new Error('Wait for Active timed out after ' + timeout + ' ms.')
      );
    }
    if (!table.initialized) {
      return setTimeout(waitForActive, 10);
    }
    table.describe()
      .then(function (data) {
        var active = (data.Table.TableStatus === 'ACTIVE');
        var indexes = data.Table.GlobalSecondaryIndexes || [];
        indexes.forEach(function (gsi) {
          //debug('waitForActive Index Check: %s', JSON.stringify(gsi, null, 2));
          debug('index %s.IndexStatus is %s', gsi.IndexName, gsi.IndexStatus);
          if (gsi.IndexStatus !== 'ACTIVE') {
            active = false;
          }
        });
        if (!active) {
          debug('Waiting for Active again - %s', table.name);
          setTimeout(waitForActive, 500);
        } else {
          table.active = true;
          deferred.resolve();
        }
      })
      .catch(function (err) {
        if (err && err.code === 'ResourceNotFoundException') {
          return setTimeout(waitForActive, 10);
        }
        debug('Error waiting for active', err.stack);
        return deferred.reject(err);
      });
  }

  waitForActive();

  return deferred.promise.nodeify(next);
};

Table.prototype.describe = function (next) {
  var describeTableReq = {
    TableName: this.name
  };

  var deferred = Q.defer();

  var ddb = this.base.ddb();
  ddb.describeTable(describeTableReq, function (err, data) {
    if (err) {
      debug('error describing table', err);
      return deferred.reject(err);
    }
    deferred.resolve(data);
  });

  return deferred.promise.nodeify(next);
};

var buildTableReq = function buildTableReq(name, schema) {
  var attrDefs = [];

  var keyAttr = {};

  function addKeyAttr(attr) {
    if (attr) {
      keyAttr[attr.name] = attr;
    }
  }

  addKeyAttr(schema.hashKey);
  addKeyAttr(schema.rangeKey);
  for (var globalIndexName in schema.indexes.global) {
    addKeyAttr(schema.indexes.global[globalIndexName]);

    // add the range key to the attribute definitions if specified
    var rangeKeyName = schema.indexes.global[globalIndexName].indexes[globalIndexName].rangeKey;
    addKeyAttr(schema.attributes[rangeKeyName]);
  }
  for (var indexName in schema.indexes.local) {
    addKeyAttr(schema.indexes.local[indexName]);
  }

  for (var keyAttrName in keyAttr) {
    attrDefs.push({
      AttributeName: keyAttrName,
      AttributeType: keyAttr[keyAttrName].type.dynamo
    });
  }

  var keySchema = [{
    AttributeName: schema.hashKey.name,
    KeyType: 'HASH'
  }];
  if (schema.rangeKey) {
    keySchema.push({
      AttributeName: schema.rangeKey.name,
      KeyType: 'RANGE'
    });
  }

  var provThroughput = {
    ReadCapacityUnits: schema.throughput.read,
    WriteCapacityUnits: schema.throughput.write
  };

  var createTableReq = {
    AttributeDefinitions: attrDefs,
    TableName: name,
    KeySchema: keySchema,
    ProvisionedThroughput: provThroughput
  };

  debug('Creating table local indexes', schema.indexes.local);
  var localSecIndexes, index;
  for (var localSecIndexName in schema.indexes.local) {
    localSecIndexes = localSecIndexes || [];

    var indexAttr = schema.indexes.local[localSecIndexName];
    index = indexAttr.indexes[localSecIndexName];
    var localSecIndex = {
      IndexName: localSecIndexName,
      KeySchema: [{
        AttributeName: schema.hashKey.name,
        KeyType: 'HASH'
      }, {
        AttributeName: indexAttr.name,
        KeyType: 'RANGE'
      }]
    };

    if (index.project) {
      if (util.isArray(index.project)) {
        localSecIndex.Projection = {
          ProjectionType: 'INCLUDE',
          NonKeyAttributes: index.project
        };
      } else {
        localSecIndex.Projection = {
          ProjectionType: 'ALL'
        };
      }
    } else {
      localSecIndex.Projection = {
        ProjectionType: 'KEYS_ONLY'
      };
    }

    localSecIndexes.push(localSecIndex);
  }

  var globalSecIndexes;
  for (var globalSecIndexName in schema.indexes.global) {
    globalSecIndexes = globalSecIndexes || [];

    var globalIndexAttr = schema.indexes.global[globalSecIndexName];
    index = globalIndexAttr.indexes[globalSecIndexName];

    var globalSecIndex = {
      IndexName: globalSecIndexName,
      KeySchema: [{
        AttributeName: globalIndexAttr.name,
        KeyType: 'HASH'
      }],
      ProvisionedThroughput: {
        ReadCapacityUnits: index.throughput.read,
        WriteCapacityUnits: index.throughput.write
      }
    };

    if (index.rangeKey) {
      globalSecIndex.KeySchema.push({
        AttributeName: index.rangeKey,
        KeyType: 'RANGE'
      });
    }

    if (index.project) {
      if (util.isArray(index.project)) {
        globalSecIndex.Projection = {
          ProjectionType: 'INCLUDE',
          NonKeyAttributes: index.project
        };
      } else {
        globalSecIndex.Projection = {
          ProjectionType: 'ALL'
        };
      }
    } else {
      globalSecIndex.Projection = {
        ProjectionType: 'KEYS_ONLY'
      };
    }

    globalSecIndexes.push(globalSecIndex);
  }

  if (localSecIndexes) {
    createTableReq.LocalSecondaryIndexes = localSecIndexes;
  }

  if (globalSecIndexes) {
    createTableReq.GlobalSecondaryIndexes = globalSecIndexes;
  }

  return createTableReq;
};

Table.prototype.create = function (next) {
  var ddb = this.base.ddb();
  var schema = this.schema;
  var createTableReq = buildTableReq(this.name, schema);

  debug('ddb.createTable request:', createTableReq);

  var deferred = Q.defer();

  ddb.createTable(createTableReq, function (err, data) {
    if (err) {
      debug('error creating table', err);
      return deferred.reject(err);
    }
    debug('table created', data);
    deferred.resolve(data);
  });
  return deferred.promise.nodeify(next);

};

Table.prototype.delete = function (next) {
  var deleteTableReq = {
    TableName: this.name
  };

  debug('ddb.deleteTable request:', deleteTableReq);

  var ddb = this.base.ddb();

  var deferred = Q.defer();

  ddb.deleteTable(deleteTableReq, function (err, data) {
    if (err) {
      debug('error deleting table', err);
      return deferred.reject(err);
    }
    debug('deleted table', data);
    deferred.resolve(data);
  });

  return deferred.promise.nodeify(next);

};

Table.prototype.update = function (next) {
  // var ddb = this.base.ddb();
  // ddb.updateTable();
  var deferred = Q.defer();
  deferred.reject(new Error('TODO'));
  return deferred.promise.nodeify(next);
};

module.exports = Table;
<commit_msg>Fix critical bug with deleting and creating global secondary index<commit_after>'use strict';
var Q = require('q');
var debug = require('debug')('dynamoose:table');
var util = require('util');
var _ = require('underscore');

function Table(name, schema, options, base) {
  debug('new Table (%s)', name, schema);
  this.name = name;
  this.schema = schema;
  this.options = options || {};
  this.base = base;

  if (this.options.create === undefined || this.options.create === null) {
    this.options.create = true;
  }
}

var compareIndexes = function compareIndexes(local, remote) {
  var indexes = {
    delete: [],
    create: [],
    both: []
  };
  var localTableReq = local;
  var remoteTableReq = remote;
  var i;
  var j;

  var localIndexes = localTableReq.GlobalSecondaryIndexes || [];
  var remoteIndexes = remoteTableReq.GlobalSecondaryIndexes || [];

  debug('compareIndexes');
  // let's see what remote indexes we need to sync or create
  for (i = 0; i < localIndexes.length; i++) {
    var remoteIndexFound = false;
    for (j = 0; j < remoteIndexes.length; j++) {
      if (remoteIndexes[j].IndexName === localIndexes[i].IndexName) {
        // let's see if the core data matches. if it doesn't,
        // we may need to delete the remote GSI and rebuild.
        var localIndex = _.pick(localIndexes[i], 'IndexName', 'KeySchema', 'Projection');
        var remoteIndex = _.pick(remoteIndexes[j], 'IndexName', 'KeySchema', 'Projection');

        if (!_.isEqual(remoteIndex, localIndex)) {
          indexes.both.push(localIndex);
          remoteIndexFound = true;
        } else {
          remoteIndexFound = true;
        }
      }
    }
    if (!remoteIndexFound) {
      indexes.create.push(localIndexes[i]);
    }
  }
  for (j = 0; j < remoteIndexes.length; j++) {
    var localExists = false;
    for (i = 0; i < localIndexes.length; i++) {
      if (remoteIndexes[j].IndexName === localIndexes[i].IndexName) {
        localExists = true;
      }
    }
    if (!localExists) {
      indexes.delete.push(remoteIndexes[j]);
    }
  }
  // now let's see what remote indexes exist that shouldn't exist

  return indexes;
};

Table.prototype.deleteIndex = function deleteIndex(indexName) {
  var deferred = Q.defer();
  var table = this;
  table.active = false;
  var params = {
    TableName: table.name,
    GlobalSecondaryIndexUpdates: [
      {
        Delete: {
          IndexName: indexName
        }
      }
    ]
  };
  table.base.ddb().updateTable(params, function (err, data) {
    debug('deleteIndex handler running');
    if (err) {
      deferred.reject(err);
    }
    else {
      setTimeout(function () {
        table.waitForActive()
          .then(function () {
            deferred.resolve(data);
          });
      }, 300);
    }
  });
  return deferred.promise;
};

Table.prototype.createIndex = function createIndex(attributes, indexSpec) {
  var deferred = Q.defer();
  var table = this;
  table.active = false;
  var params = {
    TableName: this.name,
    AttributeDefinitions: attributes,
    GlobalSecondaryIndexUpdates: [
      {
        Create: indexSpec
      }
    ]
  };
  this.base.ddb().updateTable(params, function (err, data) {
    if (err) {
      deferred.reject(err);
    }
    else {
      setTimeout(function () {
        table.waitForActive()
          .then(function () {
            deferred.resolve(data);
          });
      }, 300);
    }
  });
  return deferred.promise;
};

Table.prototype.init = function (next) {
  debug('initializing table, %s, %j', this.name, this.options);
  var deferred = Q.defer();

  var table = this;
  var localTableReq;

  if (this.options.create) {
    this.describe()
      .then(function (data) {
        debug('table exist -- initialization done');
        localTableReq = buildTableReq(table.name, table.schema);
        var indexes = compareIndexes(localTableReq, data.Table);

        debug('%s', JSON.stringify(indexes, null, 2));
        if (table.options.update) {
          debug('checking indexes');
          for (var deleteIdx in indexes.delete) {
            table.deleteIndex(indexes.delete[deleteIdx].IndexName);
          }
          for (var bothIdx in indexes.both) {
            /*jshint loopfunc: true */
            table.deleteIndex(indexes.both[bothIdx].IndexName)
              .then(function () {
                table.createIndex(localTableReq.AttributeDefinitions, indexes.both[bothIdx]);
              });
          }
          for (var createIdx in indexes.create) {
            table.createIndex(localTableReq.AttributeDefinitions, indexes.create[createIdx]);
          }
        } else {
          if (indexes.delete.length > 0 || indexes.create.length > 0) {
            debug('indexes are not synchronized and update flag is set to false');
            deferred.reject(new Error('indexes are not synchronized and update flag is set to false'));
          }
        }
        table.initialized = true;
        return table.waitForActive()
          .then(function () {
            //table.active = data.Table.TableStatus === 'ACTIVE';

            return deferred.resolve();
          });
      })
      .catch(function (err) {
        if (err && err.code === 'ResourceNotFoundException') {
          debug('table does not exist -- creating');
          return deferred.resolve(
            table.create()
              .then(function () {
                table.initialized = true;
              })
              .then(function() {
                if(table.options.waitForActive) {
                  return table.waitForActive();
                }
              })
          );
        }
        if (err) {
          debug('error initializing', err.stack);
          return deferred.reject(err);
        }
      });
  } else {
    table.initialized = true;
    return deferred.resolve();
  }
  return deferred.promise.nodeify(next);
};

Table.prototype.waitForActive = function (timeout, next) {
  debug('Waiting for Active table, %s, %j', this.name, this.options);
  var deferred = Q.defer();

  if (typeof timeout === 'function') {
    next = timeout;
    timeout = null;
  }

  if (!timeout) {
    timeout = this.options.waitForActiveTimeout;
  }

  var table = this;

  var timeoutAt = Date.now() + timeout;

  function waitForActive() {
    debug('Waiting...');
    /*
     if (table.active) {
     debug('Table flag is set to Active - %s', table.name);
     return deferred.resolve();
     }*/
    if (Date.now() > timeoutAt) {
      return deferred.reject(
        new Error('Wait for Active timed out after ' + timeout + ' ms.')
      );
    }
    if (!table.initialized) {
      return setTimeout(waitForActive, 10);
    }
    table.describe()
      .then(function (data) {
        var active = (data.Table.TableStatus === 'ACTIVE');
        var indexes = data.Table.GlobalSecondaryIndexes || [];
        indexes.forEach(function (gsi) {
          //debug('waitForActive Index Check: %s', JSON.stringify(gsi, null, 2));
          debug('index %s.IndexStatus is %s', gsi.IndexName, gsi.IndexStatus);
          if (gsi.IndexStatus !== 'ACTIVE') {
            active = false;
          }
        });
        if (!active) {
          debug('Waiting for Active again - %s', table.name);
          setTimeout(waitForActive, 500);
        } else {
          table.active = true;
          deferred.resolve();
        }
      })
      .catch(function (err) {
        if (err && err.code === 'ResourceNotFoundException') {
          return setTimeout(waitForActive, 10);
        }
        debug('Error waiting for active', err.stack);
        return deferred.reject(err);
      });
  }

  waitForActive();

  return deferred.promise.nodeify(next);
};

Table.prototype.describe = function (next) {
  var describeTableReq = {
    TableName: this.name
  };

  var deferred = Q.defer();

  var ddb = this.base.ddb();
  ddb.describeTable(describeTableReq, function (err, data) {
    if (err) {
      debug('error describing table', err);
      return deferred.reject(err);
    }
    deferred.resolve(data);
  });

  return deferred.promise.nodeify(next);
};

var buildTableReq = function buildTableReq(name, schema) {
  var attrDefs = [];

  var keyAttr = {};

  function addKeyAttr(attr) {
    if (attr) {
      keyAttr[attr.name] = attr;
    }
  }

  addKeyAttr(schema.hashKey);
  addKeyAttr(schema.rangeKey);
  for (var globalIndexName in schema.indexes.global) {
    addKeyAttr(schema.indexes.global[globalIndexName]);

    // add the range key to the attribute definitions if specified
    var rangeKeyName = schema.indexes.global[globalIndexName].indexes[globalIndexName].rangeKey;
    addKeyAttr(schema.attributes[rangeKeyName]);
  }
  for (var indexName in schema.indexes.local) {
    addKeyAttr(schema.indexes.local[indexName]);
  }

  for (var keyAttrName in keyAttr) {
    attrDefs.push({
      AttributeName: keyAttrName,
      AttributeType: keyAttr[keyAttrName].type.dynamo
    });
  }

  var keySchema = [{
    AttributeName: schema.hashKey.name,
    KeyType: 'HASH'
  }];
  if (schema.rangeKey) {
    keySchema.push({
      AttributeName: schema.rangeKey.name,
      KeyType: 'RANGE'
    });
  }

  var provThroughput = {
    ReadCapacityUnits: schema.throughput.read,
    WriteCapacityUnits: schema.throughput.write
  };

  var createTableReq = {
    AttributeDefinitions: attrDefs,
    TableName: name,
    KeySchema: keySchema,
    ProvisionedThroughput: provThroughput
  };

  debug('Creating table local indexes', schema.indexes.local);
  var localSecIndexes, index;
  for (var localSecIndexName in schema.indexes.local) {
    localSecIndexes = localSecIndexes || [];

    var indexAttr = schema.indexes.local[localSecIndexName];
    index = indexAttr.indexes[localSecIndexName];
    var localSecIndex = {
      IndexName: localSecIndexName,
      KeySchema: [{
        AttributeName: schema.hashKey.name,
        KeyType: 'HASH'
      }, {
        AttributeName: indexAttr.name,
        KeyType: 'RANGE'
      }]
    };

    if (index.project) {
      if (util.isArray(index.project)) {
        localSecIndex.Projection = {
          ProjectionType: 'INCLUDE',
          NonKeyAttributes: index.project
        };
      } else {
        localSecIndex.Projection = {
          ProjectionType: 'ALL'
        };
      }
    } else {
      localSecIndex.Projection = {
        ProjectionType: 'KEYS_ONLY'
      };
    }

    localSecIndexes.push(localSecIndex);
  }

  var globalSecIndexes;
  for (var globalSecIndexName in schema.indexes.global) {
    globalSecIndexes = globalSecIndexes || [];

    var globalIndexAttr = schema.indexes.global[globalSecIndexName];
    index = globalIndexAttr.indexes[globalSecIndexName];

    var globalSecIndex = {
      IndexName: globalSecIndexName,
      KeySchema: [{
        AttributeName: globalIndexAttr.name,
        KeyType: 'HASH'
      }],
      ProvisionedThroughput: {
        ReadCapacityUnits: index.throughput.read,
        WriteCapacityUnits: index.throughput.write
      }
    };

    if (index.rangeKey) {
      globalSecIndex.KeySchema.push({
        AttributeName: index.rangeKey,
        KeyType: 'RANGE'
      });
    }

    if (index.project) {
      if (util.isArray(index.project)) {
        globalSecIndex.Projection = {
          ProjectionType: 'INCLUDE',
          NonKeyAttributes: index.project
        };
      } else {
        globalSecIndex.Projection = {
          ProjectionType: 'ALL'
        };
      }
    } else {
      globalSecIndex.Projection = {
        ProjectionType: 'KEYS_ONLY'
      };
    }

    globalSecIndexes.push(globalSecIndex);
  }

  if (localSecIndexes) {
    createTableReq.LocalSecondaryIndexes = localSecIndexes;
  }

  if (globalSecIndexes) {
    createTableReq.GlobalSecondaryIndexes = globalSecIndexes;
  }

  return createTableReq;
};

Table.prototype.create = function (next) {
  var ddb = this.base.ddb();
  var schema = this.schema;
  var createTableReq = buildTableReq(this.name, schema);

  debug('ddb.createTable request:', createTableReq);

  var deferred = Q.defer();

  ddb.createTable(createTableReq, function (err, data) {
    if (err) {
      debug('error creating table', err);
      return deferred.reject(err);
    }
    debug('table created', data);
    deferred.resolve(data);
  });
  return deferred.promise.nodeify(next);

};

Table.prototype.delete = function (next) {
  var deleteTableReq = {
    TableName: this.name
  };

  debug('ddb.deleteTable request:', deleteTableReq);

  var ddb = this.base.ddb();

  var deferred = Q.defer();

  ddb.deleteTable(deleteTableReq, function (err, data) {
    if (err) {
      debug('error deleting table', err);
      return deferred.reject(err);
    }
    debug('deleted table', data);
    deferred.resolve(data);
  });

  return deferred.promise.nodeify(next);

};

Table.prototype.update = function (next) {
  // var ddb = this.base.ddb();
  // ddb.updateTable();
  var deferred = Q.defer();
  deferred.reject(new Error('TODO'));
  return deferred.promise.nodeify(next);
};

module.exports = Table;
<|endoftext|>


<commit_before>/*
    Rob Holme

    Displays all fields in a segment in a tree view.
    Return a formatted string to be displayed by the caller.
*/

// load modules
const common = require('./common.js');

// defines a class representing a component item
class Component {
    constructor(Name) {
        this.name = Name;
        this.value = "";
    }
    get Name() {
        return this.name;
    }
    set Value(Value) {
        this.value = Value;
    }
    get Value() {
        return this.value;
    }
}

// defines a class representing a Field. This is a collection of FieldItems.
// A repeating Field contains many FieldItems, a non-repeating Field contains
// one FieldItem.
class Field {
    constructor() {
        this.fieldItems = [];
        this.maxLength = 0;
    }
    // add a new FieldItem object to this collection. Calculate the maximum
    // length of all FieldItems to allow output to be formatted in columns
    AddFieldItem(FieldItem) {
        this.fieldItems.push(FieldItem);
        if (FieldItem.MaxLength > this.maxLength) {
            this.maxLength = FieldItem.MaxLength;
        }
    }
    // return the maximum length of the name/description of all FieldItems
    get MaxLength() {
        return this.maxLength;
    }
    // return the array of all FieldItem objects contained in this Field
    get FieldItems() {
        return this.fieldItems();
    }
}

// Defines a single instance of a Field. Contains a collection of Components.
// If the field does not contain components, the value attribute is set to the
// value of the HL7 field. If the field contains components, the value will be empty
// and the components will contain values instead.
class FieldItem {
    constructor(Name) {
        this.name = Name;
        this.components = [];
        this.value = "";
        this.maxLength = Name.length + 12 || 0;
    }
    // add a new component to the array.
    AddComponent(Component) {
        this.components.push(Component)
        // record the length of the longest component description to calculate length to pad all strings.
        // allow minimum of 17 characters for indenting, component index etc.
        if ((Component.Name.length + 17) > this.maxLength) {
            this.maxLength = Component.Name.length + 17;
        }
    }
    get Name() {
        return this.name;
    }
    set Name(Name) {
        this.name = Name;
        // update the maxLength attribute if needed (allowing 12 characters for field name, field index, repeat index etc.)
        if ((Name.length + 12) > this.maxLength) {
            this.maxLength = Name.length + 12;
        }
    }
    get Value() {
        return this.value;
    }
    set Value(Value) {
        this.value = Value;
    }
    get Components() {
        return this.components;
    }
    get MaxLength() {
        return this.maxLength;
    }
}

// This class represents a HL7 segment. It contains a list of Field objects.
class Segment {
    constructor(Name) {
        this.name = Name;
        this.fields = [];
        this.maxLength = 0;
        this.description = "";
    }
    // add a new Field object to the collection of fields.
    AddField(Field) {
        this.fields.push(Field);
        if (Field.MaxLength > this.maxLength) {
            this.maxLength = Field.MaxLength;
        }
    }
    // Set and Get the segment description.
    set Description(Description) {
        this.description = Description;
    }
    get Description() {
        return this.description;
    }
    // Set and Get the segment Name. This will be the 3 letter segment code.
    set Name(Name) {
        this.name = Name;
    }
    get Name() {
        return this.name;
    }
    // this returns the maximum length of the name/description for all Fields contained in this segment object
    get MaxLength() {
        return this.maxLength;
    }
    // return the list of Field objects that this segment comprises of.
    get Fields() {
        return this.fields;
    }
}

//----------------------------------------------------
// add trailing characters to right pad a string
function padRight(stringToPad, padLength, padChar) {
    paddingCharacter = padChar || ' '; // default to space if the padding char not supplied
    if (!stringToPad || stringToPad.length >= padLength) {
        return stringToPad;
    }
    var maxLength = (padLength - stringToPad.length);
    for (var i = 0; i < maxLength; i++) {
        stringToPad += paddingCharacter;
    }
    return stringToPad;
}

//----------------------------------------------------
// Display the fields in a segment
// @param {string} segment - a string containing the segment to format as a tree view
// @param {array} hl7Schema - An array containing the HL7 schema corresponding to the version of the hl7 file being viewed in the editor
// @param {array} hl7Fields - An array containing the field descriptions
//
// @returns {string} - returns a string containing the segment fields formatted on a tree view
function DisplaySegmentAsTree(segment, hl7Schema, hl7Fields) {
    // load the message delimiters from the current file
    var delimiters = common.ParseDelimiters();
    var repeatNum = 0;
    var segmentFieldArray = segment.split(delimiters.FIELD);
    var segmentName = segmentFieldArray[0];
    var segmentDefinition = hl7Schema[segmentName];
    var segmentToDisplay = new Segment(segmentName)

    // special case for MSH segment, since the field delimiter is MSH-1
    if (segmentName === 'MSH') {
        segmentFieldArray.splice(1, 0, delimiters.FIELD);
    }

    // if a custom segment ('Z' segment) is selected, the segment name will not exist in hl7Schema.
    if (!segmentDefinition) {
        segmentToDisplay.Name = segmentName;
        segmentToDisplay.Description = "Custom Segment";
    }
    else {
        segmentToDisplay.Name = segmentName;
        segmentToDisplay.Description = segmentDefinition.desc;
    }

    for (var i = 1; i < segmentFieldArray.length; i++) {
        var fieldName;
        // determine the field name, default to 'Unknown' if not found in schema
        fieldName = "undefined";
        if (segmentDefinition) {
            if (i <= segmentDefinition.fields.length) {
                fieldName = segmentDefinition.fields[i - 1].desc;
            }
        }
        // empty field - just add the description and continue to next field
        if (segmentFieldArray[i] == "") {
            var field = new Field();
            var fieldItem = new FieldItem(fieldName);
            field.AddFieldItem(fieldItem);
            segmentToDisplay.AddField(field);
            continue;
        }
        var fieldDataType;
        if (segmentDefinition) {
            fieldDataType = segmentDefinition.fields[i - 1].datatype;
        }
        // for each repeating field item create a FieldItem object and add it to Field object
        var fieldRepeats = segmentFieldArray[i].split(delimiters.REPEAT);
        // don't split MSH-2 since it contains the repeat delimiter by definition
        if ((segmentName == 'MSH') && (i == 2)) {
            fieldRepeats = segmentFieldArray[2].split();
        }
        var field = new Field();
        for (var j = 0; j < fieldRepeats.length; j++) {
            var fieldItem = new FieldItem(fieldName);
            // for each component, add it to the FieldItem's list fo Components. If no component delimiters found, assign the field value to FieldItem's Value property
            // don't split the field into components if it is MSH-2, as by definition it contains the component delimiter but is a single field.
            if ((segmentName == 'MSH') && (i == 2)) {
                components = fieldRepeats[j].split();
            }
            else {
                components = fieldRepeats[j].split(delimiters.COMPONENT);
            }
            if (components.length > 1) {
                for (k = 0; k < components.length; k++) {
                    // confirm the field data type is listed in the schema, if not the component description will be 'undefined'
                    var componentName = "undefined";
                    if (fieldDataType) {
                        if (hl7Fields[fieldDataType].subfields.length > k) {
                            componentName = hl7Fields[fieldDataType].subfields[k].desc;
                        }
                    }
                    var component = new Component(componentName);
                    component.Value = components[k];
                    fieldItem.AddComponent(component);
                }
            }
            else {
                fieldItem.Value = fieldRepeats[j];
            }
            field.AddFieldItem(fieldItem);
        }
        segmentToDisplay.AddField(field);
    }

    // display the items to the output window. Enumerate through all Fields in the Segment object,
    // then all FieldItems in the Field collection. If the FieldItem objects contain Components, list
    // all components values and names. If there are no components, then list the FieldItem value and name.
    var output = segmentToDisplay.Name + " - " + segmentToDisplay.Description + "\n\n";
    segmentToDisplay.Fields.forEach(function (fieldElement, fieldIndex) {
        fieldElement.fieldItems.forEach(function (fieldItemElement, fieldItemIndex) {
            // if a field is repeating (i.e. a Field object contains more than 1 FieldItem objects) include the repeat index in the field location
            var repeat = "";
            if (fieldElement.fieldItems.length > 1) {
                repeat = "[" + (fieldItemIndex + 1) + "]"
            }
            var fieldValue = fieldItemElement.Value;
            output += padRight(segmentToDisplay.Name + "-" + (fieldIndex + 1) + repeat + " " + fieldItemElement.Name + ":", segmentToDisplay.MaxLength) + fieldValue + "\n";
            fieldItemElement.Components.forEach(function (componentElement, componentIndex) {
                var border = " ├ ";
                if (componentIndex + 1 == fieldItemElement.Components.length) {
                    border = " └ "
                }
                output += padRight(border + segmentToDisplay.Name + "-" + (fieldIndex + 1) + repeat + "." + (componentIndex + 1) + " (" + componentElement.Name + ") ", segmentToDisplay.MaxLength) + componentElement.Value + "\n";
            });
        });
    });
    return output;
}

exports.DisplaySegmentAsTree = DisplaySegmentAsTree;<commit_msg>fixed issue with Display Segment Fields Commnad failing when an undefined field contains a value<commit_after>/*
    Rob Holme

    Displays all fields in a segment in a tree view.
    Return a formatted string to be displayed by the caller.
*/

// load modules
const common = require('./common.js');

// defines a class representing a component item
class Component {
    constructor(Name) {
        this.name = Name;
        this.value = "";
    }
    get Name() {
        return this.name;
    }
    set Value(Value) {
        this.value = Value;
    }
    get Value() {
        return this.value;
    }
}

// defines a class representing a Field. This is a collection of FieldItems.
// A repeating Field contains many FieldItems, a non-repeating Field contains
// one FieldItem.
class Field {
    constructor() {
        this.fieldItems = [];
        this.maxLength = 0;
    }
    // add a new FieldItem object to this collection. Calculate the maximum
    // length of all FieldItems to allow output to be formatted in columns
    AddFieldItem(FieldItem) {
        this.fieldItems.push(FieldItem);
        if (FieldItem.MaxLength > this.maxLength) {
            this.maxLength = FieldItem.MaxLength;
        }
    }
    // return the maximum length of the name/description of all FieldItems
    get MaxLength() {
        return this.maxLength;
    }
    // return the array of all FieldItem objects contained in this Field
    get FieldItems() {
        return this.fieldItems();
    }
}

// Defines a single instance of a Field. Contains a collection of Components.
// If the field does not contain components, the value attribute is set to the
// value of the HL7 field. If the field contains components, the value will be empty
// and the components will contain values instead.
class FieldItem {
    constructor(Name) {
        this.name = Name;
        this.components = [];
        this.value = "";
        this.maxLength = Name.length + 12 || 0;
    }
    // add a new component to the array.
    AddComponent(Component) {
        this.components.push(Component)
        // record the length of the longest component description to calculate length to pad all strings.
        // allow minimum of 17 characters for indenting, component index etc.
        if ((Component.Name.length + 17) > this.maxLength) {
            this.maxLength = Component.Name.length + 17;
        }
    }
    get Name() {
        return this.name;
    }
    set Name(Name) {
        this.name = Name;
        // update the maxLength attribute if needed (allowing 12 characters for field name, field index, repeat index etc.)
        if ((Name.length + 12) > this.maxLength) {
            this.maxLength = Name.length + 12;
        }
    }
    get Value() {
        return this.value;
    }
    set Value(Value) {
        this.value = Value;
    }
    get Components() {
        return this.components;
    }
    get MaxLength() {
        return this.maxLength;
    }
}

// This class represents a HL7 segment. It contains a list of Field objects.
class Segment {
    constructor(Name) {
        this.name = Name;
        this.fields = [];
        this.maxLength = 0;
        this.description = "";
    }
    // add a new Field object to the collection of fields.
    AddField(Field) {
        this.fields.push(Field);
        if (Field.MaxLength > this.maxLength) {
            this.maxLength = Field.MaxLength;
        }
    }
    // Set and Get the segment description.
    set Description(Description) {
        this.description = Description;
    }
    get Description() {
        return this.description;
    }
    // Set and Get the segment Name. This will be the 3 letter segment code.
    set Name(Name) {
        this.name = Name;
    }
    get Name() {
        return this.name;
    }
    // this returns the maximum length of the name/description for all Fields contained in this segment object
    get MaxLength() {
        return this.maxLength;
    }
    // return the list of Field objects that this segment comprises of.
    get Fields() {
        return this.fields;
    }
}

//----------------------------------------------------
// add trailing characters to right pad a string
function padRight(stringToPad, padLength, padChar) {
    paddingCharacter = padChar || ' '; // default to space if the padding char not supplied
    if (!stringToPad || stringToPad.length >= padLength) {
        return stringToPad;
    }
    var maxLength = (padLength - stringToPad.length);
    for (var i = 0; i < maxLength; i++) {
        stringToPad += paddingCharacter;
    }
    return stringToPad;
}

//----------------------------------------------------
// Display the fields in a segment
// @param {string} segment - a string containing the segment to format as a tree view
// @param {array} hl7Schema - An array containing the HL7 schema corresponding to the version of the hl7 file being viewed in the editor
// @param {array} hl7Fields - An array containing the field descriptions
//
// @returns {string} - returns a string containing the segment fields formatted on a tree view
function DisplaySegmentAsTree(segment, hl7Schema, hl7Fields) {
    // load the message delimiters from the current file
    var delimiters = common.ParseDelimiters();
	var repeatNum = 0;
	// get the list of fields as an array. After saving the segment name, remove it from the array so it contains fields only (i.e. remove the item at index 0)
	var segmentFieldArray = segment.split(delimiters.FIELD);
    var segmentName = segmentFieldArray[0];
	segmentFieldArray.splice(0, 1);
    var segmentDefinition = hl7Schema[segmentName];
    var segmentToDisplay = new Segment(segmentName)

    // special case for MSH segment, since the field delimiter is MSH-1
    if (segmentName === 'MSH') {
        segmentFieldArray.splice(1, 0, delimiters.FIELD);
    }

    // if a custom segment ('Z' segment) is selected, the segment name will not exist in hl7Schema.
    if (!segmentDefinition) {
        segmentToDisplay.Name = segmentName;
        segmentToDisplay.Description = "Custom Segment";
    }
    else {
        segmentToDisplay.Name = segmentName;
        segmentToDisplay.Description = segmentDefinition.desc;
    }

    for (var i = 0; i < segmentFieldArray.length; i++) {
		var fieldName;
        // determine the field name, default to 'Unknown' if not found in schema
        fieldName = "undefined";
        if (segmentDefinition) {
            if (i < segmentDefinition.fields.length) {
                fieldName = segmentDefinition.fields[i].desc;
            }
        }
        // empty field - just add the description and continue to next field
        if (segmentFieldArray[i] == "") {
            var field = new Field();
            var fieldItem = new FieldItem(fieldName);
            field.AddFieldItem(fieldItem);
            segmentToDisplay.AddField(field);
            continue;
        }
        var fieldDataType;
        if (segmentDefinition) {
			if (i < segmentDefinition.fields.length) {
			fieldDataType = segmentDefinition.fields[i].datatype;
			}
        }
        // for each repeating field item create a FieldItem object and add it to Field object
        var fieldRepeats = segmentFieldArray[i].split(delimiters.REPEAT);
        // don't split MSH-2 since it contains the repeat delimiter by definition
        if ((segmentName == 'MSH') && (i == 2)) {
            fieldRepeats = segmentFieldArray[2].split();
        }
        var field = new Field();
        for (var j = 0; j < fieldRepeats.length; j++) {
            var fieldItem = new FieldItem(fieldName);
            // for each component, add it to the FieldItem's list fo Components. If no component delimiters found, assign the field value to FieldItem's Value property
            // don't split the field into components if it is MSH-2, as by definition it contains the component delimiter but is a single field.
            if ((segmentName == 'MSH') && (i == 2)) {
                components = fieldRepeats[j].split();
            }
            else {
                components = fieldRepeats[j].split(delimiters.COMPONENT);
            }
            if (components.length > 1) {
                for (k = 0; k < components.length; k++) {
                    // confirm the field data type is listed in the schema, if not the component description will be 'undefined'
                    var componentName = "undefined";
                    if (fieldDataType) {
                        if (hl7Fields[fieldDataType].subfields.length > k) {
                            componentName = hl7Fields[fieldDataType].subfields[k].desc;
                        }
                    }
                    var component = new Component(componentName);
                    component.Value = components[k];
                    fieldItem.AddComponent(component);
                }
            }
            else {
                fieldItem.Value = fieldRepeats[j];
            }
            field.AddFieldItem(fieldItem);
        }
        segmentToDisplay.AddField(field);
    }
    // display the items to the output window. Enumerate through all Fields in the Segment object,
    // then all FieldItems in the Field collection. If the FieldItem objects contain Components, list
    // all components values and names. If there are no components, then list the FieldItem value and name.
    var output = segmentToDisplay.Name + " - " + segmentToDisplay.Description + "\n\n";
    segmentToDisplay.Fields.forEach(function (fieldElement, fieldIndex) {
        fieldElement.fieldItems.forEach(function (fieldItemElement, fieldItemIndex) {
            // if a field is repeating (i.e. a Field object contains more than 1 FieldItem objects) include the repeat index in the field location
            var repeat = "";
            if (fieldElement.fieldItems.length > 1) {
                repeat = "[" + (fieldItemIndex + 1) + "]"
            }
            var fieldValue = fieldItemElement.Value;
            output += padRight(segmentToDisplay.Name + "-" + (fieldIndex + 1) + repeat + " " + fieldItemElement.Name + ":", segmentToDisplay.MaxLength) + fieldValue + "\n";
            fieldItemElement.Components.forEach(function (componentElement, componentIndex) {
                var border = " ├ ";
                if (componentIndex + 1 == fieldItemElement.Components.length) {
                    border = " └ "
                }
                output += padRight(border + segmentToDisplay.Name + "-" + (fieldIndex + 1) + repeat + "." + (componentIndex + 1) + " (" + componentElement.Name + ") ", segmentToDisplay.MaxLength) + componentElement.Value + "\n";
            });
        });
    });
    return output;
}

exports.DisplaySegmentAsTree = DisplaySegmentAsTree;<|endoftext|>


<commit_before><commit_msg>fix null message format exception<commit_after><|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

var whatis = require('./');

var help = require('help-version')(usage()).help,
    align = require('string-align');

function usage () {
  return 'Usage:  npm-whatis <package>';
}

var format = function (name, description) {
  if (name.length < 20) {
    name = align(name, 20, 'left');
  }
  return name + ' - ' + description;
};

(function (argv) {
  if (argv.length != 1) {
    return help(argv.length);
  }

  var name = argv[0];

  whatis(name, function (err, description) {
    if (err) return console.error(err.toString());
    console.log(format(name, description));
  });
}(process.argv.slice(2)));
<commit_msg>Fix exit code on error<commit_after>#!/usr/bin/env node
'use strict';

var whatis = require('./');

var help = require('help-version')(usage()).help,
    align = require('string-align');

function usage () {
  return 'Usage:  npm-whatis <package>';
}

var format = function (name, description) {
  if (name.length < 20) {
    name = align(name, 20, 'left');
  }
  return name + ' - ' + description;
};

(function (argv) {
  if (argv.length != 1) {
    return help(argv.length);
  }

  var name = argv[0];

  whatis(name, function (err, description) {
    if (err) {
      console.error(err.toString());
      process.exit(1);
    }
    console.log(format(name, description));
  });
}(process.argv.slice(2)));
<|endoftext|>


<commit_before>var api = require('../api');

module.exports = {
  route: 'connections[{keys:query}][{ranges:range}][{keys:fields}]',
  get: function(pathSets) {
    var fromLocation = pathSets.query[0],
      toLocation = pathSets.query[1],
      range = pathSets.range[0],
      fields = pathSets.fields;

    console.log(fromLocation, toLocation, range, fields);

    return api
      .getConnections(fromLocation, toLocation)
      .then(function(connections) {
        console.log(connections);

        connections = connections.slice(range.from, range.to + 1);

        return connections.reduce(function(acc, location, i) {
          pathSets[3].forEach(function(key) {
            if (location[key]) {
              acc.push({ path: ['locations', query, range.from + i, key], value: location[key] });
            }
          });

          // Location ref
          acc.push({
            path: ['locations', query, i, 'departures'],
            value: { $type: 'ref', $expires: -30 * 1000, value: ['departures', '"' + location.id + '"'] }
          });

          return acc;
        }, []);
      })
      .catch(function(err) {
        console.error(err.stack);
      });
  }
};
<commit_msg>Fix connections<commit_after>var api = require('../api');

module.exports = {
  route: 'connections[{keys:query}][{ranges:range}][{keys:fields}]',
  get: function(pathSets) {
    var fromLocation = pathSets.query[0],
      toLocation = pathSets.query[1],
      range = pathSets.range[0],
      fields = pathSets.fields;

    return api
      .getConnections(fromLocation, toLocation)
      .then(function(connections) {
        connections = connections.slice(range.from, range.to + 1);

        return connections.reduce(function(acc, connection, i) {
          fields.forEach(function(key) {
            if (connection.from[key]) {
              acc.push({ path: ['connections', fromLocation, range.from + i, key], value: connection.from[key] });
            }

            if (connection.to[key]) {
              acc.push({ path: ['connections', toLocation, range.from + i, key], value: connection.to[key] });
            }
          });

          // Location ref
          acc.push({
            path: ['connections', fromLocation, range.from + i, 'location'],
            value: { $type: 'ref', $expires: -30 * 1000, value: ['locations', '"' + connection.from.station.name + '"'] }
          });

          acc.push({
            path: ['connections', toLocation, range.from + i, 'location'],
            value: { $type: 'ref', $expires: -30 * 1000, value: ['locations', '"' + connection.to.station.name + '"'] }
          });

          return acc;
        }, []);
      })
      .catch(function(err) {
        console.error(err.stack);
      });
  }
};
<|endoftext|>


<commit_before>/*
 * @copyright Copyright (c) Sematext Group, Inc. - All Rights Reserved
 *
 * @licence SPM for NodeJS is free-to-use, proprietary software.
 * THIS IS PROPRIETARY SOURCE CODE OF Sematext Group, Inc. (Sematext)
 * This source code may not be copied, reverse engineered, or altered for any purpose.
 * This source code is to be used exclusively by users and customers of Sematext.
 * Please see the full license (found in LICENSE in this distribution) for details on its license and the licenses of its dependencies.
 */

'use strict'
var moment = require('moment')
var logger = require('./util/logger.js')
var util = require ('util')

function formatArray (a) {
  if (a instanceof Array) {
    return a.join('\t')
  } else {
    return a + ''
  }
}
/**
 * Agent super class that provides an EventEmitter for 'metric' event.
 * In addition it fires for each added metric an event named by the property metric.name
 *
 * @param {function} plugin -  Function providing a plugin having start(agent) and stop method. The plugin can use agent.addMetric function.
 * @returns {function} an Agent having start,stop, addMetric function
 */
function Agent (plugin) {
  var events = require('events')
  var util = require('util')
  var eventEmitter = new events.EventEmitter()
  var cluster = require('cluster')
  var workerId = 0 + '-' + process.pid // 0 == Master, default
  if (!cluster.isMaster) {
    workerId = cluster.worker.id + '-' + process.pid
  }
  var agentSuperClass = {
    plugin: plugin,
    defaultType: 'njs',
    defaultFilters: function () {
      return [workerId || 0, process.pid || 0]
    },
    metricBuffer: [],
    /**
     * Adds a metric value and emits event to listeners on 'metric' or metric.name
     * @param metric
     */
    addMetrics: function (metric) {
      metric.workerId = workerId
      metric.pid = process.pid
      if (!metric.ts) {
        metric.ts = new Date().getTime()
      }
      if (!metric.type) {
        metric.type = this.defaultType
      }
      if (!metric.filters && metric.type === 'njs') {
        metric.filters = this.defaultFilters()
      }
      metric.spmLine = this.formatLine(metric)
      this.emit('metric', metric)
      if (metric.name) {
        this.emit(metric.name, metric)
      }
    },
    collectdFormatLine: function (metric) {
      var now = (new Date().getTime()).toFixed(0)
      var metricsTs = (metric.ts).toFixed(0)
      var now2 = (new Date().getTime() / 1000).toFixed(0)
      var metricsTs2 = (metric.ts / 1000).toFixed(0)
      var dateString = moment(metric.ts).format('YYYY-MM-DD')
      var line = ''
      if ((/collectd.*\-/.test(metric.name))) {
        if (/collectd\-cpu/.test(metric.name)) {
          line = (now2 + '\t' + metric.name + '-' + dateString + ',' + metricsTs2 + ',' + metric.value)
        } else if (/disk\-space/.test(metric.name)) {
          line = (now + '\t' + metric.name + '-' + dateString + ',' + metricsTs2 + ',' + metric.value.toFixed(6))
        } else {
          line = now2 + '\t' + metric.name + '\t' + metricsTs2 + ',' + metric.value
        }
      } else {
        line = (now / 1000).toFixed(0) + '\t' + metric.name + '\t' + metricsTs + ',' + metric.value
      }
      return line
    },

    defaultFormatLine: function (metric) {
      var now = (new Date().getTime()).toFixed(0)
      var line = null
      if (metric.sct === 'OS') {
        line = this.collectdFormatLine(metric)
      } else {
        line = util.format('%d\t%s\t%d\t%s\t%s', now, (metric.type || this.defaultType) + '-' + metric.name, metric.ts, formatArray(metric.filters), formatArray(metric.value))
        logger.log(now + '\t' + 'njs-' + metric.name + '\t' + metric.ts + '\t' + (metric.workerId || 0) + '\t' + (metric.pid || 0) + '\t' + formatArray(metric.value))
      }
      return line
    },
    /**
     * formats a line for SPM sender, if the plugin does not support this method the default formatter for nodejs and OP metrics is used
     * @maram metric to format
     */
    formatLine: function (metric) {
      if (!this.plugin.formatLine) {
        return this.defaultFormatLine(metric)
      } else {
        return this.plugin.formatLine(metric)
      }
    },

    /**
     * Starts the agent - typically thy create listeners or interval checks for metrics, and use 'addMetrics' to inform listeners
     */
    start: function () {
      if (this.plugin && this.plugin.start) {
        // this is the place to register for external events that return metrics
        this.plugin.start(this)
      } else {
        console.log('NOT called start')
      }
    },
    /**
     * Stops the plugin - timers could be pushed to plugins 'timers' Array, if timers exists it tries to clearInterval(timerId)
     * if no timers are defined it tries to call stop method of the plugin
     */
    stop: function () {
      if (this.plugin.timers) {
        this.plugin.timers.forEach(function (tid) {
          clearInterval(tid)
        })
      } else {
        if (this.plugin.stop) {
          this.plugin.stop()
        } else {
          logger.warn('could not stop agent')
        }
      }
    }
  }
  return util._extend(eventEmitter, agentSuperClass)
}

module.exports = Agent
<commit_msg>unify timstamp for collectd format<commit_after>/*
 * @copyright Copyright (c) Sematext Group, Inc. - All Rights Reserved
 *
 * @licence SPM for NodeJS is free-to-use, proprietary software.
 * THIS IS PROPRIETARY SOURCE CODE OF Sematext Group, Inc. (Sematext)
 * This source code may not be copied, reverse engineered, or altered for any purpose.
 * This source code is to be used exclusively by users and customers of Sematext.
 * Please see the full license (found in LICENSE in this distribution) for details on its license and the licenses of its dependencies.
 */

'use strict'
var moment = require('moment')
var logger = require('./util/logger.js')
var util = require ('util')

function formatArray (a) {
  if (a instanceof Array) {
    return a.join('\t')
  } else {
    return a + ''
  }
}
/**
 * Agent super class that provides an EventEmitter for 'metric' event.
 * In addition it fires for each added metric an event named by the property metric.name
 *
 * @param {function} plugin -  Function providing a plugin having start(agent) and stop method. The plugin can use agent.addMetric function.
 * @returns {function} an Agent having start,stop, addMetric function
 */
function Agent (plugin) {
  var events = require('events')
  var util = require('util')
  var eventEmitter = new events.EventEmitter()
  var cluster = require('cluster')
  var workerId = 0 + '-' + process.pid // 0 == Master, default
  if (!cluster.isMaster) {
    workerId = cluster.worker.id + '-' + process.pid
  }
  var agentSuperClass = {
    plugin: plugin,
    defaultType: 'njs',
    defaultFilters: function () {
      return [workerId || 0, process.pid || 0]
    },
    metricBuffer: [],
    /**
     * Adds a metric value and emits event to listeners on 'metric' or metric.name
     * @param metric
     */
    addMetrics: function (metric) {
      metric.workerId = workerId
      metric.pid = process.pid
      if (!metric.ts) {
        metric.ts = new Date().getTime()
      }
      if (!metric.type) {
        metric.type = this.defaultType
      }
      if (!metric.filters && metric.type === 'njs') {
        metric.filters = this.defaultFilters()
      }
      metric.spmLine = this.formatLine(metric)
      this.emit('metric', metric)
      if (metric.name) {
        this.emit(metric.name, metric)
      }
    },
    collectdFormatLine: function (metric) {
      var now = (new Date().getTime()).toFixed(0)
      var metricsTs = (metric.ts).toFixed(0)
      var metricsTs2 = (metric.ts / 1000).toFixed(0)
      var dateString = moment(metric.ts).format('YYYY-MM-DD')
      var line = ''
      if ((/collectd.*\-/.test(metric.name))) {
        if (/collectd\-cpu/.test(metric.name)) {
          line = (now + '\t' + metric.name + '-' + dateString + ',' + metricsTs2 + ',' + metric.value)
        } else if (/disk\-space/.test(metric.name)) {
          line = (now + '\t' + metric.name + '-' + dateString + ',' + metricsTs2 + ',' + metric.value.toFixed(6))
        } else {
          line = now + '\t' + metric.name + '\t' + metricsTs2 + ',' + metric.value
        }
      } else {
        line = now + '\t' + metric.name + '\t' + metricsTs + ',' + metric.value
      }
      return line
    },

    defaultFormatLine: function (metric) {
      var now = (new Date().getTime()).toFixed(0)
      var line = null
      if (metric.sct === 'OS') {
        line = this.collectdFormatLine(metric)
      } else {
        line = util.format('%d\t%s\t%d\t%s\t%s', now, (metric.type || this.defaultType) + '-' + metric.name, metric.ts, formatArray(metric.filters), formatArray(metric.value))
        logger.log(line)
      }
      return line
    },
    /**
     * formats a line for SPM sender, if the plugin does not support this method the default formatter for nodejs and OP metrics is used
     * @maram metric to format
     */
    formatLine: function (metric) {
      if (!this.plugin.formatLine) {
        return this.defaultFormatLine(metric)
      } else {
        return this.plugin.formatLine(metric)
      }
    },

    /**
     * Starts the agent - typically thy create listeners or interval checks for metrics, and use 'addMetrics' to inform listeners
     */
    start: function () {
      if (this.plugin && this.plugin.start) {
        // this is the place to register for external events that return metrics
        this.plugin.start(this)
      } else {
        console.log('NOT called start')
      }
    },
    /**
     * Stops the plugin - timers could be pushed to plugins 'timers' Array, if timers exists it tries to clearInterval(timerId)
     * if no timers are defined it tries to call stop method of the plugin
     */
    stop: function () {
      if (this.plugin.timers) {
        this.plugin.timers.forEach(function (tid) {
          clearInterval(tid)
        })
      } else {
        if (this.plugin.stop) {
          this.plugin.stop()
        } else {
          logger.warn('could not stop agent')
        }
      }
    }
  }
  return util._extend(eventEmitter, agentSuperClass)
}

module.exports = Agent
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013-2014 node-coap contributors.
 *
 * node-coap is licensed under an MIT +no-false-attribs license.
 * All rights not explicitly granted in the MIT license are reserved.
 * See the included LICENSE file for more details.
 */

const bl              = require('bl')
    , util            = require('util')
    , events          = require('events')
    , dgram           = require('dgram')
    , parse           = require('coap-packet').parse
    , generate        = require('coap-packet').generate
    , URL             = require('url')
    , IncomingMessage = require('./incoming_message')
    , OutgoingMessage = require('./outgoing_message')
    , ObserveStream   = require('./observe_read_stream')
    , parameters      = require('./parameters')
    , optionsConv     = require('./option_converter')
    , RetrySend       = require('./retry_send')
    , maxToken        = Math.pow(2, 32)
    , maxMessageId    = Math.pow(2, 16)

function Agent(opts) {
  if (!(this instanceof Agent))
    return new Agent()

  if (!opts)
    opts = {}

  if (!opts.type)
    opts.type = 'udp4'

  this._opts = opts

  this._init()
}

util.inherits(Agent, events.EventEmitter)

Agent.prototype._init = function initSock() {
  if (this._sock)
    return

  var that = this
  this._sock = dgram.createSocket(this._opts.type, function(msg, rsinfo) {
    var packet
      , message

    try {
      packet = parse(msg)
    } catch(err) {
      message = generate({ code: '5.00', payload: new Buffer('Unable to parse packet') })
      that._sock.send(message, 0, message.length,
                      rsinfo.port, rsinfo.address)
      return
    }
    that._handle(msg, rsinfo)
  })

  if(this._opts.port){
    this._sock.bind( this._opts.port );
  };

  this._sock.on('error', function(err) {
    // we are skipping DNS errors
    if(err.code !== 'ENOTFOUND')
      that.emit('error', err)
  })

  this._msgIdToReq = {}
  this._tkToReq = {}

  this._lastToken = Math.floor(Math.random() * (maxToken - 1))
  this._lastMessageId = Math.floor(Math.random() * (maxMessageId - 1))

  this._closing = false
  this._msgInFlight = 0
  this._requests = 0
}

Agent.prototype._cleanUp = function cleanUp() {
  if (--this._requests !== 0)
    return

  this._closing = true

  if (this._msgInFlight !== 0)
    return

  this._doClose()
}

Agent.prototype._doClose = function() {
  for (var k in this._msgIdToReq)
    this._msgIdToReq[k].sender.reset()

  this._sock.close()
  this._sock = null
}

Agent.prototype._handle = function handle(msg, rsinfo) {
  var packet = parse(msg)
    , buf
    , response
    , that = this
    , req = this._msgIdToReq[packet.messageId]
    , ackSent = function(err) {
        if (err && req)
          req.emit('error', err)

        that._msgInFlight--
        if (that._closing && that._msgInFlight === 0) {
          that._doClose()
        }
      }

  if (packet.confirmable) {
    buf = generate({
        code: '0.00'
      , ack: true
      , messageId: packet.messageId
    })

    this._msgInFlight++
    this._sock.send(buf, 0, buf.length, rsinfo.port, rsinfo.address, ackSent)
  }

  if (!req) {
    if (packet.token.length == 4) {
      req = this._tkToReq[packet.token.readUInt32BE(0)]
    }

    if (packet.ack && !req) {
      // nothing to do, somehow there was
      // a duplicate ack
      return
    }

    if (!req) {
      buf = generate({
          code: '0.00'
        , reset: true
        , messageId: packet.messageId
      })

      this._msgInFlight++
      this._sock.send(buf, 0, buf.length, rsinfo.port, rsinfo.address, ackSent)
      return
    }
  }

  if (!packet.confirmable)
    delete this._msgIdToReq[packet.messageId]

  req.sender.reset()

  if (packet.code == '0.00')
    return

  if (req.response)
    // it is an observe request
    // and we are already streaming
    return req.response.append(packet)
  else if (!req.url.observe)
    // it is not, so delete the token
    delete that._tkToReq[packet.token.readUInt32BE(0)]

  if (req.url.observe && packet.code !== '4.04') {
    response = new ObserveStream(packet, rsinfo)
    response.on('close', function() {
      delete that._tkToReq[packet.token.readUInt32BE(0)]
      that._cleanUp()
    })
  } else
    response = new IncomingMessage(packet, rsinfo)

  req.response = response
  req.emit('response', response)
}

Agent.prototype._nextToken = function nextToken() {
  var buf = new Buffer(4)

  if (++this._lastToken === maxToken)
    this._lastToken = 0

  buf.writeUInt32BE(this._lastToken, 0)

  return buf;
}

Agent.prototype._nextMessageId = function nextToken() {
  if (++this._lastMessageId === maxMessageId)
    this._lastMessageId = 1

  return this._lastMessageId
}

Agent.prototype.request = function request(url) {
  this._init()

  var req, response
    , that = this

  req = new OutgoingMessage({}, function(req, packet) {
    var buf

    if (url.confirmable !== false) {
      packet.confirmable = true
    }

    if (!(packet.ack || packet.reset)) {
      packet.messageId = that._nextMessageId()
      packet.token = that._nextToken()
    }

    try {
      buf = generate(packet)
    } catch(err) {
      req.sender.reset()
      return req.emit('error', err)
    }

    that._msgIdToReq[packet.messageId] = req
    that._tkToReq[that._lastToken] = req

    req.sender.send(buf)
  })

  req.sender = new RetrySend(this._sock, url.port, url.hostname || url.host)

  req.url = url

  req.statusCode = url.method || 'GET'

  urlPropertyToPacketOption(url, req, 'pathname', 'Uri-Path', '/')
  urlPropertyToPacketOption(url, req, 'query', 'Uri-Query', '&')

  req.sender.on('error', req.emit.bind(req, 'error'))

  req.sender.on('sending', function() {
    that._msgInFlight++
  })

  req.sender.on('sent', function() {
    that._msgInFlight--
    if (that._closing && that._msgInFlight === 0) {
      that._doClose()
    }
  })

  if (url.observe)
    req.setOption('Observe', null)
  else
    req.on('response', this._cleanUp.bind(this))

  this._requests++

  return req
}

function urlPropertyToPacketOption(url, req, property, option, separator) {
  if (url[property])
    req.setOption(option, url[property].split(separator)
         .filter(function(part) { return part !== '' })
         .map(function(part) {

      var buf = new Buffer(Buffer.byteLength(part))
      buf.write(part)
      return buf
    }))
}

module.exports = Agent
<commit_msg>check block2 from agent<commit_after>/*
 * Copyright (c) 2013-2014 node-coap contributors.
 *
 * node-coap is licensed under an MIT +no-false-attribs license.
 * All rights not explicitly granted in the MIT license are reserved.
 * See the included LICENSE file for more details.
 */

const bl              = require('bl')
    , util            = require('util')
    , events          = require('events')
    , dgram           = require('dgram')
    , parse           = require('coap-packet').parse
    , generate        = require('coap-packet').generate
    , URL             = require('url')
    , IncomingMessage = require('./incoming_message')
    , OutgoingMessage = require('./outgoing_message')
    , ObserveStream   = require('./observe_read_stream')
    , parameters      = require('./parameters')
    , optionsConv     = require('./option_converter')
    , RetrySend       = require('./retry_send')
    , maxToken        = Math.pow(2, 32)
    , maxMessageId    = Math.pow(2, 16)

function Agent(opts) {
  if (!(this instanceof Agent))
    return new Agent()

  if (!opts)
    opts = {}

  if (!opts.type)
    opts.type = 'udp4'

  this._opts = opts

  this._init()
}

util.inherits(Agent, events.EventEmitter)

Agent.prototype._init = function initSock() {
  if (this._sock)
    return

  var that = this
  this._sock = dgram.createSocket(this._opts.type, function(msg, rsinfo) {
    var packet
      , message

    try {
      packet = parse(msg)
    } catch(err) {
      message = generate({ code: '5.00', payload: new Buffer('Unable to parse packet') })
      that._sock.send(message, 0, message.length,
                      rsinfo.port, rsinfo.address)
      return
    }
    that._handle(msg, rsinfo)
  })

  if(this._opts.port){
    this._sock.bind( this._opts.port );
  };

  this._sock.on('error', function(err) {
    // we are skipping DNS errors
    if(err.code !== 'ENOTFOUND')
      that.emit('error', err)
  })

  this._msgIdToReq = {}
  this._tkToReq = {}

  this._lastToken = Math.floor(Math.random() * (maxToken - 1))
  this._lastMessageId = Math.floor(Math.random() * (maxMessageId - 1))

  this._closing = false
  this._msgInFlight = 0
  this._requests = 0
}

Agent.prototype._cleanUp = function cleanUp() {
  if (--this._requests !== 0)
    return

  this._closing = true

  if (this._msgInFlight !== 0)
    return

  this._doClose()
}

Agent.prototype._doClose = function() {
  for (var k in this._msgIdToReq)
    this._msgIdToReq[k].sender.reset()

  this._sock.close()
  this._sock = null
}

Agent.prototype._handle = function handle(msg, rsinfo) {
  var packet = parse(msg)
    , buf
    , response
    , that = this
    , req = this._msgIdToReq[packet.messageId]
    , ackSent = function(err) {
        if (err && req)
          req.emit('error', err)

        that._msgInFlight--
        if (that._closing && that._msgInFlight === 0) {
          that._doClose()
        }
      }

  //check if this is a block

  if (packet.confirmable) {
    buf = generate({
        code: '0.00'
      , ack: true
      , messageId: packet.messageId
    })

    this._msgInFlight++
    this._sock.send(buf, 0, buf.length, rsinfo.port, rsinfo.address, ackSent)
  }

  if (!req) {
    if (packet.token.length == 4) {
      req = this._tkToReq[packet.token.readUInt32BE(0)]
    }

    if (packet.ack && !req) {
      // nothing to do, somehow there was
      // a duplicate ack
      return
    }

    if (!req) {
      buf = generate({
          code: '0.00'
        , reset: true
        , messageId: packet.messageId
      })

      this._msgInFlight++
      this._sock.send(buf, 0, buf.length, rsinfo.port, rsinfo.address, ackSent)
      return
    }
  }

  if (!packet.confirmable)
    delete this._msgIdToReq[packet.messageId]

  console.log(packet);
  if (packet.block2) {
    // console.log('block2 packet number', packet.block2.num)
    // console.log('block2 packet size', packet.block2.size)
    if (packet.block2.moreBlock2)
      console.log('more block2 packet is waiting')

    var originalRequest = req._packet
    console.log(originalRequest)
    var nextRequest = packetSetBlock(originalRequest, packet.block2.num, packet.block2.size)
    console.log('--------nex request')
    console.log(nextRequest)
  }
/*  console.log('----- response')
  console.log(packet)
  console.log('----- for req')
  console.log(req._packet.token)
  console.log(req)
  return
*/
  req.sender.reset()

  if (packet.code == '0.00')
    return

  if (req.response)
    // it is an observe request
    // and we are already streaming
    return req.response.append(packet)
  else if (!req.url.observe)
    // it is not, so delete the token
    delete that._tkToReq[packet.token.readUInt32BE(0)]

  if (req.url.observe && packet.code !== '4.04') {
    response = new ObserveStream(packet, rsinfo)
    response.on('close', function() {
      delete that._tkToReq[packet.token.readUInt32BE(0)]
      that._cleanUp()
    })
  } else
    response = new IncomingMessage(packet, rsinfo)

  req.response = response
  req.emit('response', response)
}

Agent.prototype._nextToken = function nextToken() {
  var buf = new Buffer(4)

  if (++this._lastToken === maxToken)
    this._lastToken = 0

  buf.writeUInt32BE(this._lastToken, 0)

  return buf;
}

Agent.prototype._nextMessageId = function nextToken() {
  if (++this._lastMessageId === maxMessageId)
    this._lastMessageId = 1

  return this._lastMessageId
}

Agent.prototype.request = function request(url) {
  this._init()

  var req, response
    , that = this

  req = new OutgoingMessage({}, function(req, packet) {
    var buf

    if (url.confirmable !== false) {
      packet.confirmable = true
    }

    if (!(packet.ack || packet.reset)) {
      packet.messageId = that._nextMessageId()
      packet.token = that._nextToken()
    }

    try {
      buf = generate(packet)
    } catch(err) {
      req.sender.reset()
      return req.emit('error', err)
    }

    that._msgIdToReq[packet.messageId] = req
    that._tkToReq[that._lastToken] = req

    req.sender.send(buf)
  })

  req.sender = new RetrySend(this._sock, url.port, url.hostname || url.host)

  req.url = url

  req.statusCode = url.method || 'GET'

  urlPropertyToPacketOption(url, req, 'pathname', 'Uri-Path', '/')
  urlPropertyToPacketOption(url, req, 'query', 'Uri-Query', '&')

  req.sender.on('error', req.emit.bind(req, 'error'))

  req.sender.on('sending', function() {
    that._msgInFlight++
  })

  req.sender.on('sent', function() {
    that._msgInFlight--
    if (that._closing && that._msgInFlight === 0) {
      that._doClose()
    }
  })

  if (url.observe)
    req.setOption('Observe', null)
  else
    req.on('response', this._cleanUp.bind(this))

  this._requests++

  return req
}

function urlPropertyToPacketOption(url, req, property, option, separator) {
  if (url[property])
    req.setOption(option, url[property].split(separator)
         .filter(function(part) { return part !== '' })
         .map(function(part) {

      var buf = new Buffer(Buffer.byteLength(part))
      buf.write(part)
      return buf
    }))
}

function packetSetBlock(packet, number, size) {
  console.log('size = ', size)
  console.log('number = ', number)
  if (size < 16) size = 16
  if (size > 1024) size = 1024
  if (number > 0xfffff)
    throw new Error('Block2 number access limit of 1048575, has:', number)

  var buffer = new Buffer(4)
  var block2Value = ((Math.log(size)/Math.log(2)-4)&0x07) + (number+1<<4)
  console.log('block2: ', block2Value)

  buffer.writeUInt32BE(block2Value,0)

  if (block2Value < 0xff) {
    buffer = buffer.slice(3,4);
  }
  else if (block2Value < 0xffff) {
    buffer = buffer.slice(2,4);
  }
  else {
    buffer = buffer.slice(1,4);
  }
  packet.options.push({
    name: 'Block2',
    value: buffer
  })
  return packet
}

module.exports = Agent
<|endoftext|>


<commit_before>var phpspecPlugin = PHPCI.UiPlugin.extend({
    id: 'build-phpspec-errors',
    css: 'col-lg-12 col-md-12 col-sm-12 col-xs-12',
    title: 'PHPSpec',
    lastData: null,
    displayOnUpdate: false,
    box: true,
    rendered: false,

    register: function() {
        var self = this;
        var query = PHPCI.registerQuery('phpspec', -1, {key: 'phpspec'})

        $(window).on('phpspec', function(data) {
            self.onUpdate(data);
        });

        $(window).on('build-updated', function() {
            if (!self.rendered) {
                self.displayOnUpdate = true;
                query();
            }
        });
    },

    render: function() {

        return $('
' +
            '
' +
            '
' +
            '   Suite	' +
            '   Test	' +
            '   Result	' +
            '
' +
            '

');
    },

    onUpdate: function(e) {
        if (!e.queryData) {
            $('#build-phpcs').hide();
            return;
        }

        this.rendered = true;
        this.lastData = e.queryData;

        var tests = this.lastData[0].meta_value;
        var tbody = $('#phpspec-data tbody');
        tbody.empty();

        for (var i in tests.suites) {
			var test_suite = tests.suites[i];

			for(var k in test_suite.cases){
				var test_case = test_suite.cases[k];

				var row = $(
					'
'+
						''+test_suite.name+'	'+
						''+test_case.name+'	'+
						''+(test_case.message?test_case.message:'OK')+'	'+
					'
'
				);

				if (test_case.status!='passed') {
					row.addClass('danger');
				} else {
					row.addClass('success');
				}

				tbody.append(row);
			}
        }
    }
});

PHPCI.registerPlugin(new phpspecPlugin());
<commit_msg>fix removing of element<commit_after>var phpspecPlugin = PHPCI.UiPlugin.extend({
    id: 'build-phpspec-errors',
    css: 'col-lg-12 col-md-12 col-sm-12 col-xs-12',
    title: 'PHPSpec',
    lastData: null,
    displayOnUpdate: false,
    box: true,
    rendered: false,

    register: function() {
        var self = this;
        var query = PHPCI.registerQuery('phpspec', -1, {key: 'phpspec'})

        $(window).on('phpspec', function(data) {
            self.onUpdate(data);
        });

        $(window).on('build-updated', function() {
            if (!self.rendered) {
                self.displayOnUpdate = true;
                query();
            }
        });
    },

    render: function() {

        return $('
' +
            '
' +
            '
' +
            '   Suite	' +
            '   Test	' +
            '   Result	' +
            '
' +
            '

');
    },

    onUpdate: function(e) {
        if (!e.queryData) {
            $('#build-phpspec-errors').hide();
            return;
        }

        this.rendered = true;
        this.lastData = e.queryData;

        var tests = this.lastData[0].meta_value;
        var tbody = $('#phpspec-data tbody');
        tbody.empty();

        for (var i in tests.suites) {
			var test_suite = tests.suites[i];

			for(var k in test_suite.cases){
				var test_case = test_suite.cases[k];

				var row = $(
					'
'+
						''+test_suite.name+'	'+
						''+test_case.name+'	'+
						''+(test_case.message?test_case.message:'OK')+'	'+
					'
'
				);

				if (test_case.status!='passed') {
					row.addClass('danger');
				} else {
					row.addClass('success');
				}

				tbody.append(row);
			}
        }
    }
});

PHPCI.registerPlugin(new phpspecPlugin());
<|endoftext|>


<commit_before>'use strict';

var aplus = exports,
    _Promise;

aplus.Promise = global.Promise;

// Use the promise polyfill for old versions of node.
/* istanbul ignore next */
if (!aplus.Promise) {
    aplus.Promise = require('promise-polyfill');
}

_Promise = aplus.Promise;

aplus.setPromise = function setPromise(newPromise) {
    aplus.Promise = newPromise;
};

aplus.resetPromise = function resetPromise() {
    aplus.Promise = _Promise;
};

aplus.mapLimit = aplus.forEachLimit = aplus.eachLimit = function eachLimit(arr, limit, iterator) {
    if (Number.isNaN(Math.floor(limit))) {
        limit = 1; // Fallback if non number provided
    }
    if (!arr || arr.length === 0) {
        return Promise.resolve([]);
    }
    return new aplus.Promise(function (resolve, reject) {
        var results = [],
            finished = 0,
            started = 0,
            active = 0,
            done = false;

        function onReject(error) {
            done = true;
            reject(error);
        }

        function getCompleter(idx) {
            return function onComplete(value) {
                results[idx] = value;
                active -= 1;
                finished += 1;
                if (!done && finished === arr.length) {
                    done = true;
                    return resolve(results);
                }
                spawn();
            };
        }

        function spawn() {
            while (!done && started < arr.length && active < limit) {
                var index = started;
                active += 1;
                started += 1;
                iterator(arr[index], index, arr).then(getCompleter(index)).catch(onReject);
            }
        }
        spawn();
    });
};

aplus.map = aplus.forEach = aplus.each = function each(arr, iterator) {
    return aplus.eachLimit(arr, Number.POSITIVE_INFINITY, iterator);
};

aplus.mapSeries = aplus.forEachSeries = aplus.eachSeries = function eachSeries(arr, iterator) {
    return aplus.eachLimit(arr, 1, iterator);
};

function _filter(arr) {
    return function (values) {
        return aplus.Promise.resolve(arr.filter(function (_, index) {
            return values[index];
        }));
    };
}

function _reject(arr) {
    return function (values) {
        return aplus.Promise.resolve(arr.filter(function (_, index) {
            return !values[index];
        }));
    };
}

aplus.filter = aplus.select = function select(arr, iterator) {
    return aplus.each(arr, iterator).then(_filter(arr));
};
aplus.filterSeries = aplus.selectSeries = function selectSeries(arr, iterator) {
    return aplus.eachSeries(arr, iterator).then(_filter(arr));
};
aplus.filterLimit = aplus.selectLimit = function selectLimit(arr, limit, iterator) {
    return aplus.eachLimit(arr, limit, iterator).then(_filter(arr));
};

aplus.reject = function reject(arr, iterator) {
    return aplus.each(arr, iterator).then(_reject(arr));
};
aplus.rejectSeries = function rejectSeries(arr, limit, iterator) {
    return aplus.eachSeries(arr, limit, iterator).then(_reject(arr));
};
aplus.rejectLimit = function rejectLimit(arr, limit, iterator) {
    return aplus.eachLimit(arr, limit, iterator).then(_reject(arr));
};

aplus.foldl = aplus.reduce = function reduce(arr, start, iterator) {
    function stepper(item) {
        return iterator(start, item).then(function (value) {
            start = value;
            return aplus.Promise.resolve(value);
        });
    }
    return aplus.eachSeries(arr, stepper).then(function () {
        return aplus.Promise.resolve(start);
    });
};

aplus.foldr = aplus.reduceRight = function reduceRight(arr, start, iterator) {
    arr = (arr || []).reverse();
    return aplus.foldl(arr, start, iterator);
};

function _every(each, arr, iterator) {
    var breakEarly = new Error('early break');

    function stepper(item) {
        return iterator(item).then(function (value) {
            if (!value) {
                return aplus.Promise.reject(breakEarly);
            }
            return aplus.Promise.resolve(value);
        });
    }
    return each(arr, stepper).then(function () {
        return aplus.Promise.resolve(true);
    }).catch(function (err) {
        if (err === breakEarly) {
            return aplus.Promise.resolve(false);
        }
        return aplus.Promise.reject(err);
    });
}

aplus.all = aplus.every = function every(arr, iterator) {
    return _every(aplus.each, arr, iterator);
};
aplus.allSeries = aplus.everySeries = function everySeries(arr, iterator) {
    return _every(aplus.eachSeries, arr, iterator);
};
aplus.allLimit = aplus.everyLimit = function everyLimit(arr, limit, iterator) {
    return _every(function (a, i) {
        return aplus.eachLimit(a, limit, i);
    }, arr, iterator);
};

function _any(each, arr, iterator) {
    var breakEarly = new Error('early break');

    function stepper(item) {
        return iterator(item).then(function (value) {
            if (value) {
                return aplus.Promise.reject(breakEarly);
            }
            return aplus.Promise.resolve(value);
        });
    }
    return each(arr, stepper).then(function () {
        return aplus.Promise.resolve(false);
    }).catch(function (err) {
        if (err === breakEarly) {
            return aplus.Promise.resolve(true);
        }
        return aplus.Promise.reject(err);
    });
}

aplus.any = aplus.some = function some(arr, iterator) {
    return _any(aplus.each, arr, iterator);
};
aplus.anySeries = aplus.someSeries = function someSeries(arr, iterator) {
    return _any(aplus.eachSeries, arr, iterator);
};
aplus.anyLimit = aplus.someLimit = function someLimit(arr, limit, iterator) {
    return _any(function (a, i) {
        return aplus.eachLimit(a, limit, i);
    }, arr, iterator);
};

function _sorter(each, arr, iterator) {
    var original = (arr || []).map(function (value) {
        return {
            original: value,
            key: null
        };
    });
    return each(original, function (value, index) {
        return iterator(arr[index], index, arr).then(function (key) {
            value.key = key;
            return aplus.Promise.resolve(key);
        });
    }).then(function () {
        var sorted = original.sort(function (a, b) {
            if (a.key < b.key) {
                return -1;
            }
            if (a.key > b.key) {
                return 1;
            }
            return 0;
        });
        return aplus.Promise.resolve(sorted.map(function (value) {
            return value.original;
        }));
    });
}
aplus.sort = aplus.sortBy = function sortBy(arr, iterator) {
    return _sorter(aplus.each, arr, iterator);
};
aplus.sortSeries = aplus.sortBySeries = function sortBySeries(arr, iterator) {
    return _sorter(aplus.eachSeries, arr, iterator);
};
aplus.sortLimit = aplus.sortByLimit = function sortByLimit(arr, limit, iterator) {
    return _sorter(function (a, i) {
        return aplus.eachLimit(a, limit, i);
    }, arr, iterator);
};

function _concat(each, arr, iterator) {
    return each(arr, iterator).then(function (values) {
        var result = [].concat.apply([],
            values.map(function (item) {
                if (Array.isArray(item)) {
                    return item;
                }
                return [item];
            }));
        return aplus.Promise.resolve(result);
    });
}

aplus.concat = function concat(arr, iterator) {
    return _concat(aplus.each, arr, iterator);
};
aplus.concatSeries = function concatSeries(arr, iterator) {
    return _concat(aplus.eachSeries, arr, iterator);
};
aplus.concatLimit = function concatLimit(arr, limit, iterator) {
    return _concat(function (a, i) {
        return aplus.eachLimit(a, limit, i);
    }, arr, iterator);
};
<commit_msg> add support for object iteration<commit_after>'use strict';

var aplus = exports,
    _Promise;

aplus.Promise = global.Promise;

// Use the promise polyfill for old versions of node.
/* istanbul ignore next */
if (!aplus.Promise) {
    aplus.Promise = require('promise-polyfill');
}

_Promise = aplus.Promise;

aplus.setPromise = function setPromise(newPromise) {
    aplus.Promise = newPromise;
};

aplus.resetPromise = function resetPromise() {
    aplus.Promise = _Promise;
};

aplus.mapLimit = aplus.forEachLimit = aplus.eachLimit = function eachLimit(arr, limit, iterator) {
    if (Number.isNaN(Math.floor(limit))) {
        limit = 1; // Fallback if non number provided
    }
    if (!arr || arr.length === 0) {
        return Promise.resolve([]);
    }
    var keys = Object.keys(arr);
    return new aplus.Promise(function (resolve, reject) {
        var results = [],
            finished = 0,
            started = 0,
            active = 0,
            done = false;

        function onReject(error) {
            done = true;
            reject(error);
        }

        function getCompleter(idx) {
            return function onComplete(value) {
                results[idx] = value;
                active -= 1;
                finished += 1;
                if (!done && finished === arr.length) {
                    done = true;
                    return resolve(results);
                }
                spawn();
            };
        }

        function spawn() {
            while (!done && started < arr.length && active < limit) {
                var index = started;
                active += 1;
                started += 1;
                iterator(arr[keys[index]], keys[index], arr).then(getCompleter(index)).catch(onReject);
            }
        }
        spawn();
    });
};

aplus.map = aplus.forEach = aplus.each = function each(arr, iterator) {
    return aplus.eachLimit(arr, Number.POSITIVE_INFINITY, iterator);
};

aplus.mapSeries = aplus.forEachSeries = aplus.eachSeries = function eachSeries(arr, iterator) {
    return aplus.eachLimit(arr, 1, iterator);
};

function _filter(arr) {
    return function (values) {
        return aplus.Promise.resolve(arr.filter(function (_, index) {
            return values[index];
        }));
    };
}

function _reject(arr) {
    return function (values) {
        return aplus.Promise.resolve(arr.filter(function (_, index) {
            return !values[index];
        }));
    };
}

aplus.filter = aplus.select = function select(arr, iterator) {
    return aplus.each(arr, iterator).then(_filter(arr));
};
aplus.filterSeries = aplus.selectSeries = function selectSeries(arr, iterator) {
    return aplus.eachSeries(arr, iterator).then(_filter(arr));
};
aplus.filterLimit = aplus.selectLimit = function selectLimit(arr, limit, iterator) {
    return aplus.eachLimit(arr, limit, iterator).then(_filter(arr));
};

aplus.reject = function reject(arr, iterator) {
    return aplus.each(arr, iterator).then(_reject(arr));
};
aplus.rejectSeries = function rejectSeries(arr, limit, iterator) {
    return aplus.eachSeries(arr, limit, iterator).then(_reject(arr));
};
aplus.rejectLimit = function rejectLimit(arr, limit, iterator) {
    return aplus.eachLimit(arr, limit, iterator).then(_reject(arr));
};

aplus.foldl = aplus.reduce = function reduce(arr, start, iterator) {
    function stepper(item) {
        return iterator(start, item).then(function (value) {
            start = value;
            return aplus.Promise.resolve(value);
        });
    }
    return aplus.eachSeries(arr, stepper).then(function () {
        return aplus.Promise.resolve(start);
    });
};

aplus.foldr = aplus.reduceRight = function reduceRight(arr, start, iterator) {
    arr = (arr || []).reverse();
    return aplus.foldl(arr, start, iterator);
};

function _every(each, arr, iterator) {
    var breakEarly = new Error('early break');

    function stepper(item) {
        return iterator(item).then(function (value) {
            if (!value) {
                return aplus.Promise.reject(breakEarly);
            }
            return aplus.Promise.resolve(value);
        });
    }
    return each(arr, stepper).then(function () {
        return aplus.Promise.resolve(true);
    }).catch(function (err) {
        if (err === breakEarly) {
            return aplus.Promise.resolve(false);
        }
        return aplus.Promise.reject(err);
    });
}

aplus.all = aplus.every = function every(arr, iterator) {
    return _every(aplus.each, arr, iterator);
};
aplus.allSeries = aplus.everySeries = function everySeries(arr, iterator) {
    return _every(aplus.eachSeries, arr, iterator);
};
aplus.allLimit = aplus.everyLimit = function everyLimit(arr, limit, iterator) {
    return _every(function (a, i) {
        return aplus.eachLimit(a, limit, i);
    }, arr, iterator);
};

function _any(each, arr, iterator) {
    var breakEarly = new Error('early break');

    function stepper(item) {
        return iterator(item).then(function (value) {
            if (value) {
                return aplus.Promise.reject(breakEarly);
            }
            return aplus.Promise.resolve(value);
        });
    }
    return each(arr, stepper).then(function () {
        return aplus.Promise.resolve(false);
    }).catch(function (err) {
        if (err === breakEarly) {
            return aplus.Promise.resolve(true);
        }
        return aplus.Promise.reject(err);
    });
}

aplus.any = aplus.some = function some(arr, iterator) {
    return _any(aplus.each, arr, iterator);
};
aplus.anySeries = aplus.someSeries = function someSeries(arr, iterator) {
    return _any(aplus.eachSeries, arr, iterator);
};
aplus.anyLimit = aplus.someLimit = function someLimit(arr, limit, iterator) {
    return _any(function (a, i) {
        return aplus.eachLimit(a, limit, i);
    }, arr, iterator);
};

function _sorter(each, arr, iterator) {
    var original = (arr || []).map(function (value) {
        return {
            original: value,
            key: null
        };
    });
    return each(original, function (value, index) {
        return iterator(arr[index], index, arr).then(function (key) {
            value.key = key;
            return aplus.Promise.resolve(key);
        });
    }).then(function () {
        var sorted = original.sort(function (a, b) {
            if (a.key < b.key) {
                return -1;
            }
            if (a.key > b.key) {
                return 1;
            }
            return 0;
        });
        return aplus.Promise.resolve(sorted.map(function (value) {
            return value.original;
        }));
    });
}
aplus.sort = aplus.sortBy = function sortBy(arr, iterator) {
    return _sorter(aplus.each, arr, iterator);
};
aplus.sortSeries = aplus.sortBySeries = function sortBySeries(arr, iterator) {
    return _sorter(aplus.eachSeries, arr, iterator);
};
aplus.sortLimit = aplus.sortByLimit = function sortByLimit(arr, limit, iterator) {
    return _sorter(function (a, i) {
        return aplus.eachLimit(a, limit, i);
    }, arr, iterator);
};

function _concat(each, arr, iterator) {
    return each(arr, iterator).then(function (values) {
        var result = [].concat.apply([],
            values.map(function (item) {
                if (Array.isArray(item)) {
                    return item;
                }
                return [item];
            }));
        return aplus.Promise.resolve(result);
    });
}

aplus.concat = function concat(arr, iterator) {
    return _concat(aplus.each, arr, iterator);
};
aplus.concatSeries = function concatSeries(arr, iterator) {
    return _concat(aplus.eachSeries, arr, iterator);
};
aplus.concatLimit = function concatLimit(arr, limit, iterator) {
    return _concat(function (a, i) {
        return aplus.eachLimit(a, limit, i);
    }, arr, iterator);
};
<|endoftext|>