<commit_before>'use strict';

var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var Promise = require('rsvp').Promise;
var Plugin = require('broccoli-plugin');
var helpers = require('broccoli-kitchen-sink-helpers');
var walkSync = require('walk-sync');
var mapSeries = require('promise-map-series');
var symlinkOrCopySync = require('symlink-or-copy').sync;
var copyDereferenceSync = require('copy-dereference').sync;
var Cache = require('./lib/cache');
var debugGenerator = require('debug');
var keyForFile = require('./lib/key-for-file');
var md5Hex = require('md5-hex');
var Processor = require('./lib/processor');
var defaultProccessor = require('./lib/strategies/default');
var hashForDep = require('hash-for-dep');

module.exports = Filter;

Filter.prototype = Object.create(Plugin.prototype);
Filter.prototype.constructor = Filter;
function Filter(inputTree, options) {
  if (!this || !(this instanceof Filter) ||
      Object.getPrototypeOf(this) === Filter.prototype) {
    throw new TypeError('Filter is an abstract class and must be sub-classed');
  }

  var name = 'broccoli-persistent-filter:' + (this.constructor.name);
  if (this.description) {
    name += ' > [' + this.description + ']';
  }

  this._debug = debugGenerator(name);

  Plugin.call(this, [inputTree]);

  this.processor = new Processor(options);
  this.processor.setStrategy(defaultProccessor);

  /* Destructuring assignment in node 0.12.2 would be really handy for this! */
  if (options) {
    if (options.extensions != null)
      this.extensions = options.extensions;
    if (options.targetExtension != null)
      this.targetExtension = options.targetExtension;
    if (options.inputEncoding != null)
      this.inputEncoding = options.inputEncoding;
    if (options.outputEncoding != null)
      this.outputEncoding = options.outputEncoding;
    if (options.persist) {
      this.processor.setStrategy(require('./lib/strategies/persistent'));
    }
  }

  this.processor.init(this);

  this._cache = new Cache();
  this._canProcessCache = Object.create(null);
  this._destFilePathCache = Object.create(null);
}

Filter.prototype.build = function build() {
  var self = this;
  var srcDir = this.inputPaths[0];
  var destDir = this.outputPath;
  var paths = walkSync(srcDir);

  this._cache.deleteExcept(paths).forEach(function(key) {
    fs.unlinkSync(this.cachePath + '/' + key);
  }, this);

  return mapSeries(paths, function rebuildEntry(relativePath) {
    var destPath = destDir + '/' + relativePath;
    if (relativePath.slice(-1) === '/') {
      mkdirp.sync(destPath);
    } else {
      if (self.canProcessFile(relativePath)) {
        return self.processAndCacheFile(srcDir, destDir, relativePath);
      } else {
        var srcPath = srcDir + '/' + relativePath;
        symlinkOrCopySync(srcPath, destPath);
      }
    }
  });
};

/*
 The cache key to be used for this plugins set of dependencies. By default
 a hash is created based on `package.json` and nested dependencies.

 Implement this to customize the cache key (for example if you need to
 account for non-NPM dependencies).

 @public
 @method cacheKey
 @returns {String}
 */
Filter.prototype.cacheKey = function() {
  return hashForDep(this.baseDir());
};

/* @public
 *
 * @method baseDir
 * @returns {String} absolute path to the root of the filter...
 */
Filter.prototype.baseDir = function() {
  throw Error('Filter must implement prototype.baseDir');
};

/**
 * @public
 *
 * optionally override this to build a more rhobust cache key
 * @param  {String} string The contents of a file that is being processed
 * @return {String}        A cache key
 */
Filter.prototype.cacheKeyProcessString = function(string, relativePath) {
  return md5Hex(string + 0x00 + relativePath);
};

Filter.prototype.canProcessFile =
    function canProcessFile(relativePath) {
  return !!this.getDestFilePath(relativePath);
};

Filter.prototype.getDestFilePath = function getDestFilePath(relativePath) {
  if (this.extensions == null) return relativePath;

  for (var i = 0, ii = this.extensions.length; i < ii; ++i) {
    var ext = this.extensions[i];
    if (relativePath.slice(-ext.length - 1) === '.' + ext) {
      if (this.targetExtension != null) {
        relativePath =
            relativePath.slice(0, -ext.length) + this.targetExtension;
      }
      return relativePath;
    }
  }
  return null;
};

Filter.prototype.processAndCacheFile =
    function processAndCacheFile(srcDir, destDir, relativePath) {
  var self = this;
  var cacheEntry = this._cache.get(relativePath);
  var outputRelativeFile = self.getDestFilePath(relativePath);

  if (cacheEntry) {
    var hashResult = hash(srcDir, cacheEntry.inputFile);

    if (cacheEntry.hash.hash === hashResult.hash) {
      this._debug('cache hit: %s', relativePath);

      return symlinkOrCopyFromCache(cacheEntry, destDir, outputRelativeFile);
    } else {
      this._debug('cache miss: %s \n  - previous: %o \n  - next:     %o ', relativePath, cacheEntry.hash.key, hashResult.key);
    }

  } else {
    this._debug('cache prime: %s', relativePath);
  }

  return Promise.resolve().
      then(function asyncProcessFile() {
        return self.processFile(srcDir, destDir, relativePath);
      }).
      then(copyToCache,
      // TODO(@caitp): error wrapper is for API compat, but is not particularly
      // useful.
      // istanbul ignore next
      function asyncProcessFileErrorWrapper(e) {
        if (typeof e !== 'object') e = new Error('' + e);
        e.file = relativePath;
        e.treeDir = srcDir;
        throw e;
      });

  function copyToCache() {
    var entry = {
      hash: hash(srcDir, relativePath),
      inputFile: relativePath,
      outputFile: destDir + '/' + outputRelativeFile,
      cacheFile: self.cachePath + '/' + outputRelativeFile
    };

    if (fs.existsSync(entry.cacheFile)) {
      fs.unlinkSync(entry.cacheFile);
    } else {
      mkdirp.sync(path.dirname(entry.cacheFile));
    }

    copyDereferenceSync(entry.outputFile, entry.cacheFile);

    return self._cache.set(relativePath, entry);
  }
};

function invoke(context, fn, args) {
  return new Promise(function(resolve) {
    resolve(fn.apply(context, args));
  });
}

Filter.prototype.processFile =
    function processFile(srcDir, destDir, relativePath) {
  var self = this;
  var inputEncoding = this.inputEncoding;
  var outputEncoding = this.outputEncoding;
  if (inputEncoding === void 0)  { inputEncoding  = 'utf8'; }
  if (outputEncoding === void 0) { outputEncoding = 'utf8'; }
  var contents = fs.readFileSync(
      srcDir + '/' + relativePath, { encoding: inputEncoding });

  var string = invoke(this.processor, this.processor.processString, [this, contents, relativePath]);

  return string.then(function asyncOutputFilteredFile(outputString) {
    var outputPath = self.getDestFilePath(relativePath);
    if (outputPath == null) {
      throw new Error('canProcessFile("' + relativePath + '") is true, but getDestFilePath("' + relativePath + '") is null');
    }
    outputPath = destDir + '/' + outputPath;
    mkdirp.sync(path.dirname(outputPath));
    fs.writeFileSync(outputPath, outputString, {
      encoding: outputEncoding
    });

    return outputString;
  });
};

Filter.prototype.processString =
    function unimplementedProcessString(contents, relativePath) {
  throw new Error(
      'When subclassing cauliflower-filter you must implement the ' +
      '`processString()` method.');
};

function hash(src, filePath) {
  var path = src + '/' + filePath;
  var key = keyForFile(path);

  return {
    key: key,
    hash: helpers.hashStrings([
      path,
      key.size,
      key.mode,
      key.mtime
    ])
  };
}

function symlinkOrCopyFromCache(entry, dest, relativePath) {
  mkdirp.sync(path.dirname(entry.outputFile));

  symlinkOrCopySync(entry.cacheFile, dest + '/' + relativePath);
}
<commit_msg>add try/catch around fs.unlinkSync in Filter.prototype.build to protect against ENOENT, no such file or directory<commit_after>'use strict';

var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var Promise = require('rsvp').Promise;
var Plugin = require('broccoli-plugin');
var helpers = require('broccoli-kitchen-sink-helpers');
var walkSync = require('walk-sync');
var mapSeries = require('promise-map-series');
var symlinkOrCopySync = require('symlink-or-copy').sync;
var copyDereferenceSync = require('copy-dereference').sync;
var Cache = require('./lib/cache');
var debugGenerator = require('debug');
var keyForFile = require('./lib/key-for-file');
var md5Hex = require('md5-hex');
var Processor = require('./lib/processor');
var defaultProccessor = require('./lib/strategies/default');
var hashForDep = require('hash-for-dep');

module.exports = Filter;

Filter.prototype = Object.create(Plugin.prototype);
Filter.prototype.constructor = Filter;
function Filter(inputTree, options) {
  if (!this || !(this instanceof Filter) ||
      Object.getPrototypeOf(this) === Filter.prototype) {
    throw new TypeError('Filter is an abstract class and must be sub-classed');
  }

  var name = 'broccoli-persistent-filter:' + (this.constructor.name);
  if (this.description) {
    name += ' > [' + this.description + ']';
  }

  this._debug = debugGenerator(name);

  Plugin.call(this, [inputTree]);

  this.processor = new Processor(options);
  this.processor.setStrategy(defaultProccessor);

  /* Destructuring assignment in node 0.12.2 would be really handy for this! */
  if (options) {
    if (options.extensions != null)
      this.extensions = options.extensions;
    if (options.targetExtension != null)
      this.targetExtension = options.targetExtension;
    if (options.inputEncoding != null)
      this.inputEncoding = options.inputEncoding;
    if (options.outputEncoding != null)
      this.outputEncoding = options.outputEncoding;
    if (options.persist) {
      this.processor.setStrategy(require('./lib/strategies/persistent'));
    }
  }

  this.processor.init(this);

  this._cache = new Cache();
  this._canProcessCache = Object.create(null);
  this._destFilePathCache = Object.create(null);
}

Filter.prototype.build = function build() {
  var self = this;
  var srcDir = this.inputPaths[0];
  var destDir = this.outputPath;
  var paths = walkSync(srcDir);

  this._cache.deleteExcept(paths).forEach(function(key) {
    try {
      fs.unlinkSync(this.cachePath + '/' + key);
    } catch (e) {
      if(e.code !== 'ENOENT') {
        throw e;
      }
    }
  }, this);

  return mapSeries(paths, function rebuildEntry(relativePath) {
    var destPath = destDir + '/' + relativePath;
    if (relativePath.slice(-1) === '/') {
      mkdirp.sync(destPath);
    } else {
      if (self.canProcessFile(relativePath)) {
        return self.processAndCacheFile(srcDir, destDir, relativePath);
      } else {
        var srcPath = srcDir + '/' + relativePath;
        symlinkOrCopySync(srcPath, destPath);
      }
    }
  });
};

/*
 The cache key to be used for this plugins set of dependencies. By default
 a hash is created based on `package.json` and nested dependencies.

 Implement this to customize the cache key (for example if you need to
 account for non-NPM dependencies).

 @public
 @method cacheKey
 @returns {String}
 */
Filter.prototype.cacheKey = function() {
  return hashForDep(this.baseDir());
};

/* @public
 *
 * @method baseDir
 * @returns {String} absolute path to the root of the filter...
 */
Filter.prototype.baseDir = function() {
  throw Error('Filter must implement prototype.baseDir');
};

/**
 * @public
 *
 * optionally override this to build a more rhobust cache key
 * @param  {String} string The contents of a file that is being processed
 * @return {String}        A cache key
 */
Filter.prototype.cacheKeyProcessString = function(string, relativePath) {
  return md5Hex(string + 0x00 + relativePath);
};

Filter.prototype.canProcessFile =
    function canProcessFile(relativePath) {
  return !!this.getDestFilePath(relativePath);
};

Filter.prototype.getDestFilePath = function getDestFilePath(relativePath) {
  if (this.extensions == null) return relativePath;

  for (var i = 0, ii = this.extensions.length; i < ii; ++i) {
    var ext = this.extensions[i];
    if (relativePath.slice(-ext.length - 1) === '.' + ext) {
      if (this.targetExtension != null) {
        relativePath =
            relativePath.slice(0, -ext.length) + this.targetExtension;
      }
      return relativePath;
    }
  }
  return null;
};

Filter.prototype.processAndCacheFile =
    function processAndCacheFile(srcDir, destDir, relativePath) {
  var self = this;
  var cacheEntry = this._cache.get(relativePath);
  var outputRelativeFile = self.getDestFilePath(relativePath);

  if (cacheEntry) {
    var hashResult = hash(srcDir, cacheEntry.inputFile);

    if (cacheEntry.hash.hash === hashResult.hash) {
      this._debug('cache hit: %s', relativePath);

      return symlinkOrCopyFromCache(cacheEntry, destDir, outputRelativeFile);
    } else {
      this._debug('cache miss: %s \n  - previous: %o \n  - next:     %o ', relativePath, cacheEntry.hash.key, hashResult.key);
    }

  } else {
    this._debug('cache prime: %s', relativePath);
  }

  return Promise.resolve().
      then(function asyncProcessFile() {
        return self.processFile(srcDir, destDir, relativePath);
      }).
      then(copyToCache,
      // TODO(@caitp): error wrapper is for API compat, but is not particularly
      // useful.
      // istanbul ignore next
      function asyncProcessFileErrorWrapper(e) {
        if (typeof e !== 'object') e = new Error('' + e);
        e.file = relativePath;
        e.treeDir = srcDir;
        throw e;
      });

  function copyToCache() {
    var entry = {
      hash: hash(srcDir, relativePath),
      inputFile: relativePath,
      outputFile: destDir + '/' + outputRelativeFile,
      cacheFile: self.cachePath + '/' + outputRelativeFile
    };

    if (fs.existsSync(entry.cacheFile)) {
      fs.unlinkSync(entry.cacheFile);
    } else {
      mkdirp.sync(path.dirname(entry.cacheFile));
    }

    copyDereferenceSync(entry.outputFile, entry.cacheFile);

    return self._cache.set(relativePath, entry);
  }
};

function invoke(context, fn, args) {
  return new Promise(function(resolve) {
    resolve(fn.apply(context, args));
  });
}

Filter.prototype.processFile =
    function processFile(srcDir, destDir, relativePath) {
  var self = this;
  var inputEncoding = this.inputEncoding;
  var outputEncoding = this.outputEncoding;
  if (inputEncoding === void 0)  { inputEncoding  = 'utf8'; }
  if (outputEncoding === void 0) { outputEncoding = 'utf8'; }
  var contents = fs.readFileSync(
      srcDir + '/' + relativePath, { encoding: inputEncoding });

  var string = invoke(this.processor, this.processor.processString, [this, contents, relativePath]);

  return string.then(function asyncOutputFilteredFile(outputString) {
    var outputPath = self.getDestFilePath(relativePath);
    if (outputPath == null) {
      throw new Error('canProcessFile("' + relativePath + '") is true, but getDestFilePath("' + relativePath + '") is null');
    }
    outputPath = destDir + '/' + outputPath;
    mkdirp.sync(path.dirname(outputPath));
    fs.writeFileSync(outputPath, outputString, {
      encoding: outputEncoding
    });

    return outputString;
  });
};

Filter.prototype.processString =
    function unimplementedProcessString(contents, relativePath) {
  throw new Error(
      'When subclassing cauliflower-filter you must implement the ' +
      '`processString()` method.');
};

function hash(src, filePath) {
  var path = src + '/' + filePath;
  var key = keyForFile(path);

  return {
    key: key,
    hash: helpers.hashStrings([
      path,
      key.size,
      key.mode,
      key.mtime
    ])
  };
}

function symlinkOrCopyFromCache(entry, dest, relativePath) {
  mkdirp.sync(path.dirname(entry.outputFile));

  symlinkOrCopySync(entry.cacheFile, dest + '/' + relativePath);
}
<|endoftext|>


<commit_before>var path = require('path');
var fs = require('fs');
var builder = require('xmlbuilder');

var TRXReporter = function (baseReporterDecorator, config, emitter, logger, helper, formatError) {
    var outputFile = config.outputFile;
    var log = logger.create('reporter.trx');
    var hostName = require('os').hostname();
    var testRun;
    var resultSummary;
    var counters;
    var testDefinitions;
    var testListIdNotInAList;
    var testEntries;
    var results;
    var times;

    var getTimestamp = function () {
        // todo: use local time ?
        return (new Date()).toISOString().substr(0, 19);
    }

    var s4 = function () {
        return Math.floor((1 + Math.random()) * 0x10000)
            .toString(16)
            .substring(1);
    };

    var newGuid = function () {
        return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
    };

    var formatDuration = function (duration) {
        duration = duration | 0;
        var ms = duration % 1000;
        duration -= ms;
        var s = (duration / 1000) % 60;
        duration -= s * 1000;
        var m = (duration / 60000) % 60;
        duration -= m * 60000;
        var h = (duration / 3600000) % 24;
        duration -= h * 3600000;
        var d = duration / 86400000;

        return (d > 0 ? d + '.' : '') +
            (h < 10 ? '0' + h : h) + ':' +
            (m < 10 ? '0' + m : m) + ':' +
            (s < 10 ? '0' + s : s) + '.' +
            (ms < 10 ? '00' + ms : ms < 100 ? '0' + ms : ms);
    };

    baseReporterDecorator(this);

    this.onRunStart = function () {
        var userName = process.env['USERNAME'];
        var runStartTimestamp = getTimestamp();
        testRun = builder.create("TestRun", {version: '1.0', encoding: 'UTF-8'})
            .att('id', newGuid())
            .att('name', userName + '@' + hostName + ' ' + runStartTimestamp)
            .att('runUser', userName)
            .att('xmlns', 'http://microsoft.com/schemas/VisualStudio/TeamTest/2010');

        testRun.ele('TestSettings')
            .att('name', 'Karma Test Run')
            .att('id', newGuid());

        times = testRun.ele('Times')
        times.att('creation', runStartTimestamp)
        times.att('queuing', runStartTimestamp)
        times.att('start', runStartTimestamp);

        resultSummary = testRun.ele('ResultSummary');
        counters = resultSummary.ele('Counters');
        testDefinitions = testRun.ele('TestDefinitions');

        testListIdNotInAList = "8c84fa94-04c1-424b-9868-57a2d4851a1d";
        var testLists = testRun.ele('TestLists');

        testLists.ele('TestList')
            .att('name', 'Results Not in a List')
            .att('id', testListIdNotInAList);

        // seems to be VS is expecting that exact id
        testLists.ele('TestList')
            .att('name', 'All Loaded Results')
            .att('id', "19431567-8539-422a-85d7-44ee4e166bda");

        testEntries = testRun.ele('TestEntries');
        results = testRun.ele('Results');
    };

    this.onBrowserStart = function(browser) {
    };

    this.onBrowserComplete = function (browser) {
        var result = browser.lastResult;

        var passed = result.failed <= 0 && !result.error;
        resultSummary.att('outcome', passed ? 'Passed' : 'Failed');

        // todo: checkout if all theses numbers map well
        counters.att('total', result.total)
            .att('executed', result.total - result.skipped)
            .att('passed', result.success)
            .att('error', result.error ? 1 : 0)
            .att('failed', result.failed);

        // possible useful info:
        // todo: result.disconnected => this seems to happen occasionally? => Possibly handle it!
        // (result.netTime || 0) / 1000)
    };

    this.onRunComplete = function () {
        times.att('finish', getTimestamp());
        var xmlToOutput = testRun;

        helper.mkdirIfNotExists(path.dirname(outputFile), function () {
            fs.writeFile(outputFile, xmlToOutput.end({pretty: true}), function (err) {
                if (err) {
                    log.warn('Cannot write TRX testRun\n\t' + err.message);
                } else {
                    log.debug('TRX results written to "%s".', outputFile);
                }
            });
        });
    };

    this.specSuccess = this.specSkipped = this.specFailure = function (browser, result) {
        var unitTestId = newGuid();
        var unitTestName = browser.name + '_' + result.description;
        var className = result.suite.join('.');
        var codeBase = className + '.' + unitTestName;

        var unitTest = testDefinitions.ele('UnitTest')
            .att('name', unitTestName)
            .att('id', unitTestId);
        var executionId = newGuid();
        unitTest.ele('Execution')
            .att('id', executionId);
        unitTest.ele('TestMethod')
            .att('codeBase', codeBase)
            .att('name', unitTestName)
            .att('className', className);

        testEntries.ele('TestEntry')
            .att('testId', unitTestId)
            .att('executionId', executionId)
            .att('testListId', testListIdNotInAList);

        var unitTestResult = results.ele('UnitTestResult')
            .att('executionId', executionId)
            .att('testId', unitTestId)
            .att('testName', unitTestName)
            .att('computerName', hostName)
            .att('duration', formatDuration(result.time || 0))
            // todo: are there other test types?
            .att('testType', '13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b') // that guid seems to represent 'unit test'
            .att('outcome', result.skipped ? 'NotExecuted' : (result.success ? 'Passed' : 'Failed'))
            .att('testListId', testListIdNotInAList);

        if (!result.success) {
            unitTestResult.ele('Output')
                .ele('ErrorInfo')
                .ele('Message', formatError(result.log[0]))
        }
    };
};

TRXReporter.$inject = ['baseReporterDecorator', 'config.trxReporter', 'emitter', 'logger',
    'helper', 'formatError'];

// PUBLISH DI MODULE
module.exports = {
    'reporter:trx': ['type', TRXReporter]
};
<commit_msg>Revert "Handled skipped tests"<commit_after>var path = require('path');
var fs = require('fs');
var builder = require('xmlbuilder');

var TRXReporter = function (baseReporterDecorator, config, emitter, logger, helper, formatError) {
    var outputFile = config.outputFile;
    var log = logger.create('reporter.trx');
    var hostName = require('os').hostname();
    var testRun;
    var resultSummary;
    var counters;
    var testDefinitions;
    var testListIdNotInAList;
    var testEntries;
    var results;
    var times;

    var getTimestamp = function () {
        // todo: use local time ?
        return (new Date()).toISOString().substr(0, 19);
    }

    var s4 = function () {
        return Math.floor((1 + Math.random()) * 0x10000)
            .toString(16)
            .substring(1);
    };

    var newGuid = function () {
        return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
    };

    var formatDuration = function (duration) {
        duration = duration | 0;
        var ms = duration % 1000;
        duration -= ms;
        var s = (duration / 1000) % 60;
        duration -= s * 1000;
        var m = (duration / 60000) % 60;
        duration -= m * 60000;
        var h = (duration / 3600000) % 24;
        duration -= h * 3600000;
        var d = duration / 86400000;

        return (d > 0 ? d + '.' : '') +
            (h < 10 ? '0' + h : h) + ':' +
            (m < 10 ? '0' + m : m) + ':' +
            (s < 10 ? '0' + s : s) + '.' +
            (ms < 10 ? '00' + ms : ms < 100 ? '0' + ms : ms);
    };

    baseReporterDecorator(this);

    this.onRunStart = function () {
        var userName = process.env['USERNAME'];
        var runStartTimestamp = getTimestamp();
        testRun = builder.create("TestRun", {version: '1.0', encoding: 'UTF-8'})
            .att('id', newGuid())
            .att('name', userName + '@' + hostName + ' ' + runStartTimestamp)
            .att('runUser', userName)
            .att('xmlns', 'http://microsoft.com/schemas/VisualStudio/TeamTest/2010');

        testRun.ele('TestSettings')
            .att('name', 'Karma Test Run')
            .att('id', newGuid());

        times = testRun.ele('Times')
        times.att('creation', runStartTimestamp)
        times.att('queuing', runStartTimestamp)
        times.att('start', runStartTimestamp);

        resultSummary = testRun.ele('ResultSummary');
        counters = resultSummary.ele('Counters');
        testDefinitions = testRun.ele('TestDefinitions');

        testListIdNotInAList = "8c84fa94-04c1-424b-9868-57a2d4851a1d";
        var testLists = testRun.ele('TestLists');

        testLists.ele('TestList')
            .att('name', 'Results Not in a List')
            .att('id', testListIdNotInAList);

        // seems to be VS is expecting that exact id
        testLists.ele('TestList')
            .att('name', 'All Loaded Results')
            .att('id', "19431567-8539-422a-85d7-44ee4e166bda");

        testEntries = testRun.ele('TestEntries');
        results = testRun.ele('Results');
    };

    this.onBrowserStart = function(browser) {
    };

    this.onBrowserComplete = function (browser) {
        var result = browser.lastResult;

        var passed = result.failed <= 0 && !result.error;
        resultSummary.att('outcome', passed ? 'Passed' : 'Failed');

        // todo: checkout if all theses numbers map well
        counters.att('total', result.total)
            .att('executed', result.total)
            .att('passed', result.total - result.failed)
            .att('error', result.error ? 1 : 0)
            .att('failed', result.failed);

        // possible useful info:
        // todo: result.disconnected => this seems to happen occasionally? => Possibly handle it!
        // (result.netTime || 0) / 1000)
    };

    this.onRunComplete = function () {
        times.att('finish', getTimestamp());
        var xmlToOutput = testRun;

        helper.mkdirIfNotExists(path.dirname(outputFile), function () {
            fs.writeFile(outputFile, xmlToOutput.end({pretty: true}), function (err) {
                if (err) {
                    log.warn('Cannot write TRX testRun\n\t' + err.message);
                } else {
                    log.debug('TRX results written to "%s".', outputFile);
                }
            });
        });
    };

    this.specSuccess = this.specSkipped = this.specFailure = function (browser, result) {
        var unitTestId = newGuid();
        var unitTestName = browser.name + '_' + result.description;
        var className = result.suite.join('.');
        var codeBase = className + '.' + unitTestName;

        var unitTest = testDefinitions.ele('UnitTest')
            .att('name', unitTestName)
            .att('id', unitTestId);
        var executionId = newGuid();
        unitTest.ele('Execution')
            .att('id', executionId);
        unitTest.ele('TestMethod')
            .att('codeBase', codeBase)
            .att('name', unitTestName)
            .att('className', className);

        testEntries.ele('TestEntry')
            .att('testId', unitTestId)
            .att('executionId', executionId)
            .att('testListId', testListIdNotInAList);

        var unitTestResult = results.ele('UnitTestResult')
            .att('executionId', executionId)
            .att('testId', unitTestId)
            .att('testName', unitTestName)
            .att('computerName', hostName)
            .att('duration', formatDuration(result.time || 0))
            .att('startTime', getTimestamp())
            .att('endTime', getTimestamp())
            // todo: are there other test types?
            .att('testType', '13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b') // that guid seems to represent 'unit test'
            // todo: possibly write result.skipped also => Check out how this could happen.
            .att('outcome', result.success ? 'Passed' : 'Failed')
            .att('testListId', testListIdNotInAList);

        if (!result.success) {
            unitTestResult.ele('Output')
                .ele('ErrorInfo')
                .ele('Message', formatError(result.log[0]))
        }
    };
};

TRXReporter.$inject = ['baseReporterDecorator', 'config.trxReporter', 'emitter', 'logger',
    'helper', 'formatError'];

// PUBLISH DI MODULE
module.exports = {
    'reporter:trx': ['type', TRXReporter]
};
<|endoftext|>


<commit_before>var http = require('http');
http.createServer(function(request, response) {
    response.writeHead(200, {
        'Content-Type': 'text/html',
        'X-Powered-By': 'hope'
    });
    response.end(`

SPOOL

Sprint Zero. No features to see yet.

    `);
}).listen(process.env.PORT || 3000);
<commit_msg>Retitle placeholder page<commit_after>var http = require('http');
http.createServer(function(request, response) {
    response.writeHead(200, {
        'Content-Type': 'text/html',
        'X-Powered-By': 'hope'
    });
    response.end(`

SPOOL!

Sprint Zero. No features to see yet.

    `);
}).listen(process.env.PORT || 3000);
<|endoftext|>


<commit_before>var fs = require('fs'),
    path = require('path'),
    http = require('http'),
    env = process.env

exports.credentialsCallChain = [
  loadCredentialsFromEnv,
  loadCredentialsFromIniFile,
  loadCredentialsFromHttp,
]

exports.regionCallChain = [
  loadRegionFromEnv,
  loadRegionFromIniFile,
]

exports.load = exports.loadCredentialsAndRegion = loadCredentialsAndRegion
exports.loadCredentials = loadCredentials
exports.loadRegion = loadRegion
exports.loadRegionSync = loadRegionSync
exports.loadCredentialsFromEnv = loadCredentialsFromEnv
exports.loadRegionFromEnv = loadRegionFromEnv
exports.loadRegionFromEnvSync = loadRegionFromEnvSync
exports.loadCredentialsFromIniFile = loadCredentialsFromIniFile
exports.loadRegionFromIniFile = loadRegionFromIniFile
exports.loadRegionFromIniFileSync = loadRegionFromIniFileSync
exports.loadCredentialsFromHttp = loadCredentialsFromHttp
exports.loadCredentialsFromEc2Metadata = loadCredentialsFromEc2Metadata
exports.loadCredentialsFromEcs = loadCredentialsFromEcs
exports.loadProfileFromIniFile = loadProfileFromIniFile
exports.loadProfileFromIniFileSync = loadProfileFromIniFileSync
exports.merge = merge

function loadCredentialsAndRegion(options, cb) {
  if (!cb) { cb = options; options = {} }
  cb = once(cb)

  var out = {}, callsRemaining = 2

  function checkDone(propName) {
    return function(err, data) {
      if (err) return cb(err)
      out[propName] = data
      if (!--callsRemaining) return cb(null, out)
    }
  }

  loadCredentials(options, checkDone('credentials'))

  loadRegion(options, checkDone('region'))
}

function loadCredentials(options, cb) {
  if (!cb) { cb = options; options = {} }
  var credentialsCallChain = options.credentialsCallChain || exports.credentialsCallChain

  function nextCall(i) {
    credentialsCallChain[i](options, function(err, credentials) {
      if (err) return cb(err)

      if (credentials.accessKeyId && credentials.secretAccessKey)
        return cb(null, credentials)

      if (i >= credentialsCallChain.length - 1)
        return cb(null, {})

      nextCall(i + 1)
    })
  }
  nextCall(0)
}

function loadRegion(options, cb) {
  if (!cb) { cb = options; options = {} }
  var regionCallChain = options.regionCallChain || exports.regionCallChain

  function nextCall(i) {
    regionCallChain[i](options, function(err, region) {
      if (err) return cb(err)

      if (region)
        return cb(null, region)

      if (i >= regionCallChain.length - 1)
        return cb(null, 'us-east-1')

      nextCall(i + 1)
    })
  }
  nextCall(0)
}

function loadRegionSync(options) {
  return loadRegionFromEnvSync(options) || loadRegionFromIniFileSync(options)
}

function loadCredentialsFromEnv(options, cb) {
  if (!cb) { cb = options; options = {} }

  cb(null, {
    accessKeyId: env.AWS_ACCESS_KEY_ID || env.AMAZON_ACCESS_KEY_ID || env.AWS_ACCESS_KEY,
    secretAccessKey: env.AWS_SECRET_ACCESS_KEY || env.AMAZON_SECRET_ACCESS_KEY || env.AWS_SECRET_KEY,
    sessionToken: env.AWS_SESSION_TOKEN || env.AMAZON_SESSION_TOKEN,
  })
}

function loadRegionFromEnv(options, cb) {
  if (!cb) { cb = options; options = {} }

  cb(null, loadRegionFromEnvSync())
}

function loadRegionFromEnvSync() {
  return env.AWS_REGION || env.AMAZON_REGION || env.AWS_DEFAULT_REGION
}

function loadCredentialsFromIniFile(options, cb) {
  if (!cb) { cb = options; options = {} }

  loadProfileFromIniFile(options, 'credentials', function(err, profile) {
    if (err) return cb(err)
    cb(null, {
      accessKeyId: profile.aws_access_key_id,
      secretAccessKey: profile.aws_secret_access_key,
      sessionToken: profile.aws_session_token,
    })
  })
}

function loadRegionFromIniFile(options, cb) {
  if (!cb) { cb = options; options = {} }

  loadProfileFromIniFile(options, 'config', function(err, profile) {
    if (err) return cb(err)
    cb(null, profile.region)
  })
}

function loadRegionFromIniFileSync(options) {
  return loadProfileFromIniFileSync(options || {}, 'config').region
}

var TIMEOUT_CODES = ['ECONNRESET', 'ETIMEDOUT', 'EHOSTUNREACH', 'Unknown system errno 64']
var ec2Callbacks = []
var ecsCallbacks = []

function loadCredentialsFromHttp(options, cb) {
  return process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI ?
    loadCredentialsFromEcs(options, cb) : loadCredentialsFromEc2Metadata(options, cb)
}

function loadCredentialsFromEc2Metadata(options, cb) {
  if (!cb) { cb = options; options = {} }

  ec2Callbacks.push(cb)
  if (ec2Callbacks.length > 1) return // only want one caller at a time

  cb = function(err, credentials) {
    ec2Callbacks.forEach(function(cb) { cb(err, credentials) })
    ec2Callbacks = []
  }

  if (options.timeout == null) options.timeout = 5000
  options.host = '169.254.169.254'
  options.path = '/latest/meta-data/iam/security-credentials/'

  return request(options, function(err, res, data) {
    if (err && ~TIMEOUT_CODES.indexOf(err.code)) return cb(null, {})
    if (err) return cb(err)

    if (res.statusCode != 200)
      return cb(new Error('Failed to fetch IAM role: ' + res.statusCode + ' ' + data))

    options.path += data.split('\n')[0]
    request(options, function(err, res, data) {
      if (err) return cb(err)

      try { data = JSON.parse(data) } catch (e) { }

      if (res.statusCode != 200 || data.Code != 'Success')
        return cb(new Error('Failed to fetch IAM credentials: ' + res.statusCode + ' ' + data))

      cb(null, {
        accessKeyId: data.AccessKeyId,
        secretAccessKey: data.SecretAccessKey,
        sessionToken: data.Token,
        expiration: new Date(data.Expiration),
      })
    })
  })
}

function loadCredentialsFromEcs(options, cb) {
  if (!cb) { cb = options; options = {} }

  ecsCallbacks.push(cb)
  if (ecsCallbacks.length > 1) return // only want one caller at a time

  cb = function(err, credentials) {
    ecsCallbacks.forEach(function(cb) { cb(err, credentials) })
    ecsCallbacks = []
  }

  if (options.timeout == null) options.timeout = 5000
  options.host = '169.254.170.2'
  options.path = process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI

  return request(options, function(err, res, data) {
    if (err && ~TIMEOUT_CODES.indexOf(err.code)) return cb(null, {})
    if (err) return cb(err)

    if (res.statusCode != 200)
      return cb(new Error('Failed to fetch IAM credentials: ' + res.statusCode + ' ' + data))

    try { data = JSON.parse(data) } catch (e) { }

    cb(null, {
      accessKeyId: data.AccessKeyId,
      secretAccessKey: data.SecretAccessKey,
      sessionToken: data.Token,
      expiration: new Date(data.Expiration),
    })
  })
}

function loadProfileFromIniFile(options, defaultFilename, cb) {
  var filename = options.filename || path.join(resolveHome(), '.aws', defaultFilename),
      profile = options.profile || resolveProfile()

  fs.readFile(filename, 'utf8', function(err, data) {
    if (err && err.code == 'ENOENT') return cb(null, {})
    if (err) return cb(err)
    cb(null, parseAwsIni(data)[profile] || {})
  })
}

function loadProfileFromIniFileSync(options, defaultFilename) {
  var filename = options.filename || path.join(resolveHome(), '.aws', defaultFilename),
      profile = options.profile || resolveProfile(),
      data

  try {
    data = fs.readFileSync(filename, 'utf8')
  } catch (err) {
    if (err.code == 'ENOENT') return {}
    throw err
  }

  return parseAwsIni(data)[profile] || {}
}

function merge(obj, options, cb) {
  if (!cb) { cb = options; options = {} }

  var needRegion = !obj.region
  var needCreds = !obj.credentials || !obj.credentials.accessKeyId || !obj.credentials.secretAccessKey

  function loadCreds(cb) {
    if (needRegion && needCreds) {
      return loadCredentialsAndRegion(options, cb)
    } else if (needRegion) {
      return loadRegion(options, function(err, region) { cb(err, {region: region}) })
    } else if (needCreds) {
      return loadCredentials(options, function(err, credentials) { cb(err, {credentials: credentials}) })
    }
    cb(null, {})
  }

  loadCreds(function(err, creds) {
    if (err) return cb(err)

    if (creds.region) obj.region = creds.region
    if (creds.credentials) {
      if (!obj.credentials) {
        obj.credentials = creds.credentials
      } else {
        Object.keys(creds.credentials).forEach(function(key) {
          if (!obj.credentials[key]) obj.credentials[key] = creds.credentials[key]
        })
      }
    }

    cb()
  })
}

function resolveProfile() {
  return env.AWS_PROFILE || env.AMAZON_PROFILE || 'default'
}

function resolveHome() {
  return env.HOME || env.USERPROFILE || ((env.HOMEDRIVE || 'C:') + env.HOMEPATH)
}

// Fairly strict INI parser – will only deal with alpha keys, must be within sections
function parseAwsIni(ini) {
  var section,
      out = Object.create(null),
      re = /^\[([^\]]+)\]\s*$|^([a-z_]+)\s*=\s*(.+?)\s*$/,
      lines = ini.split(/\r?\n/)

  lines.forEach(function(line) {
    var match = line.match(re)
    if (!match) return
    if (match[1]) {
      section = match[1]
      if (out[section] == null) out[section] = Object.create(null)
    } else if (section) {
      out[section][match[2]] = match[3]
    }
  })

  return out
}

function request(options, cb) {
  cb = once(cb)

  var req = http.request(options, function(res) {
    var data = ''
    res.setEncoding('utf8')
    res.on('error', cb)
    res.on('data', function(chunk) { data += chunk })
    res.on('end', function() { cb(null, res, data) })
  }).on('error', cb)

  if (options.timeout != null) {
    req.setTimeout(options.timeout)
    req.on('timeout', function() { req.abort() })
  }

  req.end()
}

function once(cb) {
  var called = false
  return function() {
    if (called) return
    called = true
    cb.apply(this, arguments)
  }
}
<commit_msg>Refactor http fetching to share code<commit_after>var fs = require('fs'),
    path = require('path'),
    http = require('http'),
    env = process.env

var TIMEOUT_CODES = ['ECONNRESET', 'ETIMEDOUT', 'EHOSTUNREACH', 'Unknown system errno 64']
var httpCallbacks = []

exports.credentialsCallChain = [
  loadCredentialsFromEnv,
  loadCredentialsFromIniFile,
  loadCredentialsFromHttp,
]

exports.regionCallChain = [
  loadRegionFromEnv,
  loadRegionFromIniFile,
]

exports.load = exports.loadCredentialsAndRegion = loadCredentialsAndRegion
exports.loadCredentials = loadCredentials
exports.loadRegion = loadRegion
exports.loadRegionSync = loadRegionSync
exports.loadCredentialsFromEnv = loadCredentialsFromEnv
exports.loadRegionFromEnv = loadRegionFromEnv
exports.loadRegionFromEnvSync = loadRegionFromEnvSync
exports.loadCredentialsFromIniFile = loadCredentialsFromIniFile
exports.loadRegionFromIniFile = loadRegionFromIniFile
exports.loadRegionFromIniFileSync = loadRegionFromIniFileSync
exports.loadCredentialsFromHttp = loadCredentialsFromHttp
exports.loadCredentialsFromEc2Metadata = loadCredentialsFromEc2Metadata
exports.loadCredentialsFromEcs = loadCredentialsFromEcs
exports.loadProfileFromIniFile = loadProfileFromIniFile
exports.loadProfileFromIniFileSync = loadProfileFromIniFileSync
exports.merge = merge

function loadCredentialsAndRegion(options, cb) {
  if (!cb) { cb = options; options = {} }
  cb = once(cb)

  var out = {}, callsRemaining = 2

  function checkDone(propName) {
    return function(err, data) {
      if (err) return cb(err)
      out[propName] = data
      if (!--callsRemaining) return cb(null, out)
    }
  }

  loadCredentials(options, checkDone('credentials'))

  loadRegion(options, checkDone('region'))
}

function loadCredentials(options, cb) {
  if (!cb) { cb = options; options = {} }
  var credentialsCallChain = options.credentialsCallChain || exports.credentialsCallChain

  function nextCall(i) {
    credentialsCallChain[i](options, function(err, credentials) {
      if (err) return cb(err)

      if (credentials.accessKeyId && credentials.secretAccessKey)
        return cb(null, credentials)

      if (i >= credentialsCallChain.length - 1)
        return cb(null, {})

      nextCall(i + 1)
    })
  }
  nextCall(0)
}

function loadRegion(options, cb) {
  if (!cb) { cb = options; options = {} }
  var regionCallChain = options.regionCallChain || exports.regionCallChain

  function nextCall(i) {
    regionCallChain[i](options, function(err, region) {
      if (err) return cb(err)

      if (region)
        return cb(null, region)

      if (i >= regionCallChain.length - 1)
        return cb(null, 'us-east-1')

      nextCall(i + 1)
    })
  }
  nextCall(0)
}

function loadRegionSync(options) {
  return loadRegionFromEnvSync(options) || loadRegionFromIniFileSync(options)
}

function loadCredentialsFromEnv(options, cb) {
  if (!cb) { cb = options; options = {} }

  cb(null, {
    accessKeyId: env.AWS_ACCESS_KEY_ID || env.AMAZON_ACCESS_KEY_ID || env.AWS_ACCESS_KEY,
    secretAccessKey: env.AWS_SECRET_ACCESS_KEY || env.AMAZON_SECRET_ACCESS_KEY || env.AWS_SECRET_KEY,
    sessionToken: env.AWS_SESSION_TOKEN || env.AMAZON_SESSION_TOKEN,
  })
}

function loadRegionFromEnv(options, cb) {
  if (!cb) { cb = options; options = {} }

  cb(null, loadRegionFromEnvSync())
}

function loadRegionFromEnvSync() {
  return env.AWS_REGION || env.AMAZON_REGION || env.AWS_DEFAULT_REGION
}

function loadCredentialsFromIniFile(options, cb) {
  if (!cb) { cb = options; options = {} }

  loadProfileFromIniFile(options, 'credentials', function(err, profile) {
    if (err) return cb(err)
    cb(null, {
      accessKeyId: profile.aws_access_key_id,
      secretAccessKey: profile.aws_secret_access_key,
      sessionToken: profile.aws_session_token,
    })
  })
}

function loadRegionFromIniFile(options, cb) {
  if (!cb) { cb = options; options = {} }

  loadProfileFromIniFile(options, 'config', function(err, profile) {
    if (err) return cb(err)
    cb(null, profile.region)
  })
}

function loadRegionFromIniFileSync(options) {
  return loadProfileFromIniFileSync(options || {}, 'config').region
}

function loadCredentialsFromHttp(options, cb) {
  return process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI ?
    loadCredentialsFromEcs(options, cb) : loadCredentialsFromEc2Metadata(options, cb)
}

function loadCredentialsFromEc2Metadata(options, cb) {
  if (!cb) { cb = options; options = {} }

  options.host = '169.254.169.254'
  options.resolvePath = function(options, cb) {
    options.path = '/latest/meta-data/iam/security-credentials/'

    request(options, function(err, res, data) {
      if (err) return cb(err)

      if (res.statusCode == 404)
        return cb(new Error('Could not find IAM role. Check that you assigned an IAM role to your EC2 instance'))

      if (res.statusCode != 200)
        return cb(new Error('Failed to fetch IAM role: ' + res.statusCode + ' ' + data))

      cb(null, options.path + data.split('\n')[0])
    })
  }

  requestCredentials(options, cb)
}

function loadCredentialsFromEcs(options, cb) {
  if (!cb) { cb = options; options = {} }

  options.host = '169.254.170.2'
  options.resolvePath = function(options, cb) {
    cb(null, process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI)
  }

  requestCredentials(options, cb)
}

function requestCredentials(options, cb) {
  httpCallbacks.push(cb)
  if (httpCallbacks.length > 1) return // only want one caller at a time

  cb = function(err, credentials) {
    httpCallbacks.forEach(function(cb) { cb(err, credentials) })
    httpCallbacks = []
  }

  if (options.timeout == null) options.timeout = 5000

  options.resolvePath(options, function(err, path) {
    if (err && ~TIMEOUT_CODES.indexOf(err.code)) return cb(null, {})
    if (err) return cb(err)

    options.path = path

    request(options, function(err, res, data) {
      if (err && ~TIMEOUT_CODES.indexOf(err.code)) return cb(null, {})
      if (err) return cb(err)

      if (res.statusCode != 200)
        return cb(new Error('Failed to fetch IAM credentials: ' + res.statusCode + ' ' + data))

      try { data = JSON.parse(data) } catch (e) { }

      if (!data.AccessKeyId)
        return cb(new Error('Failed to fetch IAM credentials: ' + JSON.stringify(data)))

      cb(null, {
        accessKeyId: data.AccessKeyId,
        secretAccessKey: data.SecretAccessKey,
        sessionToken: data.Token,
        expiration: new Date(data.Expiration),
      })
    })
  })
}

function loadProfileFromIniFile(options, defaultFilename, cb) {
  var filename = options.filename || path.join(resolveHome(), '.aws', defaultFilename),
      profile = options.profile || resolveProfile()

  fs.readFile(filename, 'utf8', function(err, data) {
    if (err && err.code == 'ENOENT') return cb(null, {})
    if (err) return cb(err)
    cb(null, parseAwsIni(data)[profile] || {})
  })
}

function loadProfileFromIniFileSync(options, defaultFilename) {
  var filename = options.filename || path.join(resolveHome(), '.aws', defaultFilename),
      profile = options.profile || resolveProfile(),
      data

  try {
    data = fs.readFileSync(filename, 'utf8')
  } catch (err) {
    if (err.code == 'ENOENT') return {}
    throw err
  }

  return parseAwsIni(data)[profile] || {}
}

function merge(obj, options, cb) {
  if (!cb) { cb = options; options = {} }

  var needRegion = !obj.region
  var needCreds = !obj.credentials || !obj.credentials.accessKeyId || !obj.credentials.secretAccessKey

  function loadCreds(cb) {
    if (needRegion && needCreds) {
      return loadCredentialsAndRegion(options, cb)
    } else if (needRegion) {
      return loadRegion(options, function(err, region) { cb(err, {region: region}) })
    } else if (needCreds) {
      return loadCredentials(options, function(err, credentials) { cb(err, {credentials: credentials}) })
    }
    cb(null, {})
  }

  loadCreds(function(err, creds) {
    if (err) return cb(err)

    if (creds.region) obj.region = creds.region
    if (creds.credentials) {
      if (!obj.credentials) {
        obj.credentials = creds.credentials
      } else {
        Object.keys(creds.credentials).forEach(function(key) {
          if (!obj.credentials[key]) obj.credentials[key] = creds.credentials[key]
        })
      }
    }

    cb()
  })
}

function resolveProfile() {
  return env.AWS_PROFILE || env.AMAZON_PROFILE || 'default'
}

function resolveHome() {
  return env.HOME || env.USERPROFILE || ((env.HOMEDRIVE || 'C:') + env.HOMEPATH)
}

// Fairly strict INI parser – will only deal with alpha keys, must be within sections
function parseAwsIni(ini) {
  var section,
      out = Object.create(null),
      re = /^\[([^\]]+)\]\s*$|^([a-z_]+)\s*=\s*(.+?)\s*$/,
      lines = ini.split(/\r?\n/)

  lines.forEach(function(line) {
    var match = line.match(re)
    if (!match) return
    if (match[1]) {
      section = match[1]
      if (out[section] == null) out[section] = Object.create(null)
    } else if (section) {
      out[section][match[2]] = match[3]
    }
  })

  return out
}

function request(options, cb) {
  cb = once(cb)

  var req = http.request(options, function(res) {
    var data = ''
    res.setEncoding('utf8')
    res.on('error', cb)
    res.on('data', function(chunk) { data += chunk })
    res.on('end', function() { cb(null, res, data) })
  }).on('error', cb)

  if (options.timeout != null) {
    req.setTimeout(options.timeout)
    req.on('timeout', function() { req.abort() })
  }

  req.end()
}

function once(cb) {
  var called = false
  return function() {
    if (called) return
    called = true
    cb.apply(this, arguments)
  }
}
<|endoftext|>


<commit_before>'use strict';

const Application = require('./lib/Application');
const Controller = require('./lib/Controller');
const Method = require('./lib/Method');

module.exports = createApplication;

function createApplication(name, settings) {
  return new Application(name, settings);
}

// Expose `Application`
createApplication.Application = Application;

// Expose `Controller`
createApplication.Controller = Controller;

// Expose `Method`
createApplication.Method = Method;

// Add version reference
createApplication.VERSION = require('../package.json').version;
<commit_msg>add VERSION reference<commit_after>'use strict';

const Application = require('./lib/Application');
const Controller = require('./lib/Controller');
const Method = require('./lib/Method');

module.exports = createApplication;

function createApplication(name, settings) {
  return new Application(name, settings);
}

// Expose `Application`
createApplication.Application = Application;

// Expose `Controller`
createApplication.Controller = Controller;

// Expose `Method`
createApplication.Method = Method;

// Add version reference
createApplication.VERSION = Application.VERSION = require('../package.json').version;
<|endoftext|>


<commit_before>var express = require('express'),
  router = express.Router(),
  mongoose = require('mongoose'),
  Schema = mongoose.Schema,
  _ = require('underscore'),
  moment = require('moment'),
  debug = require('debug')('mera'),
  XLSX = require('xlsx'),
  os = require('os'),
  path = require('path');

function mongooseRoute(model, options) {
  var router = express.Router();

  var options = options || {},
    omitProps = ['output'].concat(options.omitProps),
    props = options.props || _.keys(model.schema.tree).filter(e => !/^_/.test(e)).filter(e => omitProps.indexOf(e) == -1),
    propsMapping = _.extend({ 'id': '_id' }, options.propsMapping),
    _id = options._id || '_id',
    uploadProps = options.uploadProps || {};

  options.batchSize = options.batchSize || 1000;

  function error(method, err, statusCode) {
    var msg = 'Failed to ' + method + ' ' + model.modelName + ' : ' + err;
    var retErr = new Error(msg);
    err.statusCode = statusCode || 500;
    debug('err: %j', retErr);
    return retErr;
  }

  function convert(data) {
    _.each(_.keys(propsMapping), k => {
      if (data[k] && propsMapping[k] != k) {
        data[propsMapping[k]] = data[k];
        delete data[k];
      }
    });
    return data;
  }

  function output(item) {
    if (!item) return {};
    var output = {};
    props.forEach(p => { output[p] = item[p] }); // item can have virtual props so they need to be assigned one by one.
    return output;
  }

  // time filter works in such a way:
  // if start is given, only look at end, and start/end are parsed literally by moment.js, gte start, and lt end
  // otherwise look at start_time and then end_time
  // so if given start and end_time, only start takes effects
  // a special handle on end_time is if YYYY-MM-DD is given, we regard it as a full day included
  function getTimeFilter(rawFilter) {
    if (!options.timeFilter) {
      return null;
    }
    var f = {};
    if (rawFilter.start) {
      f.$gte = moment(rawFilter.start).toDate();
      if (rawFilter.end) {
        f.$lt = moment(rawFilter.end).toDate();
      }
    } else if (rawFilter.start_time) {
      f.$gte = moment(rawFilter.start_time).toDate();
      if (rawFilter.end_time) {
        if (/[0-9]{4,4}-[0-9]{2,2}-[0-9]{2,2}/.test(rawFilter.end_time)) {
          f.$lt = moment(rawFilter.end_time).add(1, 'day').startOf('day').toDate();
        } else {
          f.$lt = moment(rawFilter.end_time).toDate();
        }
      }
    }
    if (_.isEmpty(f)) {
      return null;
    }
    var timeFilter = {};
    timeFilter[options.timeFilter] = f;
    return timeFilter;
  }

  function replaceUploaded(req, res, next) {
    if (!_.isEmpty(uploadProps)) {
      _.keys(uploadProps).forEach(k => {
        if (req.body[k]) {
          req.body[uploadProps[k]] = req.body[k];
        }
      });
    }
    next();
  }

  function pickFilters(base, raw) {
    _.keys(raw).forEach(k => {
      var secs = k.split('.'), bk = secs.shift(1);
      if (props.indexOf(bk) == -1)
        return;
      base[k] = raw[k];
    });
    return base;
  }

  function Workbook() {
    if (!(this instanceof Workbook)) return new Workbook();
    this.SheetNames = [];
    this.Sheets = {};
  }

  function datenum(v, date1904) {
    if (date1904) v += 1462;
    var epoch = Date.parse(v);
    return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
  }

  function sheet_from_array_of_arrays(data, opts) {
    var ws = {};
    var range = { s: { c: 10000000, r: 10000000 }, e: { c: 0, r: 0 } };
    for (var R = 0; R != data.length; ++R) {
      for (var C = 0; C != data[R].length; ++C) {
        if (range.s.r > R) range.s.r = R;
        if (range.s.c > C) range.s.c = C;
        if (range.e.r < R) range.e.r = R;
        if (range.e.c < C) range.e.c = C;
        var cell = { v: data[R][C] };
        if (cell.v == null) continue;
        var cell_ref = XLSX.utils.encode_cell({ c: C, r: R });

        if (typeof cell.v === 'number') cell.t = 'n';
        else if (typeof cell.v === 'boolean') cell.t = 'b';
        else if (cell.v instanceof Date) {
          cell.t = 'n'; cell.z = XLSX.SSF._table[14];
          cell.v = datenum(cell.v);
        }
        else cell.t = 's';

        ws[cell_ref] = cell;
      }
    }
    if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
    return ws;
  }

  function formatToXlsx(data, name) {
    var ws_name = "SheetJS";
    var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);

    /* add worksheet to workbook */
    wb.SheetNames.push(ws_name);
    wb.Sheets[ws_name] = ws;

    var tempFile = path.join(os.tmpdir(), (name || (new Date().toString() + '.xlsx')));
    XLSX.writeFile(wb, tempFile);
    return tempFile;
  }

  function formatToCsv(rows, head) {
    var lines = []
    if (head) {
      lines.push(head.map(e => `"${e}"`).join(','))
    }
    rows.forEach(row => {
      lines.push(row.map(e => `"${e}"`).join(','))
    })
    return lines.join('\r\n')
  }

  // protect the method if defined in protects
  router.use('/', function(req, rest, next) {
    var protects = options.protects || {};
    var protect = req.method === 'GET' ? (req.url === '/' ? protects.LIST : protects.GET) : protects[req.method];
    if (protect) {
      protect(req, next);
    } else {
      next();
    }
  });

  router.get('/', function(req, res, next) {

    // filter buildup
    var filter = _.extend({}, options.baseFilter);

    // regard _filter in query firstly
    if (req.query._filters) {
      try {
        pickFilters(filter, JSON.parse(req.query._filters));
      } catch (e) {
        debug('not valid _filter although present: %j', req.query._filter);
      }
    } else {
      pickFilters(filter, req.query);
    }
    filter = convert(filter);
    _.extend(filter, getTimeFilter(req.query));

    debug('List %s by filter %j', model.modelName, filter);

    // listOptions buildup
    var lo = {
      limit: parseInt(req.query._perPage) || 0
    };
    lo.skip = Math.max((req.query._page || 1) - 1, 0) * lo.limit;
    if (req.query._sortField && req.query._sortDir) {
      lo.sort = {};
      lo.sort[req.query._sortField.replace(/^id/, '_id')] = req.query._sortDir.toUpperCase() == 'ASC' ? 1 : -1;
    } else {
      lo.sort = options.defaultSort;
    }
    debug('get list options from request: %j', lo);

    // list model
    model.count(filter, function(err, num) {
      if (err) {
        return next(error('LIST', err));
      }
      var items = [], limit = Math.min(lo.limit, options.batchSize), skip = lo.skip;

      function findBatch(cb) {
        model.find(filter).sort(lo.sort).skip(skip).limit(limit).exec(function(errB, itemsB) {
          if (errB) return cb(errB);
          items = items.concat(itemsB)
          // if get all items, finish batch
          if (items.length >= lo.limit) {
            return cb(null, items)
          }
          // if current fetch is less than a batch, which means no more items to get, finish batch
          if (itemsB.length < limit) {
            return cb(null, items);
          }
          // else, continue
          skip = skip + itemsB.length;
          findBatch(cb)
        });
      }
      findBatch((err2, items) => {
        if (err2) {
          return next(error('LIST', err2));
        }
        if (req.query.format == 'csv') {
          res.set('Content-Type', 'text/csv');
          var rows = items.map(e => props.map(k => e[k]));
          var csvout = formatToCsv(rows, props);
          res.send(csvout);
        } else if (req.query.format == 'xlsx') {
          var rows = items.map(e => props.map(k => e[k]));
          rows.unshift(props);
          var wbout = formatToXlsx(rows, model.modelName + '_' + new Date().getTime().toString() + '.xlsx');
          res.sendfile(wbout);
        } else {
          res.set('X-Total-Count', num).json(items.map(output));
        }
      })
    });
  });

  router.post('/', replaceUploaded, function(req, res, next) {
    var data = convert(req.body);
    debug('Post to model %s by data %j', model.modelName, data);

    model.create(data, function(err, item) {
      if (err) {
        return next(error('POST', err));
      }
      res.json(output(item));
    });
  });

  router.use('/:id', function(req, res, next) {
    req.id_filter = {};
    req.id_filter[options._id || '_id'] = req.params.id;
    next();
  });

  router.get('/:id', function(req, res, next) {
    debug('Get model %s by id %s', model.modelName, req.params.id);
    model.findOne(req.id_filter).exec(function(err, item) {
      if (err) {
        return next(error('GET', err));
      }
      if (!item) {
        return next();
      }
      res.json(output(item));
    });
  });

  router.put('/:id', replaceUploaded, function(req, res, next) {
    var data = convert(req.body);
    debug('Put to model %s, id %s by data %j', model.modelName, req.params.id, data);

    model.findOne(req.id_filter).exec(function(err, item) {
      if (err) {
        return next(error('PUT', err));
      }
      if (!item) {
        return next();
      }
      _.extend(item, data);
      item.save(function(err) {
        if (err) {
          return next(error('PUT', err));
        }
        res.json(output(item));
      });
    });
  });

  router.delete('/:id', function(req, res, next) {
    debug('delete model %s by id %s', model.modelName, req.params.id);

    model.remove(req.id_filter).exec(function(err, item) {
      if (err) {
        return next(error('DELETE', err));
      }
      res.json(output(item));
    });
  });

  return router;
}

module.exports = mongooseRoute;
<commit_msg>omit empty fields at output<commit_after>var express = require('express'),
  router = express.Router(),
  mongoose = require('mongoose'),
  Schema = mongoose.Schema,
  _ = require('underscore'),
  moment = require('moment'),
  debug = require('debug')('mera'),
  XLSX = require('xlsx'),
  os = require('os'),
  path = require('path');

function mongooseRoute(model, options) {
  var router = express.Router();

  var options = options || {},
    omitProps = ['output'].concat(options.omitProps),
    props = options.props || _.keys(model.schema.tree).filter(e => !/^_/.test(e)).filter(e => omitProps.indexOf(e) == -1),
    propsMapping = _.extend({ 'id': '_id' }, options.propsMapping),
    _id = options._id || '_id',
    uploadProps = options.uploadProps || {};

  options.batchSize = options.batchSize || 1000;

  function error(method, err, statusCode) {
    var msg = 'Failed to ' + method + ' ' + model.modelName + ' : ' + err;
    var retErr = new Error(msg);
    err.statusCode = statusCode || 500;
    debug('err: %j', retErr);
    return retErr;
  }

  function convert(data) {
    _.each(_.keys(propsMapping), k => {
      if (data[k] && propsMapping[k] != k) {
        data[propsMapping[k]] = data[k];
        delete data[k];
      }
    });
    return data;
  }

  function output(item) {
    if (!item) return {};
    var output = {};
    props.forEach(p => {
      if (!_.isEmpty(item[p])) {
        output[p] = item[p]
      }
    }); // item can have virtual props so they need to be assigned one by one.
    return output;
  }

  // time filter works in such a way:
  // if start is given, only look at end, and start/end are parsed literally by moment.js, gte start, and lt end
  // otherwise look at start_time and then end_time
  // so if given start and end_time, only start takes effects
  // a special handle on end_time is if YYYY-MM-DD is given, we regard it as a full day included
  function getTimeFilter(rawFilter) {
    if (!options.timeFilter) {
      return null;
    }
    var f = {};
    if (rawFilter.start) {
      f.$gte = moment(rawFilter.start).toDate();
      if (rawFilter.end) {
        f.$lt = moment(rawFilter.end).toDate();
      }
    } else if (rawFilter.start_time) {
      f.$gte = moment(rawFilter.start_time).toDate();
      if (rawFilter.end_time) {
        if (/[0-9]{4,4}-[0-9]{2,2}-[0-9]{2,2}/.test(rawFilter.end_time)) {
          f.$lt = moment(rawFilter.end_time).add(1, 'day').startOf('day').toDate();
        } else {
          f.$lt = moment(rawFilter.end_time).toDate();
        }
      }
    }
    if (_.isEmpty(f)) {
      return null;
    }
    var timeFilter = {};
    timeFilter[options.timeFilter] = f;
    return timeFilter;
  }

  function replaceUploaded(req, res, next) {
    if (!_.isEmpty(uploadProps)) {
      _.keys(uploadProps).forEach(k => {
        if (req.body[k]) {
          req.body[uploadProps[k]] = req.body[k];
        }
      });
    }
    next();
  }

  function pickFilters(base, raw) {
    _.keys(raw).forEach(k => {
      var secs = k.split('.'), bk = secs.shift(1);
      if (props.indexOf(bk) == -1)
        return;
      base[k] = raw[k];
    });
    return base;
  }

  function Workbook() {
    if (!(this instanceof Workbook)) return new Workbook();
    this.SheetNames = [];
    this.Sheets = {};
  }

  function datenum(v, date1904) {
    if (date1904) v += 1462;
    var epoch = Date.parse(v);
    return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
  }

  function sheet_from_array_of_arrays(data, opts) {
    var ws = {};
    var range = { s: { c: 10000000, r: 10000000 }, e: { c: 0, r: 0 } };
    for (var R = 0; R != data.length; ++R) {
      for (var C = 0; C != data[R].length; ++C) {
        if (range.s.r > R) range.s.r = R;
        if (range.s.c > C) range.s.c = C;
        if (range.e.r < R) range.e.r = R;
        if (range.e.c < C) range.e.c = C;
        var cell = { v: data[R][C] };
        if (cell.v == null) continue;
        var cell_ref = XLSX.utils.encode_cell({ c: C, r: R });

        if (typeof cell.v === 'number') cell.t = 'n';
        else if (typeof cell.v === 'boolean') cell.t = 'b';
        else if (cell.v instanceof Date) {
          cell.t = 'n'; cell.z = XLSX.SSF._table[14];
          cell.v = datenum(cell.v);
        }
        else cell.t = 's';

        ws[cell_ref] = cell;
      }
    }
    if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
    return ws;
  }

  function formatToXlsx(data, name) {
    var ws_name = "SheetJS";
    var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);

    /* add worksheet to workbook */
    wb.SheetNames.push(ws_name);
    wb.Sheets[ws_name] = ws;

    var tempFile = path.join(os.tmpdir(), (name || (new Date().toString() + '.xlsx')));
    XLSX.writeFile(wb, tempFile);
    return tempFile;
  }

  function formatToCsv(rows, head) {
    var lines = []
    if (head) {
      lines.push(head.map(e => `"${e}"`).join(','))
    }
    rows.forEach(row => {
      lines.push(row.map(e => `"${e}"`).join(','))
    })
    return lines.join('\r\n')
  }

  // protect the method if defined in protects
  router.use('/', function(req, rest, next) {
    var protects = options.protects || {};
    var protect = req.method === 'GET' ? (req.url === '/' ? protects.LIST : protects.GET) : protects[req.method];
    if (protect) {
      protect(req, next);
    } else {
      next();
    }
  });

  router.get('/', function(req, res, next) {

    // filter buildup
    var filter = _.extend({}, options.baseFilter);

    // regard _filter in query firstly
    if (req.query._filters) {
      try {
        pickFilters(filter, JSON.parse(req.query._filters));
      } catch (e) {
        debug('not valid _filter although present: %j', req.query._filter);
      }
    } else {
      pickFilters(filter, req.query);
    }
    filter = convert(filter);
    _.extend(filter, getTimeFilter(req.query));

    debug('List %s by filter %j', model.modelName, filter);

    // listOptions buildup
    var lo = {
      limit: parseInt(req.query._perPage) || 0
    };
    lo.skip = Math.max((req.query._page || 1) - 1, 0) * lo.limit;
    if (req.query._sortField && req.query._sortDir) {
      lo.sort = {};
      lo.sort[req.query._sortField.replace(/^id/, '_id')] = req.query._sortDir.toUpperCase() == 'ASC' ? 1 : -1;
    } else {
      lo.sort = options.defaultSort;
    }
    debug('get list options from request: %j', lo);

    // list model
    model.count(filter, function(err, num) {
      if (err) {
        return next(error('LIST', err));
      }
      var items = [], limit = Math.min(lo.limit, options.batchSize), skip = lo.skip;

      function findBatch(cb) {
        model.find(filter).sort(lo.sort).skip(skip).limit(limit).exec(function(errB, itemsB) {
          if (errB) return cb(errB);
          items = items.concat(itemsB)
          // if get all items, finish batch
          if (items.length >= lo.limit) {
            return cb(null, items)
          }
          // if current fetch is less than a batch, which means no more items to get, finish batch
          if (itemsB.length < limit) {
            return cb(null, items);
          }
          // else, continue
          skip = skip + itemsB.length;
          findBatch(cb)
        });
      }
      findBatch((err2, items) => {
        if (err2) {
          return next(error('LIST', err2));
        }
        if (req.query.format == 'csv') {
          res.set('Content-Type', 'text/csv');
          var rows = items.map(e => props.map(k => e[k]));
          var csvout = formatToCsv(rows, props);
          res.send(csvout);
        } else if (req.query.format == 'xlsx') {
          var rows = items.map(e => props.map(k => e[k]));
          rows.unshift(props);
          var wbout = formatToXlsx(rows, model.modelName + '_' + new Date().getTime().toString() + '.xlsx');
          res.sendfile(wbout);
        } else {
          res.set('X-Total-Count', num).json(items.map(output));
        }
      })
    });
  });

  router.post('/', replaceUploaded, function(req, res, next) {
    var data = convert(req.body);
    debug('Post to model %s by data %j', model.modelName, data);

    model.create(data, function(err, item) {
      if (err) {
        return next(error('POST', err));
      }
      res.json(output(item));
    });
  });

  router.use('/:id', function(req, res, next) {
    req.id_filter = {};
    req.id_filter[options._id || '_id'] = req.params.id;
    next();
  });

  router.get('/:id', function(req, res, next) {
    debug('Get model %s by id %s', model.modelName, req.params.id);
    model.findOne(req.id_filter).exec(function(err, item) {
      if (err) {
        return next(error('GET', err));
      }
      if (!item) {
        return next();
      }
      res.json(output(item));
    });
  });

  router.put('/:id', replaceUploaded, function(req, res, next) {
    var data = convert(req.body);
    debug('Put to model %s, id %s by data %j', model.modelName, req.params.id, data);

    model.findOne(req.id_filter).exec(function(err, item) {
      if (err) {
        return next(error('PUT', err));
      }
      if (!item) {
        return next();
      }
      _.extend(item, data);
      item.save(function(err) {
        if (err) {
          return next(error('PUT', err));
        }
        res.json(output(item));
      });
    });
  });

  router.delete('/:id', function(req, res, next) {
    debug('delete model %s by id %s', model.modelName, req.params.id);

    model.remove(req.id_filter).exec(function(err, item) {
      if (err) {
        return next(error('DELETE', err));
      }
      res.json(output(item));
    });
  });

  return router;
}

module.exports = mongooseRoute;
<|endoftext|>


<commit_before>'use strict';

const underscore = require('underscore');

const logger = require('../logger');
const northstar = require('../northstar');
const rogue = require('../rogue');
const helpers = require('../helpers');
const statuses = require('./subscription').statuses;
const config = require('../../config/lib/helpers/user');

/**
 * @param {Object} user
 * @return {Promise}
 */
function createVotingPlan(user, source) {
  const votingPlan = {
    attending_with: user.voting_plan_attending_with,
    method_of_transport: user.voting_plan_method_of_transport,
    time_of_day: user.voting_plan_time_of_day,
  };
  const votingPlanPostConfig = config.posts.votingPlan;
  const payload = {
    northstar_id: user.id,
    type: votingPlanPostConfig.type,
    text: JSON.stringify(votingPlan),
    campaign_id: votingPlanPostConfig.campaignId,
    source,
  };
  logger.debug('rogue.createPost', { payload });
  // TODO: If text post already exists for our campaignId, update it instead of creating a new one.
  return rogue.createPost(payload);
}

/**
 * @param {String} userId
 * @return {Promise}
 */
function setPendingSubscriptionStatusForUserId(userId) {
  logger.debug('setPendingSubscriptionStatusForUserId', { userId });
  return northstar.updateUser(userId, { sms_status: statuses.pending() });
}

/**
 * @param {Object} req
 * @return {Promise}
 */
async function updateByMemberMessageReq(req) {
  const payload = {};
  try {
    underscore.extend(payload, module.exports.getDefaultUpdatePayloadFromReq(req));
    underscore.extend(payload, helpers.macro.getProfileUpdate(req.macro));
    if (req.platformUserAddress && !helpers.user.hasAddress(req.user)) {
      underscore.extend(payload, req.platformUserAddress);
    }
  } catch (err) {
    return Promise.reject(err);
  }

  const user = await northstar.updateUser(req.user.id, payload);
  if (helpers.macro.isCompletedVotingPlan(req.macro)) {
    await module.exports.createVotingPlan(user, req.platform);
  }
  return user;
}

module.exports = {
  createVotingPlan,
  unauthenticatedFetchById(userId) {
    return northstar.unauthenticatedFetchUserById(userId);
  },
  fetchById: function fetchById(userId) {
    return northstar.fetchUserById(userId);
  },
  fetchByMobile: function fetchByMobile(mobileNumber) {
    return northstar.fetchUserByMobile(mobileNumber);
  },
  /**
   * fetchFromReq - The conversation's `userId` has precedence over `platformUserId`
   *
   * @param  {Object} req
   * @return {Promise}
   */
  fetchFromReq: function fetchFromReq(req) {
    if (req.userId) {
      return this.fetchById(req.userId);
    }
    return this.fetchByMobile(req.platformUserId);
  },
  /**
   * @param {object} user
   * @return {boolean}
   */
  hasAddress: function hasAddress(user) {
    if (user.addr_city && user.addr_state && user.addr_zip && user.country) {
      return true;
    }
    return false;
  },
  /**
   * @param {object} req
   * @return {object}
   */
  getCreatePayloadFromReq: function getCreatePayloadFromReq(req) {
    // Currently only support creating new Users via SMS.
    const mobile = req.platformUserId;
    const data = {
      source: req.platform,
      mobile,
      sms_status: statuses.active(),
      sms_paused: false,
    };
    underscore.extend(data, req.platformUserAddress);
    return data;
  },
  /**
   * @param {object} req
   * @return {object}
   */
  getDefaultUpdatePayloadFromReq: function getDefaultUpdatePayloadFromReq(req) {
    return {
      last_messaged_at: req.inboundMessage.createdAt.toISOString(),
      sms_paused: req.conversation.isSupportTopic(),
    };
  },
  /**
   * getUndeliverableStatusUpdatePayload
   *
   * @return {Object}
   */
  getUndeliverableStatusUpdatePayload: function getUndeliverableStatusUpdatePayload() {
    return {
      sms_status: statuses.undeliverable(),
    };
  },
  /**
   * @param {object} user
   * @return {boolean}
   */
  isPaused(user) {
    return user.sms_paused;
  },
  /**
   * @param {object} user
   * @return {boolean}
   */
  isSubscriber(user) {
    const status = user.sms_status;
    /**
     * Seems to me that having a sms_status of undefined is an edge case. Because of it,
     * I think we are safe treating them as unsubscribed users.
     * TODO: To understand the bigger scale impact we should get a total count of users
     * w/ undefined as the sms_status value.
     */
    if (
      status === undefined ||
      status === statuses.stop() ||
      status === statuses.undeliverable()) {
      return false;
    }
    return true;
  },
  setPendingSubscriptionStatusForUserId,
  updateByMemberMessageReq,
};
<commit_msg>Adds missing param<commit_after>'use strict';

const underscore = require('underscore');

const logger = require('../logger');
const northstar = require('../northstar');
const rogue = require('../rogue');
const helpers = require('../helpers');
const statuses = require('./subscription').statuses;
const config = require('../../config/lib/helpers/user');

/**
 * @param {Object} user
 * @param {String} source
 * @return {Promise}
 */
function createVotingPlan(user, source) {
  const votingPlan = {
    attending_with: user.voting_plan_attending_with,
    method_of_transport: user.voting_plan_method_of_transport,
    time_of_day: user.voting_plan_time_of_day,
  };
  const votingPlanPostConfig = config.posts.votingPlan;
  const payload = {
    northstar_id: user.id,
    type: votingPlanPostConfig.type,
    text: JSON.stringify(votingPlan),
    campaign_id: votingPlanPostConfig.campaignId,
    source,
  };
  logger.debug('rogue.createPost', { payload });
  // TODO: If text post already exists for our campaignId, update it instead of creating a new one.
  return rogue.createPost(payload);
}

/**
 * @param {String} userId
 * @return {Promise}
 */
function setPendingSubscriptionStatusForUserId(userId) {
  logger.debug('setPendingSubscriptionStatusForUserId', { userId });
  return northstar.updateUser(userId, { sms_status: statuses.pending() });
}

/**
 * @param {Object} req
 * @return {Promise}
 */
async function updateByMemberMessageReq(req) {
  const payload = {};
  try {
    underscore.extend(payload, module.exports.getDefaultUpdatePayloadFromReq(req));
    underscore.extend(payload, helpers.macro.getProfileUpdate(req.macro));
    if (req.platformUserAddress && !helpers.user.hasAddress(req.user)) {
      underscore.extend(payload, req.platformUserAddress);
    }
  } catch (err) {
    return Promise.reject(err);
  }

  const user = await northstar.updateUser(req.user.id, payload);
  if (helpers.macro.isCompletedVotingPlan(req.macro)) {
    await module.exports.createVotingPlan(user, req.platform);
  }
  return user;
}

module.exports = {
  createVotingPlan,
  unauthenticatedFetchById(userId) {
    return northstar.unauthenticatedFetchUserById(userId);
  },
  fetchById: function fetchById(userId) {
    return northstar.fetchUserById(userId);
  },
  fetchByMobile: function fetchByMobile(mobileNumber) {
    return northstar.fetchUserByMobile(mobileNumber);
  },
  /**
   * fetchFromReq - The conversation's `userId` has precedence over `platformUserId`
   *
   * @param  {Object} req
   * @return {Promise}
   */
  fetchFromReq: function fetchFromReq(req) {
    if (req.userId) {
      return this.fetchById(req.userId);
    }
    return this.fetchByMobile(req.platformUserId);
  },
  /**
   * @param {object} user
   * @return {boolean}
   */
  hasAddress: function hasAddress(user) {
    if (user.addr_city && user.addr_state && user.addr_zip && user.country) {
      return true;
    }
    return false;
  },
  /**
   * @param {object} req
   * @return {object}
   */
  getCreatePayloadFromReq: function getCreatePayloadFromReq(req) {
    // Currently only support creating new Users via SMS.
    const mobile = req.platformUserId;
    const data = {
      source: req.platform,
      mobile,
      sms_status: statuses.active(),
      sms_paused: false,
    };
    underscore.extend(data, req.platformUserAddress);
    return data;
  },
  /**
   * @param {object} req
   * @return {object}
   */
  getDefaultUpdatePayloadFromReq: function getDefaultUpdatePayloadFromReq(req) {
    return {
      last_messaged_at: req.inboundMessage.createdAt.toISOString(),
      sms_paused: req.conversation.isSupportTopic(),
    };
  },
  /**
   * getUndeliverableStatusUpdatePayload
   *
   * @return {Object}
   */
  getUndeliverableStatusUpdatePayload: function getUndeliverableStatusUpdatePayload() {
    return {
      sms_status: statuses.undeliverable(),
    };
  },
  /**
   * @param {object} user
   * @return {boolean}
   */
  isPaused(user) {
    return user.sms_paused;
  },
  /**
   * @param {object} user
   * @return {boolean}
   */
  isSubscriber(user) {
    const status = user.sms_status;
    /**
     * Seems to me that having a sms_status of undefined is an edge case. Because of it,
     * I think we are safe treating them as unsubscribed users.
     * TODO: To understand the bigger scale impact we should get a total count of users
     * w/ undefined as the sms_status value.
     */
    if (
      status === undefined ||
      status === statuses.stop() ||
      status === statuses.undeliverable()) {
      return false;
    }
    return true;
  },
  setPendingSubscriptionStatusForUserId,
  updateByMemberMessageReq,
};
<|endoftext|>


<commit_before>/*!
 * is-type-of - index.js
 * Copyright(c) 2014 dead_horse <dead_horse@qq.com>
 * MIT Licensed
 */

'use strict';

/**
 * Module dependencies.
 */

var utils = require('core-util-is');
var isStearm = require('isstream');

/**
 * Expose all methods in core-util-is
 */

Object.keys(utils).map(function (name) {
  exports[transform(name)] = utils[name];
});

/**
 * Stream detected by isstream
 */

exports.stream = isStearm;
exports.readableStream = isStearm.isReadable;
exports.writableStream = isStearm.isWritable;
exports.duplexStream = isStearm.isDuplex;

/**
 * Extend method
 */

exports.NaN = function (obj) {
  return Number.isNaN(obj);
};

exports.generator = function (obj) {
  return obj && 'function' === typeof obj.next && 'function' === typeof obj.throw;
};

exports.generatorFunction = function (obj) {
  return obj && obj.constructor && 'GeneratorFunction' === obj.constructor.name;
};

exports.promise = function (obj) {
  return obj && 'function' === typeof obj.then;
};

var MAX_INT_31 = Math.pow(2, 31);

exports.int = function (obj) {
  return utils.isNumber(obj) && obj % 1 === 0;
};

exports.int32 = function (obj) {
  return exports.int(obj) && (obj < MAX_INT_31 && obj >= -MAX_INT_31);
};

exports.long = function (obj) {
  return exports.int(obj) && (obj >= MAX_INT_31 || obj < -MAX_INT_31);
};

exports.double = function (obj) {
  return utils.isNumber(obj) && !isNaN(obj) && obj % 1 !== 0;
};

/**
 * transform isNull type to null
 * @param {[type]} m [description]
 * @return {[type]} [description]
 */

function transform(m) {
  var name = m.slice(2);
  name = name[0].toLowerCase() + name.slice(1);
  return name;
}
<commit_msg>refactor<commit_after>/*!
 * is-type-of - index.js
 * Copyright(c) 2014 dead_horse <dead_horse@qq.com>
 * MIT Licensed
 */

'use strict';

/**
 * Module dependencies.
 */

var utils = require('core-util-is');
var isStearm = require('isstream');

/**
 * Expose all methods in core-util-is
 */

Object.keys(utils).map(function (name) {
  exports[transform(name)] = utils[name];
});

/**
 * Stream detected by isstream
 */

exports.stream = isStearm;
exports.readableStream = isStearm.isReadable;
exports.writableStream = isStearm.isWritable;
exports.duplexStream = isStearm.isDuplex;

/**
 * Extend method
 */

exports.NaN = function (obj) {
  return Number.isNaN(obj);
};

exports.generator = function (obj) {
  return obj
    && 'function' === typeof obj.next
    && 'function' === typeof obj.throw;
};

exports.generatorFunction = function (obj) {
  return obj
    && obj.constructor
    && 'GeneratorFunction' === obj.constructor.name;
};

exports.promise = function (obj) {
  return obj
    && 'function' === typeof obj.then;
};

var MAX_INT_31 = Math.pow(2, 31);

exports.int = function (obj) {
  return utils.isNumber(obj)
    && obj % 1 === 0;
};

exports.int32 = function (obj) {
  return exports.int(obj)
    && obj < MAX_INT_31
    && obj >= -MAX_INT_31;
};

exports.long = function (obj) {
  return exports.int(obj)
    && (obj >= MAX_INT_31 || obj < -MAX_INT_31);
};

exports.double = function (obj) {
  return utils.isNumber(obj)
    && !isNaN(obj)
    && obj % 1 !== 0;
};

/**
 * transform isNull type to null
 * @param {[type]} m [description]
 * @return {[type]} [description]
 */

function transform(m) {
  var name = m.slice(2);
  name = name[0].toLowerCase() + name.slice(1);
  return name;
}
<|endoftext|>


<commit_before>
import React, { Component } from 'react';
import {
  Image,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';

export default class Avatar extends Component {

  setAvatarColor() {
    const userName = this.props.avatarName || '';
    const name = userName.toUpperCase().split(' ');

    console.log({userName:userName});

    if (name.length === 1) {
      this.avatarName = `${name[0].charAt(0)}`;
    } else if (name.length > 1) {
      this.avatarName = `${name[0].charAt(0)}${name[1].charAt(0)}`;
    } else {
      this.avatarName = '';
    }

    let sumChars = 0;
    for(let i = 0; i < userName.length; i++) {
      sumChars += userName.charCodeAt(i);
    }

    // inspired by https://github.com/wbinnssmith/react-user-avatar
    // colors from https://flatuicolors.com/
    const colors = [
      '#e67e22', // carrot
      '#2ecc71', // emerald
      '#3498db', // peter river
      '#8e44ad', // wisteria
      '#e74c3c', // alizarin
      '#1abc9c', // turquoise
      //'#2c3e50', // midnight blue
    ];

    this.avatarColor = colors[sumChars % colors.length];

    console.log({avatarColor:this.avatarColor});
  }

  renderInitials() {
    return (
      <Text style={[defaultStyles.textStyle, this.props.textStyle]}>
        {this.avatarName}
      </Text>
    );
  }

  render() {

    //if (!this.avatarColor) {
      this.setAvatarColor();
    //}

    return (
      <TouchableOpacity
        disabled={this.props.onPress ? false : true}
        onPress={() => {
          const {onPress, ...other} = this.props;
          this.props.onPress && this.props.onPress(other);
        }}
        style={[
          defaultStyles.avatarStyle,
          {backgroundColor: this.avatarColor},
          this.props.avatarStyle,
        ]}
      >
        {this.renderInitials()}
      </TouchableOpacity>
    );
  }

}

const defaultStyles = {
  avatarStyle: {
    justifyContent: 'center',
    alignItems: 'center',
    width: 40,
    height: 40,
    borderRadius: 20,
  },
  textStyle: {
    color: '#fff',
    fontSize: 16,
    backgroundColor: 'transparent',
    fontWeight: '100',
  },
};

Avatar.defaultProps = {
  name: null,
  onPress: null,
  avatarStyle: {},
  textStyle: {},
};

Avatar.propTypes = {
  onPress: React.PropTypes.func,
  avatarStyle: Image.propTypes.style,
  textStyle: Text.propTypes.style,
};
<commit_msg>first commit<commit_after>
import React, { Component } from 'react';
import {
  Image,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';

export default class Avatar extends Component {

  setAvatarColor() {
    const userName = this.props.avatarName || '';
    const name = userName.toUpperCase().split(' ');

    //console.log({userName:userName});

    if (name.length === 1) {
      this.avatarName = `${name[0].charAt(0)}`;
    } else if (name.length > 1) {
      this.avatarName = `${name[0].charAt(0)}${name[1].charAt(0)}`;
    } else {
      this.avatarName = '';
    }

    let sumChars = 0;
    for(let i = 0; i < userName.length; i++) {
      sumChars += userName.charCodeAt(i);
    }

    // inspired by https://github.com/wbinnssmith/react-user-avatar
    // colors from https://flatuicolors.com/
    const colors = [
      '#e67e22', // carrot
      '#2ecc71', // emerald
      '#3498db', // peter river
      '#8e44ad', // wisteria
      '#e74c3c', // alizarin
      '#1abc9c', // turquoise
      //'#2c3e50', // midnight blue
    ];

    this.avatarColor = colors[sumChars % colors.length];

    //console.log({avatarColor:this.avatarColor});
  }

  renderInitials() {
    return (
      <Text style={[defaultStyles.textStyle, this.props.textStyle]}>
        {this.avatarName}
      </Text>
    );
  }

  render() {

    //if (!this.avatarColor) {
      this.setAvatarColor();
    //}

    return (
      <TouchableOpacity
        disabled={this.props.onPress ? false : true}
        onPress={() => {
          const {onPress, ...other} = this.props;
          this.props.onPress && this.props.onPress(other);
        }}
        style={[
          defaultStyles.avatarStyle,
          {backgroundColor: this.avatarColor},
          this.props.avatarStyle,
        ]}
      >
        {this.renderInitials()}
      </TouchableOpacity>
    );
  }

}

const defaultStyles = {
  avatarStyle: {
    justifyContent: 'center',
    alignItems: 'center',
    width: 40,
    height: 40,
    borderRadius: 20,
  },
  textStyle: {
    color: '#fff',
    fontSize: 16,
    backgroundColor: 'transparent',
    fontWeight: '100',
  },
};

Avatar.defaultProps = {
  name: null,
  onPress: null,
  avatarStyle: {},
  textStyle: {},
};

Avatar.propTypes = {
  onPress: React.PropTypes.func,
  avatarStyle: Image.propTypes.style,
  textStyle: Text.propTypes.style,
};
<|endoftext|>


<commit_before>
/** Write a list comprehension that builds the list of all Pythagorean triples
 * with elements between 1 and n.
 *
 * A Pythagorean triple is a triple of positive integers a, b, and c such that a right triangle
 * exists with legs a,b and hypotenuse c. By the Pythagorean theorem, this is equivalent to finding
 * positive integers a, b, and c satisfying a^2+b^2=c^2.
 * The smallest and best-known Pythagorean triple is (a,b,c)=(3,4,5).
 * The right triangle having these side lengths is sometimes called the 3, 4, 5 triangle.
 **/

 // Haskell has the most elegant solution as lists are Monads
 // pyth n = [(x,y,z) | x <- [1..n], y <- [x..n], z <- [y..n], x^2 + y^2 == z^2]

 // ES5 version

 console.log(

 (function(n) {

 // Using a list monad directly, without list comprehension notation

  return mb( rng(1, n),     function(x) {
  return mb( rng(1 + x, n), function(y) {
  return mb( rng(1 + y, n), function(z) {

    return (x * x + y * y === z * z) ? mReturn([x, y, z]) : [];

  })})});

 /* ***************************************** */

     // Monadic bind (chain) for lists
     function mb(xs, f) {

      return [].concat.apply([], xs.map(f))

     }

     // Monadic return (inject) for lists
     function mReturn(a) {
       return [a];
     }

     // Range
     function rng(m, n) {

      return Array.apply(null, Array(n - m + 1)).map(
        function (x, i) { return m + i; }
        );
     }

 })(100)

 );

<commit_msg>basics on js problems<commit_after>
/** Write a list comprehension that builds the list of all Pythagorean triples
 * with elements between 1 and n.
 *
 * A Pythagorean triple is a triple of positive integers a, b, and c such that a right triangle
 * exists with legs a,b and hypotenuse c. By the Pythagorean theorem, this is equivalent to finding
 * positive integers a, b, and c satisfying a^2+b^2=c^2.
 * The smallest and best-known Pythagorean triple is (a,b,c)=(3,4,5).
 * The right triangle having these side lengths is sometimes called the 3, 4, 5 triangle.
 **/

 // Haskell has the most elegant one line solution as lists are Monads
 // pyth n = [(x,y,z) | x <- [1..n], y <- [x..n], z <- [y..n], x^2 + y^2 == z^2]

 // ES5 version

 console.log(

 (function(n) {

 // Using a list monad directly, without list comprehension notation

  return mb( rng(1, n),     function(x) {
  return mb( rng(1 + x, n), function(y) {
  return mb( rng(1 + y, n), function(z) {

    return (x * x + y * y === z * z) ? mReturn([x, y, z]) : [];

  })})});

 /* ***************************************** */

     // Monadic bind (chain) for lists
     function mb(xs, f) {

      return [].concat.apply([], xs.map(f))

     }

     // Monadic return (inject) for lists
     function mReturn(a) {
       return [a];
     }

     // Range
     function rng(m, n) {

      return Array.apply(null, Array(n - m + 1)).map(
        function (x, i) { return m + i; }
        );
     }

 })(100)

 );

<|endoftext|>