<commit_before>/**
 * Class to represent a full Closure Library dependency tree.
 *
 * Offers a queryable tree of dependencies of a given set of sources.  The tree
 * will also do logical validation to prevent duplicate provides and circular
 * dependencies.
 */

/**
 * Represents the set of dependencies between source files.
 *
 * @param {Array.<Source>} sources
 * @constructor
 */
var DepsTree = module.exports = function(sources) {
  /** @private {Array.<Source>} */
  this._sources = sources;
  /** @private {Object.<Source>} */
  this._providesMap = this._getProvidesMap(this._sources);

  // Check that all required namespaces are provided.
  /*this._sources.forEach(function(source) {
    source.requires.forEach(function(require) {
      if (!this._providesMap[require]) {
        throw new Error('Namespace "' + require + '" never provided. ' +
          'Required in ' + source);
      }
    }, this);
  }, this);*/
};

/**
 * Resolve dependencies for Closure source files.
 *
 * Follows the dependency tree down and builds a list of sources in dependency
 * order.  This function will recursively call itself to fill all dependencies
 * below the requested namespaces, and then append its sources at the end of
 * the list.
 *
 * @param {string} requiredNamespace
 * @param {!Array.<Source>} depsList List of sources in dependency order.
 *    This function will append the required source once all of its dependencies
 *    are satisfied.
 * @param {Object.<string>} providesMap Map from namespace to source that
 *    provides it.
 * @param {Array.<string>} traversalPath List of namespaces of our path from
 *    the root down the dependency/recursion tree.  Used to identify cyclical
 *    dependencies. This is a list used as a stack -- when the function is
 *    entered, the current namespace is pushed and popped right before
 *    returning. Each recursive call will check that the current namespace does
 *    not appear in the list, throwing a error if it does.
 * @return {!Array.<Source>} The given depsList object filled with sources
 *    in dependency order.
 * @private
 */
DepsTree._resolveDependencies = function(requiredNamespace, depsList,
    providesMap, traversalPath) {
  /** @type {Source} */
  var source = providesMap[requiredNamespace];

  if (!source) {
    throw new Error('Namespace "' + requiredNamespace + '" never provided.');
  }

  if (-1 < traversalPath.indexOf(requiredNamespace)) {
    traversalPath.push(requiredNamespace); // do this *after* the test

    // This must be a cycle.
    throw new Error('Encountered circular dependency:\n\n' + traversalPath);
  }

  // If we don't have the source yet, we'll have to visit this namespace and
  // add the required dependencies to depsList.
  if (-1 == depsList.indexOf(source)) {
    traversalPath.push(requiredNamespace);

    source.requires.forEach(function(require) {
      // Append all other dependencies before we append our own.
      DepsTree._resolveDependencies(
        require, depsList, providesMap, traversalPath);
    }, this);

    depsList.push(source);
    traversalPath.pop();
  }

  return depsList;
};

/**
 * Get source dependencies, in order.
 *
 * @param {Array.<Source>} inputSources List of input source objects.
 * @return {!Array.<Source>} A list of source objects that provide those
 *    namespaces and all requirements, in dependency order.
 */
DepsTree.prototype.getDependencies = function(inputSources) {
  /** @type {!Array.<Source>} */
  var depsSources = [];
  var map = {};

  inputSources.forEach(function(jsSource) {
    jsSource.requires.forEach(function(namespace) {
      var deps = DepsTree._resolveDependencies(
        namespace, [], this._providesMap, []);

      deps.forEach(function(source) {
        if (!map[source.path]) {
          depsSources.push(source);
          map[source.path] = 1;
        }
      }, this);
    }, this);

    if (!map[jsSource.path]) {
      depsSources.push(jsSource);
      map[jsSource.path] = 1;
    }
  }, this);

  return depsSources;
};

/**
 * @param {Array.<Source>} sources
 * @return {!Object.<Source>}
 * @private
 */
DepsTree.prototype._getProvidesMap = function(sources) {
  var map = {};

  // Ensure nothing was provided twice.
  sources.forEach(function(source) {
    source.provides.forEach(function(provide) {
      if (map[provide] && map[provide].path != source.path) {
        throw new Error('Namespace "' + provide + '" provided more than once ' +
          'in sources:\n\n' + [map[provide], source]);
      }

      map[provide] = source;
    }, this);
  }, this);

  return map;
};
<commit_msg>More verbose Namespaces-never-provided error message<commit_after>/**
 * Class to represent a full Closure Library dependency tree.
 *
 * Offers a queryable tree of dependencies of a given set of sources.  The tree
 * will also do logical validation to prevent duplicate provides and circular
 * dependencies.
 */

/**
 * Represents the set of dependencies between source files.
 *
 * @param {Array.<Source>} sources
 * @constructor
 */
var DepsTree = module.exports = function(sources) {
  /** @private {Array.<Source>} */
  this._sources = sources;
  /** @private {Object.<Source>} */
  this._providesMap = this._getProvidesMap(this._sources);

  // Check that all required namespaces are provided.
  /*this._sources.forEach(function(source) {
    source.requires.forEach(function(require) {
      if (!this._providesMap[require]) {
        throw new Error('Namespace "' + require + '" never provided. ' +
          'Required in ' + source);
      }
    }, this);
  }, this);*/
};

/**
 * Resolve dependencies for Closure source files.
 *
 * Follows the dependency tree down and builds a list of sources in dependency
 * order.  This function will recursively call itself to fill all dependencies
 * below the requested namespaces, and then append its sources at the end of
 * the list.
 *
 * @param {string} requiredNamespace
 * @param {!Array.<Source>} depsList List of sources in dependency order.
 *    This function will append the required source once all of its dependencies
 *    are satisfied.
 * @param {Object.<string>} providesMap Map from namespace to source that
 *    provides it.
 * @param {Array.<string>} traversalPath List of namespaces of our path from
 *    the root down the dependency/recursion tree.  Used to identify cyclical
 *    dependencies. This is a list used as a stack -- when the function is
 *    entered, the current namespace is pushed and popped right before
 *    returning. Each recursive call will check that the current namespace does
 *    not appear in the list, throwing a error if it does.
 * @return {!Array.<Source>} The given depsList object filled with sources
 *    in dependency order.
 * @private
 */
DepsTree._resolveDependencies = function(requiredNamespace, depsList,
    providesMap, traversalPath) {
  /** @type {Source} */
  var source = providesMap[requiredNamespace];

  if (!source) {
    throw new Error('Namespace "' + requiredNamespace + '" never provided. ' +
        'Required in ' + providesMap[traversalPath.pop()].path);
  }

  if (-1 < traversalPath.indexOf(requiredNamespace)) {
    traversalPath.push(requiredNamespace); // do this *after* the test

    // This must be a cycle.
    throw new Error('Encountered circular dependency:\n\n' + traversalPath);
  }

  // If we don't have the source yet, we'll have to visit this namespace and
  // add the required dependencies to depsList.
  if (-1 == depsList.indexOf(source)) {
    traversalPath.push(requiredNamespace);

    source.requires.forEach(function(require) {
      // Append all other dependencies before we append our own.
      DepsTree._resolveDependencies(
        require, depsList, providesMap, traversalPath);
    }, this);

    depsList.push(source);
    traversalPath.pop();
  }

  return depsList;
};

/**
 * Get source dependencies, in order.
 *
 * @param {Array.<Source>} inputSources List of input source objects.
 * @return {!Array.<Source>} A list of source objects that provide those
 *    namespaces and all requirements, in dependency order.
 */
DepsTree.prototype.getDependencies = function(inputSources) {
  /** @type {!Array.<Source>} */
  var depsSources = [];
  var map = {};

  inputSources.forEach(function(jsSource) {
    jsSource.requires.forEach(function(namespace) {
      var deps = DepsTree._resolveDependencies(
        namespace, [], this._providesMap, []);

      deps.forEach(function(source) {
        if (!map[source.path]) {
          depsSources.push(source);
          map[source.path] = 1;
        }
      }, this);
    }, this);

    if (!map[jsSource.path]) {
      depsSources.push(jsSource);
      map[jsSource.path] = 1;
    }
  }, this);

  return depsSources;
};

/**
 * @param {Array.<Source>} sources
 * @return {!Object.<Source>}
 * @private
 */
DepsTree.prototype._getProvidesMap = function(sources) {
  var map = {};

  // Ensure nothing was provided twice.
  sources.forEach(function(source) {
    source.provides.forEach(function(provide) {
      if (map[provide] && map[provide].path != source.path) {
        throw new Error('Namespace "' + provide + '" provided more than once ' +
          'in sources:\n\n' + [map[provide], source]);
      }

      map[provide] = source;
    }, this);
  }, this);

  return map;
};
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

const fs = require('fs');
const path = require('path');
const util = require('util');
const _ = require('lodash');
const async = require('async');
const csv = require('csv-parse');
const cli = require('cli');
const YAML = require('yaml-js');
const moment = require('moment');
const chalk = require('chalk');
const defaultOptions = require('rc')('artillery');
const validate = require('../../../artillery-core').validate;

const createRunner = require('../runner');

module.exports = run;

function run(scriptPath, options) {
  let logfile;

  // is the destination a directory that exists?
  let isDir = false;
  if (options.output && options.output !== defaultOptions.output) {
    try {
      isDir = fs.statSync(options.output).isDirectory();
    } catch (err) {
      // ENOENT, don't need to do anything
    }
  }

  const defaultFormat = '[artillery_report_]YMMDD_HHmmSS[.json]';
  if (!isDir && options.output) {
    // -o is set with a filename (existing or not)
    logfile = options.output;
  } else if (!isDir && !options.output) {
    // no -o set
    logfile = moment().format(defaultOptions.output ||
                              defaultFormat);
  } else {
    // -o is set with a directory
    logfile = path.join(options.output, moment().format(defaultOptions.output ||
                                                        defaultFormat));
  }

  function log() {
    if (options.quiet) { return; }
    console.log.apply(console, arguments);
  }

  async.waterfall([
    function readScript(callback) {

      fs.readFile(scriptPath, 'utf-8', function(err, data) {

        if (err) {
          const msg = util.format('File not found: %s', scriptPath);
          return callback(new Error(msg), null);
        }

        let script;
        let fileformat;
        try {
          if (/\.ya?ml$/.test(path.extname(scriptPath))) {
            fileformat = 'YAML';
            script = YAML.load(data);
          } else {
            fileformat = 'JSON';
            script = JSON.parse(data);
          }
        } catch (e) {
          const msg2 = `File ${scriptPath} does not appear to be valid ${fileformat}: (${e.message})`;
          return callback(new Error(msg2), null);
        }

        if (options.target && script.config) {
          script.config.target = options.target;
        }

        if (!script.config.target && !options.environment) {
          const msg4 = 'No target specified and no environment chosen';
          return callback(new Error(msg4), null);
        }

        let validation = validate(script);
        if (!validation.valid) {
          log(validation.errors);
          return callback(new Error('Test script validation error'), null);
        }

        return callback(null, {script: script});
      });
    },
    function readPayload(context, callback) {

      if (context.script.config.payload && _.isArray(context.script.config.payload)) {
        async.map(context.script.config.payload,
          function(item, callback2) {
            let absoluteScriptPath = path.resolve(process.cwd(), scriptPath);
            let payloadFile = path.resolve(path.dirname(absoluteScriptPath), item.path);

            let data = fs.readFileSync(payloadFile, 'utf-8');
            csv(data, function(err, parsedData) {
              item.data = parsedData;
              return callback2(err, item);
            });
          },
          function(err, results) {
            if (err) {
              return callback(err, null);
            }
            context.payload = results;
            return callback(null, context);
          });
      } else if (context.script.config.payload &&
                 (_.isObject(context.script.config.payload) ||
                 options.payload)) {

        //use config if path set else use command line
        let csvdata;
        if (context.script.config.payload.path) {
          let absoluteScriptPath = path.resolve(process.cwd(), scriptPath);
          let payloadFile = path.resolve(path.dirname(absoluteScriptPath), context.script.config.payload.path);
          csvdata = fs.readFileSync(payloadFile, 'utf-8');
        } else {
          csvdata = fs.readFileSync(options.payload, 'utf-8');
        }
        csv(csvdata, function(err, payload) {

          if (err) {
            const msg3 = util.format(
              'File %s does not appear to be valid CSV', options.payload);
            return callback(new Error(msg3), null);
          }

          context.payload = payload;

          return callback(null, context);
        });
      } else {
        if (context.script.config.payload) {
          log(
            'WARNING: payload file not set, but payload is configured in %s\n',
            scriptPath);
        }

        return callback(null, context);
      }
    },
    function checkIfXPathIsUsed(context, callback) {
      // FIXME: This should probably be in core.
      let xmlInstalled = null;
      try {
        xmlInstalled = require('artillery-xml-capture');
      } catch (e) {
      }

      let usesXPathCapture = false;
      context.script.scenarios.forEach(function(scenario) {
        scenario.flow.forEach(function(step) {
          let params = step[_.keys(step)[0]];
          if ((params.capture && params.capture.xpath) ||
              (params.match && params.match.xpath)) {
            usesXPathCapture = true;
          }
        });
      });
      if (usesXPathCapture && !xmlInstalled) {
        console.log(chalk.bold.red('Warning: '), chalk.bold.yellow('your test script is using XPath capture, but artillery-xml-capture does not seem to be installed.'));
        console.log(chalk.bold.yellow('Install it with: npm install -g artillery-xml-capture'));
      }
      return callback(null, context);
    }
    ],
    function(err, result) {

      if (err) {
        log(err.message);
        process.exit(1);
      }

      if (result.script.config.processor) {
        let absoluteScriptPath = path.resolve(process.cwd(), scriptPath);
        let processorPath = path.resolve(path.dirname(absoluteScriptPath), result.script.config.processor);
        let processor = require(processorPath);
        result.script.config.processor = processor;
      }

      if (options.insecure) {
        if (result.script.config.tls) {
          if (result.script.config.tls.rejectUnauthorized) {
            log('WARNING: TLS certificate validation enabled in the ' +
                        'test script, but explicitly disabled with ' +
                        '-k/--insecure.');
          }
          result.script.config.tls.rejectUnauthorized = false;
        } else {
          result.script.config.tls = {rejectUnauthorized: false};
        }
      }

      let intermediates = [];
      result.script.config.statsInterval = result.script.config.statsInterval || 10;

      log('Log file: %s', logfile);

      var spinnerOn = function() {
        if (!options.quiet && process.stdout.isTTY) {
          cli.spinner('');
        }
      };
      var spinnerOff = function() {
        if (!options.quiet && process.stdout.isTTY) {
          cli.spinner('', true);
        }
      };

      let runner = createRunner(result.script,
                                result.payload,
                                { environment: options.environment });

      runner.events.on('phaseStarted', function(phase) {
        spinnerOff();
        log(
          'Phase %s%s started - duration: %ss',
          phase.index,
          (phase.name ? ' (' + phase.name + ')' : ''),
          phase.duration || phase.think);
        spinnerOn();
      });

      runner.events.on('stats', function(stats) {
        intermediates.push(stats.report());
        let report = stats.report();
        spinnerOff();
        log('Report for the previous %ss @ %s',
            result.script.config.statsInterval,
            report.timestamp);
        printReport(report);
        spinnerOn();
      });

      runner.events.on('done', function(allStats) {
        let report = allStats.report();
        delete report.concurrency; // doesn't make sense in this context
        delete report.pendingRequests;

        spinnerOff();
        log('all scenarios completed');
        log('Complete report @ %s', report.timestamp);

        printReport(report, { showScenarioCounts: true });

        report.phases = _.get(result, 'script.config.phases', []);
        delete report.latencies;

        fs.writeFileSync(
          logfile, JSON.stringify({
            aggregate: report,
            intermediate: intermediates
          }, null, 2),
          {flag: 'w'});

        if (result.script.config.ensure) {
          const latency = report.latency;
          _.each(result.script.config.ensure, function(max, k) {

            let bucket = k === 'p50' ? 'median' : k;
            if (latency[bucket]) {
              if (latency[bucket] > max) {
                const msg = util.format(
                  'ensure condition failed: ensure.%s < %s', bucket, max);
                log(msg);
                process.exit(1);
              }
            }
          });
        }
      });

      spinnerOn();
      runner.run();
    });
}

function printReport(report, opts) {
  opts = opts || {};

  console.log('  Scenarios launched:  %s', report.scenariosCreated);
  console.log('  Scenarios completed: %s', report.scenariosCompleted);
  console.log('  Requests completed:  %s', report.requestsCompleted);

  // Final report does not have concurrency
  if (report.concurrency) {
    console.log('  Concurrent users:    %s', report.concurrency);
  }

  console.log('  RPS sent: %s', report.rps.mean);
  console.log('  Request latency:');
  console.log('    min: %s', report.latency.min);
  console.log('    max: %s', report.latency.max);
  console.log('    median: %s', report.latency.median);
  console.log('    p95: %s', report.latency.p95);
  console.log('    p99: %s', report.latency.p99);

  console.log('  Scenario duration:');
  console.log('    min: %s', report.scenarioDuration.min);
  console.log('    max: %s', report.scenarioDuration.max);
  console.log('    median: %s', report.scenarioDuration.median);
  console.log('    p95: %s', report.scenarioDuration.p95);
  console.log('    p99: %s', report.scenarioDuration.p99);

  // We only want to show this for the aggregate report
  if (opts.showScenarioCounts && report.scenarioCounts) {
    console.log('  Scenario counts:');
    _.each(report.scenarioCounts, function(count, name) {
      let percentage = Math.round(count / report.scenariosCompleted * 100 * 1000) / 1000;
      console.log('    %s: %s (%s\%)', name, count, percentage);
    });
  }

  if (_.size(report.customStats) > 0) {
    console.log('Custom stats:');
    _.each(report.customStats, function(r, n) {
      console.log('  %s:', n);
      console.log('    min: %s', r.min);
      console.log('    max: %s', r.max);
      console.log('    median: %s', r.median);
      console.log('    p95: %s', r.p95);
      console.log('    p99: %s', r.p99);
    });
  }

  if (_.keys(report.codes).length !== 0) {
    console.log('  Codes:');
    _.each(report.codes, function(count, code) {
      console.log('    %s: %s', code, count);
    });
  }
  if (_.keys(report.errors).length !== 0) {
    console.log('  Errors:');
    _.each(report.errors, function(count, code) {
      console.log('    %s: %s', code, count);
    });
  }
}
<commit_msg>Remove repeated variable declaration<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

const fs = require('fs');
const path = require('path');
const util = require('util');
const _ = require('lodash');
const async = require('async');
const csv = require('csv-parse');
const cli = require('cli');
const YAML = require('yaml-js');
const moment = require('moment');
const chalk = require('chalk');
const defaultOptions = require('rc')('artillery');
const validate = require('../../../artillery-core').validate;

const createRunner = require('../runner');

module.exports = run;

function run(scriptPath, options) {
  let logfile;

  // is the destination a directory that exists?
  let isDir = false;
  if (options.output && options.output !== defaultOptions.output) {
    try {
      isDir = fs.statSync(options.output).isDirectory();
    } catch (err) {
      // ENOENT, don't need to do anything
    }
  }

  const defaultFormat = '[artillery_report_]YMMDD_HHmmSS[.json]';
  if (!isDir && options.output) {
    // -o is set with a filename (existing or not)
    logfile = options.output;
  } else if (!isDir && !options.output) {
    // no -o set
    logfile = moment().format(defaultOptions.output ||
                              defaultFormat);
  } else {
    // -o is set with a directory
    logfile = path.join(options.output, moment().format(defaultOptions.output ||
                                                        defaultFormat));
  }

  function log() {
    if (options.quiet) { return; }
    console.log.apply(console, arguments);
  }

  async.waterfall([
    function readScript(callback) {

      fs.readFile(scriptPath, 'utf-8', function(err, data) {

        if (err) {
          const msg = util.format('File not found: %s', scriptPath);
          return callback(new Error(msg), null);
        }

        let script;
        let fileformat;
        try {
          if (/\.ya?ml$/.test(path.extname(scriptPath))) {
            fileformat = 'YAML';
            script = YAML.load(data);
          } else {
            fileformat = 'JSON';
            script = JSON.parse(data);
          }
        } catch (e) {
          const msg2 = `File ${scriptPath} does not appear to be valid ${fileformat}: (${e.message})`;
          return callback(new Error(msg2), null);
        }

        if (options.target && script.config) {
          script.config.target = options.target;
        }

        if (!script.config.target && !options.environment) {
          const msg4 = 'No target specified and no environment chosen';
          return callback(new Error(msg4), null);
        }

        let validation = validate(script);
        if (!validation.valid) {
          log(validation.errors);
          return callback(new Error('Test script validation error'), null);
        }

        return callback(null, {script: script});
      });
    },
    function readPayload(context, callback) {

      if (context.script.config.payload && _.isArray(context.script.config.payload)) {
        async.map(context.script.config.payload,
          function(item, callback2) {
            let absoluteScriptPath = path.resolve(process.cwd(), scriptPath);
            let payloadFile = path.resolve(path.dirname(absoluteScriptPath), item.path);

            let data = fs.readFileSync(payloadFile, 'utf-8');
            csv(data, function(err, parsedData) {
              item.data = parsedData;
              return callback2(err, item);
            });
          },
          function(err, results) {
            if (err) {
              return callback(err, null);
            }
            context.payload = results;
            return callback(null, context);
          });
      } else if (context.script.config.payload &&
                 (_.isObject(context.script.config.payload) ||
                 options.payload)) {

        //use config if path set else use command line
        let csvdata;
        if (context.script.config.payload.path) {
          let absoluteScriptPath = path.resolve(process.cwd(), scriptPath);
          let payloadFile = path.resolve(path.dirname(absoluteScriptPath), context.script.config.payload.path);
          csvdata = fs.readFileSync(payloadFile, 'utf-8');
        } else {
          csvdata = fs.readFileSync(options.payload, 'utf-8');
        }
        csv(csvdata, function(err, payload) {

          if (err) {
            const msg3 = util.format(
              'File %s does not appear to be valid CSV', options.payload);
            return callback(new Error(msg3), null);
          }

          context.payload = payload;

          return callback(null, context);
        });
      } else {
        if (context.script.config.payload) {
          log(
            'WARNING: payload file not set, but payload is configured in %s\n',
            scriptPath);
        }

        return callback(null, context);
      }
    },
    function checkIfXPathIsUsed(context, callback) {
      // FIXME: This should probably be in core.
      let xmlInstalled = null;
      try {
        xmlInstalled = require('artillery-xml-capture');
      } catch (e) {
      }

      let usesXPathCapture = false;
      context.script.scenarios.forEach(function(scenario) {
        scenario.flow.forEach(function(step) {
          let params = step[_.keys(step)[0]];
          if ((params.capture && params.capture.xpath) ||
              (params.match && params.match.xpath)) {
            usesXPathCapture = true;
          }
        });
      });
      if (usesXPathCapture && !xmlInstalled) {
        console.log(chalk.bold.red('Warning: '), chalk.bold.yellow('your test script is using XPath capture, but artillery-xml-capture does not seem to be installed.'));
        console.log(chalk.bold.yellow('Install it with: npm install -g artillery-xml-capture'));
      }
      return callback(null, context);
    }
    ],
    function(err, result) {

      if (err) {
        log(err.message);
        process.exit(1);
      }

      if (result.script.config.processor) {
        let absoluteScriptPath = path.resolve(process.cwd(), scriptPath);
        let processorPath = path.resolve(path.dirname(absoluteScriptPath), result.script.config.processor);
        let processor = require(processorPath);
        result.script.config.processor = processor;
      }

      if (options.insecure) {
        if (result.script.config.tls) {
          if (result.script.config.tls.rejectUnauthorized) {
            log('WARNING: TLS certificate validation enabled in the ' +
                        'test script, but explicitly disabled with ' +
                        '-k/--insecure.');
          }
          result.script.config.tls.rejectUnauthorized = false;
        } else {
          result.script.config.tls = {rejectUnauthorized: false};
        }
      }

      result.script.config.statsInterval = result.script.config.statsInterval || 10;

      log('Log file: %s', logfile);

      var spinnerOn = function() {
        if (!options.quiet && process.stdout.isTTY) {
          cli.spinner('');
        }
      };
      var spinnerOff = function() {
        if (!options.quiet && process.stdout.isTTY) {
          cli.spinner('', true);
        }
      };

      let runner = createRunner(result.script,
                                result.payload,
                                { environment: options.environment });

      runner.events.on('phaseStarted', function(phase) {
        spinnerOff();
        log(
          'Phase %s%s started - duration: %ss',
          phase.index,
          (phase.name ? ' (' + phase.name + ')' : ''),
          phase.duration || phase.think);
        spinnerOn();
      });

      runner.events.on('stats', function(stats) {
        intermediates.push(stats.report());
        let report = stats.report();
        spinnerOff();
        log('Report for the previous %ss @ %s',
            result.script.config.statsInterval,
            report.timestamp);
        printReport(report);
        spinnerOn();
      });

      runner.events.on('done', function(allStats) {
        let report = allStats.report();
        delete report.concurrency; // doesn't make sense in this context
        delete report.pendingRequests;

        spinnerOff();
        log('all scenarios completed');
        log('Complete report @ %s', report.timestamp);

        printReport(report, { showScenarioCounts: true });

        report.phases = _.get(result, 'script.config.phases', []);
        delete report.latencies;

        fs.writeFileSync(
          logfile, JSON.stringify({
            aggregate: report,
            intermediate: intermediates
          }, null, 2),
          {flag: 'w'});

        if (result.script.config.ensure) {
          const latency = report.latency;
          _.each(result.script.config.ensure, function(max, k) {

            let bucket = k === 'p50' ? 'median' : k;
            if (latency[bucket]) {
              if (latency[bucket] > max) {
                const msg = util.format(
                  'ensure condition failed: ensure.%s < %s', bucket, max);
                log(msg);
                process.exit(1);
              }
            }
          });
        }
      });

      spinnerOn();
      runner.run();
    });
}

function printReport(report, opts) {
  opts = opts || {};

  console.log('  Scenarios launched:  %s', report.scenariosCreated);
  console.log('  Scenarios completed: %s', report.scenariosCompleted);
  console.log('  Requests completed:  %s', report.requestsCompleted);

  // Final report does not have concurrency
  if (report.concurrency) {
    console.log('  Concurrent users:    %s', report.concurrency);
  }

  console.log('  RPS sent: %s', report.rps.mean);
  console.log('  Request latency:');
  console.log('    min: %s', report.latency.min);
  console.log('    max: %s', report.latency.max);
  console.log('    median: %s', report.latency.median);
  console.log('    p95: %s', report.latency.p95);
  console.log('    p99: %s', report.latency.p99);

  console.log('  Scenario duration:');
  console.log('    min: %s', report.scenarioDuration.min);
  console.log('    max: %s', report.scenarioDuration.max);
  console.log('    median: %s', report.scenarioDuration.median);
  console.log('    p95: %s', report.scenarioDuration.p95);
  console.log('    p99: %s', report.scenarioDuration.p99);

  // We only want to show this for the aggregate report
  if (opts.showScenarioCounts && report.scenarioCounts) {
    console.log('  Scenario counts:');
    _.each(report.scenarioCounts, function(count, name) {
      let percentage = Math.round(count / report.scenariosCompleted * 100 * 1000) / 1000;
      console.log('    %s: %s (%s\%)', name, count, percentage);
    });
  }

  if (_.size(report.customStats) > 0) {
    console.log('Custom stats:');
    _.each(report.customStats, function(r, n) {
      console.log('  %s:', n);
      console.log('    min: %s', r.min);
      console.log('    max: %s', r.max);
      console.log('    median: %s', r.median);
      console.log('    p95: %s', r.p95);
      console.log('    p99: %s', r.p99);
    });
  }

  if (_.keys(report.codes).length !== 0) {
    console.log('  Codes:');
    _.each(report.codes, function(count, code) {
      console.log('    %s: %s', code, count);
    });
  }
  if (_.keys(report.errors).length !== 0) {
    console.log('  Errors:');
    _.each(report.errors, function(count, code) {
      console.log('    %s: %s', code, count);
    });
  }
}
<|endoftext|>


<commit_before>var RSVP = require("rsvp"),
    _ = require("lodash");

module.exports = function(){
  var handlers = {};

  var createRequest = function(collection, opt, httpMethod){
    return _.extend(opt, {
      direct: true,
      method: httpMethod,
      headers: _.extend({
        "content-type": "application/json"
      }, opt.headers),
      get: function(h){ return this.headers[h]; },
      set: function(key,val){ this.headers[key] = val},
      fortune: {requestMetadata: {direct: true}},
      query: opt.query || {},
      params: opt.params || {},
      zipkinTraceId: opt.zipkinTraceId,
      path: '/' + ((opt.params && opt.params.id) ? collection + '/' + opt.params.id : collection)
    });
  };

  var createResponse = function(){
    var deferred = RSVP.defer(),
        res = {
          headers: {},
          set: function(key,val){ this.headers[key] = val; },
          get: function(h){ return this.headers[h]},
          setHeader: function(key,val){this.set(key,val);},
          status: function(){return this;}, //it's not used anywhere
          send: function(body){
            deferred.resolve(_.extend({
              body: body && JSON.parse(body)
            }, res));
          },
          promise: deferred.promise
        };

    return res;
  };

  var asSingleOrCollection = function(method, collection, options, httpMethod){
    var req, res,
        methodName = (options.params || {}).id ? method : (method + "All");

    handlers[collection][methodName](req = createRequest(collection, options, httpMethod), res = createResponse());

    return res.promise;
  };

  return {
    _createRequest: createRequest,
    _createResponse: createResponse,
    methods: {
      create: function(collection, options){
        var req, res;

        options.body = _.cloneDeep(options.body);

        handlers[collection].create(req = createRequest(collection, options, "POST"),res = createResponse());

        return res.promise;
      },
      get: function(collection,options){
        return asSingleOrCollection("get", collection, options || {}, "GET");
      },
      destroy: function(collection,options){
        return asSingleOrCollection("destroy", collection, options || {}, "DELETE");
      },
      replace: function(collection, options){
        var req,res;

        options.body = _.cloneDeep(options.body);

        handlers[collection].replace(req = createRequest(collection, options, "PUT"), res = createResponse());
        return res.promise;
      },
      update: function(collection, options){
        var req, res;

        options.body = _.cloneDeep(options.body);

        handlers[collection].update(req = createRequest(collection, options, "PATCH"), res = createResponse());

        return res.promise;
      },
      callAction: function(collection, method, options){
        var req, res;

        options.body = _.cloneDeep(options.body);
        handlers[collection].callAction(req = createRequest(collection, options, method), res = createResponse());

        return res.promise;
      },
      callGenericAction: function(collection, method, options){
        var req, res;

        options.body = _.cloneDeep(options.body);
        handlers[collection].callGenericAction(req = createRequest(collection, options, method), res = createResponse());

        return res.promise;
      }
    },
    registerResource: function(collection, callbacks){
      var handlerNames = [ "create", "update", "replace", "destroy", "destroyAll", "get", "getAll", "callAction", "callGenericAction"];
      if(!_.isEqual(_.keys(callbacks), handlerNames)){
        throw new Error("Wrong route handler names");
      }
      handlers[collection] = callbacks;
    }
  };
};
<commit_msg>SUP-3478: make director.js to create url inside reqeust<commit_after>var RSVP = require("rsvp"),
    _ = require("lodash");

module.exports = function(){
  var handlers = {};

  var createRequest = function(collection, opt, httpMethod){
    var createUrl = function(path, query) {
      var queryStr = '';
      _.each(Object.keys(query), function(key) {
        if (queryStr !== '') queryStr += '&';
        queryStr += key + '=' + query[key];
      });

      return path + '?' + queryStr;
    };
    var path = '/' + ((opt.params && opt.params.id) ? collection + '/' + opt.params.id : collection);

    return _.extend(opt, {
      direct: true,
      method: httpMethod,
      headers: _.extend({
        "content-type": "application/json"
      }, opt.headers),
      get: function(h){ return this.headers[h]; },
      set: function(key,val){ this.headers[key] = val},
      fortune: {requestMetadata: {direct: true}},
      query: opt.query || {},
      params: opt.params || {},
      zipkinTraceId: opt.zipkinTraceId,
      path: path,
      url = path + '?' + createUrl(),
    });
  };

  var createResponse = function(){
    var deferred = RSVP.defer(),
        res = {
          headers: {},
          set: function(key,val){ this.headers[key] = val; },
          get: function(h){ return this.headers[h]},
          setHeader: function(key,val){this.set(key,val);},
          status: function(){return this;}, //it's not used anywhere
          send: function(body){
            deferred.resolve(_.extend({
              body: body && JSON.parse(body)
            }, res));
          },
          promise: deferred.promise
        };

    return res;
  };

  var asSingleOrCollection = function(method, collection, options, httpMethod){
    var req, res,
        methodName = (options.params || {}).id ? method : (method + "All");

    handlers[collection][methodName](req = createRequest(collection, options, httpMethod), res = createResponse());

    return res.promise;
  };

  return {
    _createRequest: createRequest,
    _createResponse: createResponse,
    methods: {
      create: function(collection, options){
        var req, res;

        options.body = _.cloneDeep(options.body);

        handlers[collection].create(req = createRequest(collection, options, "POST"),res = createResponse());

        return res.promise;
      },
      get: function(collection,options){
        return asSingleOrCollection("get", collection, options || {}, "GET");
      },
      destroy: function(collection,options){
        return asSingleOrCollection("destroy", collection, options || {}, "DELETE");
      },
      replace: function(collection, options){
        var req,res;

        options.body = _.cloneDeep(options.body);

        handlers[collection].replace(req = createRequest(collection, options, "PUT"), res = createResponse());
        return res.promise;
      },
      update: function(collection, options){
        var req, res;

        options.body = _.cloneDeep(options.body);

        handlers[collection].update(req = createRequest(collection, options, "PATCH"), res = createResponse());

        return res.promise;
      },
      callAction: function(collection, method, options){
        var req, res;

        options.body = _.cloneDeep(options.body);
        handlers[collection].callAction(req = createRequest(collection, options, method), res = createResponse());

        return res.promise;
      },
      callGenericAction: function(collection, method, options){
        var req, res;

        options.body = _.cloneDeep(options.body);
        handlers[collection].callGenericAction(req = createRequest(collection, options, method), res = createResponse());

        return res.promise;
      }
    },
    registerResource: function(collection, callbacks){
      var handlerNames = [ "create", "update", "replace", "destroy", "destroyAll", "get", "getAll", "callAction", "callGenericAction"];
      if(!_.isEqual(_.keys(callbacks), handlerNames)){
        throw new Error("Wrong route handler names");
      }
      handlers[collection] = callbacks;
    }
  };
};
<|endoftext|>


<commit_before>var Ghost = function(args) {
  this.x = args.startX;
  this.y = args.startY;
  this.direction = args.direction ? args.direction : "left";
  this.speed = GHOST_MOVE_DELAY;
  this.context = args.context;
  this.name = args.name;
  this.color = NAME_TO_COLOR[args.name];
  this.movementMode = args.mode ? args.mode : "scatter";
  this.targetTile = GHOST_CORNERS[this.name];
  this.pelletLimit = args.limit ? args.limit : 0;
}
Ghost.blinkyTarget = function(args) {
  return {
    x: args.coords.x,
    y: args.coords.y
  }
};
Ghost.pinkyTarget = function(args) {
  var xMod = 0;
  var yMod = 0;
  switch(args.direction) {
    case "up": yMod -= 4; break;
    case "down": yMod += 4; break;
    case "left": xMod -= 4; break;
    case "right": xMod += 4; break;
  }
  return {
    x: args.coords.x + xMod,
    y: args.coords.y + yMod
  }
};
Ghost.inkyTarget = function(args) {
  var xMod = 0;
  var yMod = 0;
  switch(args.direction) {
    case "up": yMod -= 2; break;
    case "down": yMod += 2; break;
    case "left": xMod -= 2; break;
    case "right": xMod += 2; break;
  }
  var pacDistance = {
    x: (args.coords.x + xMod) - args.blinky.x,
    y: (args.coords.y + yMod) - args.blinky.y
  }

  return {
    x: args.coords.x + pacDistance.x,
    y: args.coords.y + pacDistance.y
  }
};
Ghost.clydeTarget = function(args) {
  var distance = Board.getTileDistance(args.coords, args.ghost);
  if(distance > 8) {
    return args.coords;
  }
  else {
    return GHOST_CORNERS.c;
  }
};
Ghost.prototype.isCentered = function() {
  return this.x % 8 == 4 && this.y % 8 == 4;
};
Ghost.prototype.getClearDimensions = function() {
  return {
    x: this.x - 7,
    y: this.y - 7,
    w: 14,
    h: 16
  }
};
Ghost.prototype.move = function() {
  switch(this.direction){
    case "up": this.y--; break;
    case "down": this.y++; break;
    case "left": this.x--; break;
    case "right": this.x++; break;
  }
};
Ghost.prototype.exitHouse = function() {
  this.setMode("exitting");
  if(this.x < BOARD_WIDTH / 2) {
    this.direction = "right";
  }
  else if(this.x > BOARD_WIDTH / 2) {
    this.direction = "left";
  }
  else {
    this.direction = "up";
  }
  if(this.y <= 116) {
    this.setMode("scatter");
  }
};
Ghost.prototype.setMode = function(modeName) {
  if(GHOST_MODES.includes(modeName)) {
    this.movementMode = modeName;
  }
};
Ghost.prototype.handlePelletCount = function(pelletCount) {
  if(pelletCount >= this.pelletLimit) {
    this.setMode("exitting");
  }
};
<commit_msg>Remove unused context prop from Ghost<commit_after>var Ghost = function(args) {
  this.x = args.startX;
  this.y = args.startY;
  this.direction = args.direction ? args.direction : "left";
  this.speed = GHOST_MOVE_DELAY;
  this.name = args.name;
  this.color = NAME_TO_COLOR[args.name];
  this.movementMode = args.mode ? args.mode : "scatter";
  this.targetTile = GHOST_CORNERS[this.name];
  this.pelletLimit = args.limit ? args.limit : 0;
}
Ghost.blinkyTarget = function(args) {
  return {
    x: args.coords.x,
    y: args.coords.y
  }
};
Ghost.pinkyTarget = function(args) {
  var xMod = 0;
  var yMod = 0;
  switch(args.direction) {
    case "up": yMod -= 4; break;
    case "down": yMod += 4; break;
    case "left": xMod -= 4; break;
    case "right": xMod += 4; break;
  }
  return {
    x: args.coords.x + xMod,
    y: args.coords.y + yMod
  }
};
Ghost.inkyTarget = function(args) {
  var xMod = 0;
  var yMod = 0;
  switch(args.direction) {
    case "up": yMod -= 2; break;
    case "down": yMod += 2; break;
    case "left": xMod -= 2; break;
    case "right": xMod += 2; break;
  }
  var pacDistance = {
    x: (args.coords.x + xMod) - args.blinky.x,
    y: (args.coords.y + yMod) - args.blinky.y
  }

  return {
    x: args.coords.x + pacDistance.x,
    y: args.coords.y + pacDistance.y
  }
};
Ghost.clydeTarget = function(args) {
  var distance = Board.getTileDistance(args.coords, args.ghost);
  if(distance > 8) {
    return args.coords;
  }
  else {
    return GHOST_CORNERS.c;
  }
};
Ghost.prototype.isCentered = function() {
  return this.x % 8 == 4 && this.y % 8 == 4;
};
Ghost.prototype.getClearDimensions = function() {
  return {
    x: this.x - 7,
    y: this.y - 7,
    w: 14,
    h: 16
  }
};
Ghost.prototype.move = function() {
  switch(this.direction){
    case "up": this.y--; break;
    case "down": this.y++; break;
    case "left": this.x--; break;
    case "right": this.x++; break;
  }
};
Ghost.prototype.exitHouse = function() {
  this.setMode("exitting");
  if(this.x < BOARD_WIDTH / 2) {
    this.direction = "right";
  }
  else if(this.x > BOARD_WIDTH / 2) {
    this.direction = "left";
  }
  else {
    this.direction = "up";
  }
  if(this.y <= 116) {
    this.setMode("scatter");
  }
};
Ghost.prototype.setMode = function(modeName) {
  if(GHOST_MODES.includes(modeName)) {
    this.movementMode = modeName;
  }
};
Ghost.prototype.handlePelletCount = function(pelletCount) {
  if(pelletCount >= this.pelletLimit) {
    this.setMode("exitting");
  }
};
<|endoftext|>


<commit_before>import React from 'react'
import htmlescape from 'htmlescape'

export default ({ head, css, html, data, dev, staticMarkup }) => {
  return

      {(head || []).map((h, i) => React.cloneElement(h, { key: i }))}


<commit_before>import fs from 'fs';
import request from 'request';
import path from 'path';

export const download = (uri, filename, callback) => {
  request.head(uri, function(err, res, body){
    const pathname = path.join(__dirname, 'storage', 'images', filename);
    request(uri).pipe(fs.createWriteStream(pathname)).on('close', callback);
  });
};
<commit_msg>Remove unused params<commit_after>import fs from 'fs';
import request from 'request';
import path from 'path';

export const download = (uri, filename, callback) => {
  request.head(uri, () => {
    const pathname = path.join(__dirname, 'storage', 'images', filename);
    request(uri).pipe(fs.createWriteStream(pathname)).on('close', callback);
  });
};
<|endoftext|>


<commit_before>var _ = require('lodash');

var resources = require('../dummy/resources');

var DummyResource = resources.DummyResource;

var DummyConfigResource = DummyResource.extend(function(self, name, store) {
    /**class:DummyConfigResource(name)

    Handles api requests to the config resource from :class:`DummyApi`.

    :param string name:
        The name of the resource. Should match the name given in api requests.
    */
    DummyResource.call(self, name);

    /**attribute:DummyConfigResource.store
    An object containing sandbox's config data. Properties do not need to be
    JSON-stringified, this is done when the config is retrieved using a
    ``'config.get'`` api request.
    */
    self.store = store || {config: {}};

    /**attribute:DummyConfigResource.app
    A shortcut to DummyConfigResource.store.config (the app's config).
    */
    Object.defineProperty(self, 'app', {
        get: function() {
            return self.store.config;
        },
        set: function(v) {
            self.store.config = v;
            return v;
        }
    });

    self.handlers.get = function(cmd) {
        var value = self.store[cmd.key];

        if (_.isUndefined(value)) {
            value = null;
        }

        return {
            success: true,
            value: JSON.stringify(value)
        };
    };
});

this.DummyConfigResource = DummyConfigResource;
<commit_msg>Add missing article to DummyConfigResource docstring<commit_after>var _ = require('lodash');

var resources = require('../dummy/resources');

var DummyResource = resources.DummyResource;

var DummyConfigResource = DummyResource.extend(function(self, name, store) {
    /**class:DummyConfigResource(name)

    Handles api requests to the config resource from :class:`DummyApi`.

    :param string name:
        The name of the resource. Should match the name given in api requests.
    */
    DummyResource.call(self, name);

    /**attribute:DummyConfigResource.store
    An object containing the sandbox's config data. Properties do not need to be
    JSON-stringified, this is done when the config is retrieved using a
    ``'config.get'`` api request.
    */
    self.store = store || {config: {}};

    /**attribute:DummyConfigResource.app
    A shortcut to DummyConfigResource.store.config (the app's config).
    */
    Object.defineProperty(self, 'app', {
        get: function() {
            return self.store.config;
        },
        set: function(v) {
            self.store.config = v;
            return v;
        }
    });

    self.handlers.get = function(cmd) {
        var value = self.store[cmd.key];

        if (_.isUndefined(value)) {
            value = null;
        }

        return {
            success: true,
            value: JSON.stringify(value)
        };
    };
});

this.DummyConfigResource = DummyConfigResource;
<|endoftext|>


<commit_before>'use strict'

const fs = require('fs')
const path = require('path')
const {merge} = require('lodash')
const deepFreeze = require('deep-freeze')
const {Environment, Permission} = require('./enum')

const appRootPath = path.join(__dirname, '../../')
const env = process.env.NODE_ENV || Environment.development
const debugConfig = {
  debug: (env === Environment.development)
}

const packageConfig = {
  version: '0.5.1',
  minimumCompatibleVersion: '0.5.0'
}

const configFilePath = path.join(__dirname, '../../config.json')
const fileConfig = fs.existsSync(configFilePath) ? require(configFilePath)[env] : undefined

let config = require('./default')
merge(config, require('./defaultSSL'))
merge(config, debugConfig)
merge(config, packageConfig)
merge(config, fileConfig)
merge(config, require('./oldEnvironment'))
merge(config, require('./environment'))
merge(config, require('./dockerSecret'))

// load LDAP CA
if (config.ldap.tlsca) {
  let ca = config.ldap.tlsca.split(',')
  let caContent = []
  for (let i of ca) {
    if (fs.existsSync(ca[i])) {
      caContent.push(fs.readFileSync(ca[i], 'utf8'))
    }
  }
  let tlsOptions = {
    ca: caContent
  }
  config.ldap.tlsOptions = config.ldap.tlsOptions ? Object.assign(config.ldap.tlsOptions, tlsOptions) : tlsOptions
}

// Permission
config.permission = Permission
if (!config.allowanonymous) {
  delete config.permission.freely
}
if (!(config.defaultpermission in config.permission)) {
  config.defaultpermission = config.permission.editable
}

// cache result, cannot change config in runtime!!!
config.isStandardHTTPsPort = (function isStandardHTTPsPort () {
  return config.usessl && config.port === 443
})()
config.isStandardHTTPPort = (function isStandardHTTPPort () {
  return !config.usessl && config.port === 80
})()

// cache serverURL
config.serverurl = (function getserverurl () {
  var url = ''
  if (config.domain) {
    var protocol = config.protocolusessl ? 'https://' : 'http://'
    url = protocol + config.domain
    if (config.urladdport) {
      if (!config.isStandardHTTPPort || !config.isStandardHTTPsPort) {
        url += ':' + config.port
      }
    }
  }
  if (config.urlpath) {
    url += '/' + config.urlpath
  }
  return url
})()

config.Environment = Environment

// auth method
config.isFacebookEnable = config.facebook.clientID && config.facebook.clientSecret
config.isGoogleEnable = config.google.clientID && config.google.clientSecret
config.isDropboxEnable = config.dropbox.clientID && config.dropbox.clientSecret
config.isTwitterEnable = config.twitter.consumerKey && config.twitter.consumerSecret
config.isEmailEnable = config.email
config.isGitHubEnable = config.github.clientID && config.github.clientSecret
config.isGitLabEnable = config.gitlab.clientID && config.gitlab.clientSecret
config.isLDAPEnable = config.ldap.url

// generate correct path
config.sslcapath = path.join(appRootPath, config.sslcapath)
config.sslcertpath = path.join(appRootPath, config.sslcertpath)
config.sslkeypath = path.join(appRootPath, config.sslkeypath)
config.dhparampath = path.join(appRootPath, config.dhparampath)

config.tmppath = path.join(appRootPath, config.tmppath)
config.defaultnotepath = path.join(appRootPath, config.defaultnotepath)
config.docspath = path.join(appRootPath, config.docspath)
config.indexpath = path.join(appRootPath, config.indexpath)
config.hackmdpath = path.join(appRootPath, config.hackmdpath)
config.errorpath = path.join(appRootPath, config.errorpath)
config.prettypath = path.join(appRootPath, config.prettypath)
config.slidepath = path.join(appRootPath, config.slidepath)

// maek config readonly
config = deepFreeze(config)

module.exports = config
<commit_msg>Read to correct tlsca file path<commit_after>'use strict'

const fs = require('fs')
const path = require('path')
const {merge} = require('lodash')
const deepFreeze = require('deep-freeze')
const {Environment, Permission} = require('./enum')

const appRootPath = path.join(__dirname, '../../')
const env = process.env.NODE_ENV || Environment.development
const debugConfig = {
  debug: (env === Environment.development)
}

const packageConfig = {
  version: '0.5.1',
  minimumCompatibleVersion: '0.5.0'
}

const configFilePath = path.join(__dirname, '../../config.json')
const fileConfig = fs.existsSync(configFilePath) ? require(configFilePath)[env] : undefined

let config = require('./default')
merge(config, require('./defaultSSL'))
merge(config, debugConfig)
merge(config, packageConfig)
merge(config, fileConfig)
merge(config, require('./oldEnvironment'))
merge(config, require('./environment'))
merge(config, require('./dockerSecret'))

// load LDAP CA
if (config.ldap.tlsca) {
  let ca = config.ldap.tlsca.split(',')
  let caContent = []
  for (let i of ca) {
    if (fs.existsSync(i)) {
      caContent.push(fs.readFileSync(i, 'utf8'))
    }
  }
  let tlsOptions = {
    ca: caContent
  }
  config.ldap.tlsOptions = config.ldap.tlsOptions ? Object.assign(config.ldap.tlsOptions, tlsOptions) : tlsOptions
}

// Permission
config.permission = Permission
if (!config.allowanonymous) {
  delete config.permission.freely
}
if (!(config.defaultpermission in config.permission)) {
  config.defaultpermission = config.permission.editable
}

// cache result, cannot change config in runtime!!!
config.isStandardHTTPsPort = (function isStandardHTTPsPort () {
  return config.usessl && config.port === 443
})()
config.isStandardHTTPPort = (function isStandardHTTPPort () {
  return !config.usessl && config.port === 80
})()

// cache serverURL
config.serverurl = (function getserverurl () {
  var url = ''
  if (config.domain) {
    var protocol = config.protocolusessl ? 'https://' : 'http://'
    url = protocol + config.domain
    if (config.urladdport) {
      if (!config.isStandardHTTPPort || !config.isStandardHTTPsPort) {
        url += ':' + config.port
      }
    }
  }
  if (config.urlpath) {
    url += '/' + config.urlpath
  }
  return url
})()

config.Environment = Environment

// auth method
config.isFacebookEnable = config.facebook.clientID && config.facebook.clientSecret
config.isGoogleEnable = config.google.clientID && config.google.clientSecret
config.isDropboxEnable = config.dropbox.clientID && config.dropbox.clientSecret
config.isTwitterEnable = config.twitter.consumerKey && config.twitter.consumerSecret
config.isEmailEnable = config.email
config.isGitHubEnable = config.github.clientID && config.github.clientSecret
config.isGitLabEnable = config.gitlab.clientID && config.gitlab.clientSecret
config.isLDAPEnable = config.ldap.url

// generate correct path
config.sslcapath = path.join(appRootPath, config.sslcapath)
config.sslcertpath = path.join(appRootPath, config.sslcertpath)
config.sslkeypath = path.join(appRootPath, config.sslkeypath)
config.dhparampath = path.join(appRootPath, config.dhparampath)

config.tmppath = path.join(appRootPath, config.tmppath)
config.defaultnotepath = path.join(appRootPath, config.defaultnotepath)
config.docspath = path.join(appRootPath, config.docspath)
config.indexpath = path.join(appRootPath, config.indexpath)
config.hackmdpath = path.join(appRootPath, config.hackmdpath)
config.errorpath = path.join(appRootPath, config.errorpath)
config.prettypath = path.join(appRootPath, config.prettypath)
config.slidepath = path.join(appRootPath, config.slidepath)

// maek config readonly
config = deepFreeze(config)

module.exports = config
<|endoftext|>


<commit_before>/*
 * catberry-homepage
 *
 * Copyright (c) 2014 Julia Rechkunova and project contributors.
 *
 * catberry-homepage's license follows:
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * This license applies to all parts of catberry-homepage that are not externally
 * maintained libraries.
 */

'use strict';

var util = require('util');

module.exports = {
	/**
	 * Loads configuration by name and merge it with basic.
	 * @param {string} configuration Configuration name.
	 * @returns {Object} Merged configuration.
	 */
	load: function (configuration) {
		var baseConfig = require('../configs/basic.json'),
			environmentConfig = require('../configs/environment.json');

		return merge(baseConfig, environmentConfig);
	}
};

/**
 * Merges pair of objects into first.
 * @param {Object} obj1 First object.
 * @param {Object} obj2 Second object.
 * @returns {Object} Merged object.
 */
function merge(obj1, obj2) {
	if (typeof(obj1) !== 'object') {
		return obj2;
	}

	Object.keys(obj2)
		.forEach(function (key) {
			if (util.isArray(obj2[key]) &&
				util.isArray(obj1[key])) {
				obj1[key] = obj1[key].concat(obj2[key]);
			} else if (typeof(obj2[key]) === 'object' &&
				typeof(obj1[key]) === 'object') {
				obj1[key] = merge(obj1[key], obj2[key]);
			} else {
				obj1[key] = obj2[key];
			}
		});
	return obj1;
}<commit_msg>Remove unused parameter and fix jsDoc.<commit_after>/*
 * catberry-homepage
 *
 * Copyright (c) 2014 Julia Rechkunova and project contributors.
 *
 * catberry-homepage's license follows:
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * This license applies to all parts of catberry-homepage that are not externally
 * maintained libraries.
 */

'use strict';

var util = require('util');

module.exports = {
	/**
	 * Loads configuration and merge basic and
	 * environment-related configuration.
	 * @returns {Object} Merged configuration.
	 */
	load: function () {
		var baseConfig = require('../configs/basic.json'),
			environmentConfig = require('../configs/environment.json');

		return merge(baseConfig, environmentConfig);
	}
};

/**
 * Merges pair of objects into first.
 * @param {Object} obj1 First object.
 * @param {Object} obj2 Second object.
 * @returns {Object} Merged object.
 */
function merge(obj1, obj2) {
	if (typeof(obj1) !== 'object') {
		return obj2;
	}

	Object.keys(obj2)
		.forEach(function (key) {
			if (util.isArray(obj2[key]) &&
				util.isArray(obj1[key])) {
				obj1[key] = obj1[key].concat(obj2[key]);
			} else if (typeof(obj2[key]) === 'object' &&
				typeof(obj1[key]) === 'object') {
				obj1[key] = merge(obj1[key], obj2[key]);
			} else {
				obj1[key] = obj2[key];
			}
		});
	return obj1;
}<|endoftext|>


<commit_before>/* tagFiller
 * uses various 3rd party services to generate tags for images
 */

var MAX_FROM_ONE_RECOGNIZER = 6

var user = require('./util/user.js');
var tag = require('./util/tag.js');
var recognizer1 = require('./recognizer/clarifai/main.js');
var recognizer2 = require('./recognizer/moodstocks/main.js');

var recognizers = [ { name: "Clarifai", module: recognizer1 }, { name: "Moodstocks", module: recognizer2 } ]

Parse.Cloud.afterSave("Post", function(request) {
	// see if user has Smart Tags enabled
	var poster = request.object.get("createdBy");
	user.getUser(poster.id, function(user) {
		if (!request.object.existed() && user.get("smartTags")) {
      for (i = 0; i < recognizers.length; i++) {
        (function(index) {
          console.log("Requesting from: " + recognizers[index].name);
          recognizers[index].module.getTags(request.object.get("itemImage").url(), request.object.id, function(tags) {
            // only use the top tags
            var relation = request.object.relation("tags");
            console.log("Tags from " + recognizers[index].name + ": " + JSON.stringify(tags[0]));
            var count = 0;
            var maxtags = Math.min(MAX_FROM_ONE_RECOGNIZER, tags[0].classes.length);
            for (j = 0; j < maxtags; j++) {
              // make sure each tag is saved if it doesn't already exist
              tag.tagRead(tags[0].classes[j], function(tagName, tagInfo) {
                if (tagInfo == null || tagInfo.length == 0) {
                  tag.tagCreate(tagName, recognizers[index].name, request.user, function(tagInfo) {
                    relation.add(tagInfo);
                    if (++count >= maxtags)
                      request.object.save();
                  });
                }
                else {
                  relation.add(tagInfo);
                  if (++count >= maxtags)
                    request.object.save();
                }
              });
            }
          });
        })(i);
      }
		}
	});
});

Parse.Cloud.define("generateTags", function(request, response) {
	user.getObject("Post", request.params.photoId, function(result) {
		if (result) {
			var tagResults = [];
			var resultCount = 0;
			for (i = 0; i < recognizers.length; i++) {
				(function(index) {
				  console.log("Requesting from: " + recognizers[index].name);
				  recognizers[index].module.getTags(request.object.get("itemImage").url(), request.object.id, function(tags) {
					// only use the top tags
					console.log("Tags from " + recognizers[index].name + ": " + JSON.stringify(tags[0]));
					var maxtags = Math.min(MAX_FROM_ONE_RECOGNIZER, tags[0].classes.length);
					for (j = 0; j < maxtags; j++) {
						tagResults.append(tags[0].classes[j]);
					}
					if (++resultCount >= recognizers.length) {
						response.success(tagResults);
					}
				  });
				})(i);
			}
		}
		else
			response.error("Photo not found");
	});
}
<commit_msg>tag reading<commit_after>/* tagFiller
 * uses various 3rd party services to generate tags for images
 */

var MAX_FROM_ONE_RECOGNIZER = 6

var user = require('./util/user.js');
var tag = require('./util/tag.js');
var recognizer1 = require('./recognizer/clarifai/main.js');
var recognizer2 = require('./recognizer/moodstocks/main.js');

var recognizers = [ { name: "Clarifai", module: recognizer1 }, { name: "Moodstocks", module: recognizer2 } ]

Parse.Cloud.afterSave("Post", function(request) {
	// see if user has Smart Tags enabled
	var poster = request.object.get("createdBy");
	user.getUser(poster.id, function(user) {
		if (!request.object.existed() && user.get("smartTags")) {
      for (i = 0; i < recognizers.length; i++) {
        (function(index) {
          console.log("Requesting from: " + recognizers[index].name);
          recognizers[index].module.getTags(request.object.get("itemImage").url(), request.object.id, function(tags) {
            // only use the top tags
            var relation = request.object.relation("tags");
            console.log("Tags from " + recognizers[index].name + ": " + JSON.stringify(tags[0]));
            var count = 0;
            var maxtags = Math.min(MAX_FROM_ONE_RECOGNIZER, tags[0].classes.length);
            for (j = 0; j < maxtags; j++) {
              // make sure each tag is saved if it doesn't already exist
              tag.tagRead(tags[0].classes[j], function(tagName, tagInfo) {
                if (tagInfo == null || tagInfo.length == 0) {
                  tag.tagCreate(tagName, recognizers[index].name, request.user, function(tagInfo) {
                    relation.add(tagInfo);
                    if (++count >= maxtags)
                      request.object.save();
                  });
                }
                else {
                  relation.add(tagInfo);
                  if (++count >= maxtags)
                    request.object.save();
                }
              });
            }
          });
        })(i);
      }
		}
	});
});

Parse.Cloud.define("generateTags", function(request, response) {
	user.getObject("Post", request.params.photoId, function(result) {
		if (result) {
			var tagResults = [];
			var resultCount = 0;
			for (i = 0; i < recognizers.length; i++) {
				(function(index) {
				  console.log("Requesting from: " + recognizers[index].name);
				  recognizers[index].module.getTags(request.object.get("itemImage").url(), request.object.id, function(tags) {
					// only use the top tags
					console.log("Tags from " + recognizers[index].name + ": " + JSON.stringify(tags[0]));
					var maxtags = Math.min(MAX_FROM_ONE_RECOGNIZER, tags[0].classes.length);
					for (j = 0; j < maxtags; j++) {
						tagResults.append(tags[0].classes[j]);
					}
					if (++resultCount >= recognizers.length) {
						response.success(tagResults);
					}
				  });
				})(i);
			}
		}
		else
			response.error("Photo not found");
	});
});
<|endoftext|>


<commit_before>'use strict';
var U = require('lodash');
var Promise = require('bluebird');
var AWS = require('aws-sdk');
var errors = require('./errors');
var ThroughputExceededError = errors.get('ThroughputExceededError');
var NonExistentTableError = errors.get('NonExistentTableError');
var TableExistsError = errors.get('TableExistsError');
var ConditionFailedError = errors.get('ConditionFailedError');
var DynamoDBError = errors.get('DynamoDBError');
var NotFoundError = errors.get('NotFoundError');

function DynamoDB() {}

module.exports = DynamoDB;

U.extend(DynamoDB.prototype, {
	// spec.accessKeyId
	// spec.secretAccessKey
	// spec.region
	// spec.endpoint
	// spec.tablePrefix
	initialize: function (spec) {
		Object.defineProperties(this, {
			api: {
				enumerable: true,
				value: DynamoDB.createAWSDynamoDB(spec)
			},
			tablePrefix: {
				enumerable: true,
				value: spec.tablePrefix
			}
		});
		return this;
	},

	table: function (type) {
		return this.tablePrefix + '_' + type + '_entities';
	},

	relationTable: function () {
		return this.tablePrefix + '_relations';
	},

	createTable: function (params) {
		var api = this.api;
		return new Promise(function (resolve, reject) {
			var inFlight = false;

			var waitFor = {TableName: params.TableName};
			api.waitFor('tableExists', waitFor, function (err, res) {
				if (err) {
					return reject(new DynamoDBError(err));
				}
				if (inFlight) {
					resolve(res.Table);
				}
			});

			api.createTable(params, function (err) {
				inFlight = true;
				if (DynamoDB.isTableExistsException(err)) {
					reject(new TableExistsError(err.message));
				} else if (err) {
					reject(new DynamoDBError(err));
				}
			});
		});
	},

	getItem: function (params) {
		var api = this.api;
		return new Promise(function (resolve, reject) {
			api.getItem(params, function getItemCallback(err, res) {
				if (DynamoDB.isProvisionedThroughputExceededException(err)) {
					reject(new ThroughputExceededError(err.message));
				} else if (DynamoDB.isNonExistentTableException(err)) {
					reject(new NonExistentTableError(err.message));
				} else if (err) {
					reject(new DynamoDBError(err));
				} else if (res) {
					return resolve(res);
				}
				reject(new NotFoundError('Could not find entity'));
			});
		});
	},

	putItem: function (params) {
		var api = this.api;
		return new Promise(function (resolve, reject) {
			api.putItem(params, function putItemCallback(err) {
				if (DynamoDB.isProvisionedThroughputExceededException(err)) {
					reject(new ThroughputExceededError(err.message));
				} else if (DynamoDB.isNonExistentTableException(err)) {
					reject(new NonExistentTableError(err.message));
				} else if (DynamoDB.isConditionalFailedException(err)) {
					reject(new ConditionFailedError(err.message));
				} else if (err) {
					return reject(new DynamoDBError(err));
				}
				resolve(true);
			});
		});
	},

	deleteItem: function (params) {
		var api = this.api;
		return new Promise(function (resolve, reject) {
			api.deleteItem(params, function deleteItemCallback(err, res) {
				if (DynamoDB.isProvisionedThroughputExceededException(err)) {
					reject(new ThroughputExceededError(err.message));
				} else if (DynamoDB.isNonExistentTableException(err)) {
					reject(new NonExistentTableError(err.message));
				} else if (err) {
					reject(new DynamoDBError(err));
				} else if (res) {
					return resolve(res);
				}
				reject(new NotFoundError('Could not find entity'));
			});
		});
	},

	query: function (params) {
		var api = this.api;
		return new Promise(function (resolve, reject) {
			api.query(params, function getItemCallback(err, res) {
				if (DynamoDB.isProvisionedThroughputExceededException(err)) {
					reject(new ThroughputExceededError(err.message));
				} else if (DynamoDB.isNonExistentTableException(err)) {
					reject(new NonExistentTableError(err.message));
				} else if (err) {
					reject(new DynamoDBError(err));
				}
				resolve(res);
			});
		});
	}
});

// spec.accessKeyId
// spec.secretAccessKey
// spec.region
// spec.endpoint
// spec.tablePrefix
DynamoDB.create = function (spec) {
	var obj = new DynamoDB();
	return obj.initialize(spec);
};

// spec.accessKeyId
// spec.secretAccessKey
// spec.region
// spec.endpoint
DynamoDB.createAWSDynamoDB = function createAWSDynamoDB(options) {
	var awsOptions = {
		accessKeyId: options.accessKeyId,
		secretAccessKey: options.secretAccessKey,
		region: options.region,
		apiVersion: options.apiVersion || '2012-08-10'
	};
	if (options.endpoint) {
		awsOptions.endpoint = options.endpoint;
	}

	return new AWS.DynamoDB(awsOptions);
};

DynamoDB.serializeItem = function serializeItem(attrs) {
	return Object.keys(attrs).reduce(function (rec, key) {
		var val = DynamoDB.typeCast(attrs[key]);
		if (val) {
			rec[key] = val;
		}
		return rec;
	}, {});
};

DynamoDB.serializeRelation = function serializeRelation(args) {
	return DynamoDB.serializeItem({
		subject: args.subjectId,
		predicate: args.object.type,
		object: args.object
	});
};

DynamoDB.deserializeItem = function deserializeItem(obj) {
	return Object.keys(obj).reduce(function (rv, key) {
		var val = obj[key];
		if (val.hasOwnProperty('S')) {
			rv[key] = val.S.toString();
		} else if (val.hasOwnProperty('N')) {
			rv[key] = val.N;
		} else if (val.SS || val.NS) {
			rv[key] = val.SS || val.NS;
		} else if (val.hasOwnProperty('BOOL')) {
			rv[key] = Boolean(val.BOOL);
		} else if (val.hasOwnProperty('M')) {
			rv[key] = DynamoDB.deserializeItem(val.M);
		} else if (val.hasOwnProperty('NULL')) {
			rv[key] = null;
		}
		return rv;
	}, Object.create(null));
};

DynamoDB.typeCast = function typeCast(obj) {
	switch (typeof obj) {
		case 'string':
			if (obj.length === 0) {
				throw new TypeError('Cannot set empty string attributes on DynamoDB.');
			}
			return {S: obj};
		case 'number':
			if (isNaN(obj)) {
				throw new TypeError('Cannot set NaN as an attribute on DynamoDB.');
			}
			return {N: obj.toString()};
		case 'boolean':
			return {BOOL: obj};
		case 'function':
			throw new TypeError('Cannot set a function as an attribute on DynamoDB.');
		case 'undefined':
			break;
		default:
			if (!obj) {
				return {NULL: true};
			}
			return Array.isArray(obj) ?
				DynamoDB.typeCastArray(obj) :
				DynamoDB.typeCastObject(obj);
	}
};

DynamoDB.typeCastArray = function typeCastArray(obj) {
	if (!obj.length) {
		return {NULL: true};
	}

	var rv = {};
	var type = typeof obj[0];
	var key;

	if (type === 'string') {
		key = 'SS';
	} else if (type === 'number') {
		key = 'NS';
	} else {
		throw new TypeError('Only String or Number Lists (not ' + type +
												') may be defined in DynamoDB Engine.');
	}

	rv[key] = obj.map(function (val) {
		if (typeof val !== type) {
			throw new TypeError('All values in a String or Number List must be of ' +
													'the same type in DynamoDB Engine.');
		}
		return val;
	});

	return rv;
};

DynamoDB.typeCastObject = function typeCastObject(obj) {
	var keys = Object.keys(obj);
	var rv = {M: {}};
	if (!keys.length) {
		return rv;
	}

	rv.M = keys.reduce(function (M, key) {
		var type = typeof obj[key];
		var val = obj[key];
		switch (type) {
			case 'string':
				M[key] = {S: val};
				break;
			case 'number':
				M[key] = {N: val};
				break;
			case 'boolean':
				M[key] = {BOOL: val};
				break;
			default:
				throw new TypeError('Only String, Number or Boolean attributes (not ' +
					type + ') may be defined on Mixed Objects in DynamoDB Engine.');
		}
		return M;
	}, rv.M);

	return rv;
};

DynamoDB.serializeKey = function serializeKey(obj) {
	return Object.keys(obj).reduce(function (keys, key) {
		keys[key] = DynamoDB.typeCastKey(obj[key]);
		return keys;
	}, {});
};

DynamoDB.typeCastKey = function typeCastKey(obj) {
	var type = typeof obj;
	switch (type) {
		case 'string':
			return {S: obj};
		case 'number':
			return {N: obj};
		case 'boolean':
			return {B: obj};
		default:
			throw new TypeError('Only String, Number or Boolean attributes (not ' +
				type + ') may be defined on keys in DynamoDB Engine.');
	}
};

DynamoDB.deserializeKey = function deserializeKey(obj) {
	return Object.keys(obj).reduce(function (rv, key) {
		var val = obj[key];
		if (U.has(val, 'S')) {
			rv[key] = val.S.toString();
		} else if (U.has(val, 'N')) {
			rv[key] = val.N;
		} else if (U.has(val, 'B')) {
			rv[key] = Boolean(val.B);
		}
		return rv;
	}, Object.create(null));
};

DynamoDB.keySchema = function keySchema(keys, attrs) {
	keys = keys || Object.create(null);
	var keySchema = [];

	if (!keys.hash && !keys.range) {
		throw new Error('DynamoDB Engine expects keys.hash and/or keys.range');
	}

	if (keys.hash) {
		if (!U.isString(keys.hash)) {
			throw new Error('DynamoDB Engine expects keys.hash to be a String.');
		}
		if (!U.find(attrs, 'AttributeName', keys.hash)) {
			throw new Error('DynamoDB requires attribute names in hash key also ' +
				'be defined in attributes');
		}
		keySchema.push({
			AttributeName: keys.hash,
			KeyType: 'HASH'
		});
	}

	if (keys.range) {
		if (!U.isString(keys.range)) {
			throw new Error('DynamoDB Engine expects keys.range to be a String.');
		}
		if (!U.find(attrs, 'AttributeName', keys.range)) {
			throw new Error('DynamoDB requires attribute names in range key also ' +
				'be defined in attributes');
		}
		keySchema.push({
			AttributeName: keys.range,
			KeyType: 'RANGE'
		});
	}

	return keySchema;
};

DynamoDB.isResourceNotFoundException = function (err) {
	return err && err.code === 'ResourceNotFoundException';
};

DynamoDB.isConditionalFailedException = function (err) {
	return (err && err.code === 'ConditionalCheckFailedException');
};

DynamoDB.isProvisionedThroughputExceededException = function (err) {
	return err && err.code === 'ProvisionedThroughputExceededException';
};

DynamoDB.isTableExistsException = function (err) {
	return (err &&
		err.code === 'ResourceInUseException' &&
		/already\sexists/.test(err.message));
};

DynamoDB.isNonExistentTableException = function (err) {
	return (err &&
		err.code === 'ResourceNotFoundException' &&
		/non-existent\stable/.test(err.message));
};
<commit_msg>Adds updateTable to DynamoDB library.<commit_after>'use strict';
var U = require('lodash');
var Promise = require('bluebird');
var AWS = require('aws-sdk');
var errors = require('./errors');
var ThroughputExceededError = errors.get('ThroughputExceededError');
var NonExistentTableError = errors.get('NonExistentTableError');
var TableExistsError = errors.get('TableExistsError');
var ConditionFailedError = errors.get('ConditionFailedError');
var DynamoDBError = errors.get('DynamoDBError');
var NotFoundError = errors.get('NotFoundError');

function DynamoDB() {}

module.exports = DynamoDB;

U.extend(DynamoDB.prototype, {
	// spec.accessKeyId
	// spec.secretAccessKey
	// spec.region
	// spec.endpoint
	// spec.tablePrefix
	initialize: function (spec) {
		Object.defineProperties(this, {
			api: {
				enumerable: true,
				value: DynamoDB.createAWSDynamoDB(spec)
			},
			tablePrefix: {
				enumerable: true,
				value: spec.tablePrefix
			}
		});
		return this;
	},

	table: function (type) {
		return this.tablePrefix + '_' + type + '_entities';
	},

	relationTable: function () {
		return this.tablePrefix + '_relations';
	},

	hasManyIndex: function () {
		return this.tablePrefix + '_has_many';
	},

	belongsToIndex: function () {
		return this.tablePrefix + '_belongs_to';
	},

	describeTable: function (params) {
		var api = this.api;

		return new Promise(function (resolve, reject) {
			api.describeTable(params, function (err, res) {
				if (err) {
					return reject(new DynamoDBError(err));
				}
				resolve(res);
			});
		});
	},

	createTable: function (params) {
		var api = this.api;
		return new Promise(function (resolve, reject) {
			var inFlight = false;

			var waitFor = {TableName: params.TableName};
			api.waitFor('tableExists', waitFor, function (err, res) {
				if (err) {
					return reject(new DynamoDBError(err));
				}
				if (inFlight) {
					resolve(res);
				}
			});

			api.createTable(params, function (err) {
				inFlight = true;
				if (DynamoDB.isTableExistsException(err)) {
					reject(new TableExistsError(err.message));
				} else if (err) {
					reject(new DynamoDBError(err));
				}
			});
		});
	},

	updateTable: function (params) {
		var api = this.api;

		return new Promise(function (resolve, reject) {
			function poll() {
				api.describeTable({TableName: params.TableName}, function (err, res) {
					if (err) {
						return reject(new DynamoDBError(err));
					}
					if (res.Table.TableStatus !== 'ACTIVE') {
						setTimeout(poll, 4000);
						return null;
					}
					var indexes = res.Table.GlobalSecondaryIndexes;
					var i = 0;
					for (i; i < indexes.length; i += 1) {
						if (indexes[i].IndexStatus !== 'ACTIVE') {
							setTimeout(poll, 4000);
							return null;
						}
					}
					resolve(res.Table);
				});
			}

			api.updateTable(params, function (err) {
				if (err) {
					return reject(new DynamoDBError(err));
				}
				poll();
			});
		});
	},

	getItem: function (params) {
		var api = this.api;
		return new Promise(function (resolve, reject) {
			api.getItem(params, function getItemCallback(err, res) {
				if (DynamoDB.isProvisionedThroughputExceededException(err)) {
					reject(new ThroughputExceededError(err.message));
				} else if (DynamoDB.isNonExistentTableException(err)) {
					reject(new NonExistentTableError(err.message));
				} else if (err) {
					reject(new DynamoDBError(err));
				} else if (res) {
					return resolve(res);
				}
				reject(new NotFoundError('Could not find entity'));
			});
		});
	},

	putItem: function (params) {
		var api = this.api;
		return new Promise(function (resolve, reject) {
			api.putItem(params, function putItemCallback(err) {
				if (DynamoDB.isProvisionedThroughputExceededException(err)) {
					reject(new ThroughputExceededError(err.message));
				} else if (DynamoDB.isNonExistentTableException(err)) {
					reject(new NonExistentTableError(err.message));
				} else if (DynamoDB.isConditionalFailedException(err)) {
					reject(new ConditionFailedError(err.message));
				} else if (err) {
					return reject(new DynamoDBError(err));
				}
				resolve(true);
			});
		});
	},

	deleteItem: function (params) {
		var api = this.api;
		return new Promise(function (resolve, reject) {
			api.deleteItem(params, function deleteItemCallback(err, res) {
				if (DynamoDB.isProvisionedThroughputExceededException(err)) {
					reject(new ThroughputExceededError(err.message));
				} else if (DynamoDB.isNonExistentTableException(err)) {
					reject(new NonExistentTableError(err.message));
				} else if (err) {
					reject(new DynamoDBError(err));
				} else if (res) {
					return resolve(res);
				}
				reject(new NotFoundError('Could not find entity'));
			});
		});
	},

	query: function (params) {
		var api = this.api;
		return new Promise(function (resolve, reject) {
			api.query(params, function getItemCallback(err, res) {
				if (DynamoDB.isProvisionedThroughputExceededException(err)) {
					reject(new ThroughputExceededError(err.message));
				} else if (DynamoDB.isNonExistentTableException(err)) {
					reject(new NonExistentTableError(err.message));
				} else if (err) {
					reject(new DynamoDBError(err));
				}
				resolve(res);
			});
		});
	}
});

// spec.accessKeyId
// spec.secretAccessKey
// spec.region
// spec.endpoint
// spec.tablePrefix
DynamoDB.create = function (spec) {
	var obj = new DynamoDB();
	return obj.initialize(spec);
};

// spec.accessKeyId
// spec.secretAccessKey
// spec.region
// spec.endpoint
DynamoDB.createAWSDynamoDB = function createAWSDynamoDB(options) {
	var awsOptions = {
		accessKeyId: options.accessKeyId,
		secretAccessKey: options.secretAccessKey,
		region: options.region,
		apiVersion: options.apiVersion || '2012-08-10'
	};
	if (options.endpoint) {
		awsOptions.endpoint = options.endpoint;
	}

	return new AWS.DynamoDB(awsOptions);
};

DynamoDB.serializeItem = function serializeItem(attrs) {
	return Object.keys(attrs).reduce(function (rec, key) {
		var val = DynamoDB.typeCast(attrs[key]);
		if (val) {
			rec[key] = val;
		}
		return rec;
	}, {});
};

DynamoDB.serializeRelation = function serializeRelation(args) {
	return DynamoDB.serializeItem({
		subject: args.subjectId,
		predicate: args.object.type,
		object: args.object
	});
};

DynamoDB.deserializeItem = function deserializeItem(obj) {
	return Object.keys(obj).reduce(function (rv, key) {
		var val = obj[key];
		if (val.hasOwnProperty('S')) {
			rv[key] = val.S.toString();
		} else if (val.hasOwnProperty('N')) {
			rv[key] = val.N;
		} else if (val.SS || val.NS) {
			rv[key] = val.SS || val.NS;
		} else if (val.hasOwnProperty('BOOL')) {
			rv[key] = Boolean(val.BOOL);
		} else if (val.hasOwnProperty('M')) {
			rv[key] = DynamoDB.deserializeItem(val.M);
		} else if (val.hasOwnProperty('NULL')) {
			rv[key] = null;
		}
		return rv;
	}, Object.create(null));
};

DynamoDB.typeCast = function typeCast(obj) {
	switch (typeof obj) {
		case 'string':
			if (obj.length === 0) {
				throw new TypeError('Cannot set empty string attributes on DynamoDB.');
			}
			return {S: obj};
		case 'number':
			if (isNaN(obj)) {
				throw new TypeError('Cannot set NaN as an attribute on DynamoDB.');
			}
			return {N: obj.toString()};
		case 'boolean':
			return {BOOL: obj};
		case 'function':
			throw new TypeError('Cannot set a function as an attribute on DynamoDB.');
		case 'undefined':
			break;
		default:
			if (!obj) {
				return {NULL: true};
			}
			return Array.isArray(obj) ?
				DynamoDB.typeCastArray(obj) :
				DynamoDB.typeCastObject(obj);
	}
};

DynamoDB.typeCastArray = function typeCastArray(obj) {
	if (!obj.length) {
		return {NULL: true};
	}

	var rv = {};
	var type = typeof obj[0];
	var key;

	if (type === 'string') {
		key = 'SS';
	} else if (type === 'number') {
		key = 'NS';
	} else {
		throw new TypeError('Only String or Number Lists (not ' + type +
												') may be defined in DynamoDB Engine.');
	}

	rv[key] = obj.map(function (val) {
		if (typeof val !== type) {
			throw new TypeError('All values in a String or Number List must be of ' +
													'the same type in DynamoDB Engine.');
		}
		return val;
	});

	return rv;
};

DynamoDB.typeCastObject = function typeCastObject(obj) {
	var keys = Object.keys(obj);
	var rv = {M: {}};
	if (!keys.length) {
		return rv;
	}

	rv.M = keys.reduce(function (M, key) {
		var type = typeof obj[key];
		var val = obj[key];
		switch (type) {
			case 'string':
				M[key] = {S: val};
				break;
			case 'number':
				M[key] = {N: val};
				break;
			case 'boolean':
				M[key] = {BOOL: val};
				break;
			default:
				throw new TypeError('Only String, Number or Boolean attributes (not ' +
					type + ') may be defined on Mixed Objects in DynamoDB Engine.');
		}
		return M;
	}, rv.M);

	return rv;
};

DynamoDB.serializeKey = function serializeKey(obj) {
	return Object.keys(obj).reduce(function (keys, key) {
		keys[key] = DynamoDB.typeCastKey(obj[key]);
		return keys;
	}, {});
};

DynamoDB.typeCastKey = function typeCastKey(obj) {
	var type = typeof obj;
	switch (type) {
		case 'string':
			return {S: obj};
		case 'number':
			return {N: obj};
		case 'boolean':
			return {B: obj};
		default:
			throw new TypeError('Only String, Number or Boolean attributes (not ' +
				type + ') may be defined on keys in DynamoDB Engine.');
	}
};

DynamoDB.deserializeKey = function deserializeKey(obj) {
	return Object.keys(obj).reduce(function (rv, key) {
		var val = obj[key];
		if (U.has(val, 'S')) {
			rv[key] = val.S.toString();
		} else if (U.has(val, 'N')) {
			rv[key] = val.N;
		} else if (U.has(val, 'B')) {
			rv[key] = Boolean(val.B);
		}
		return rv;
	}, Object.create(null));
};

DynamoDB.keySchema = function keySchema(keys) {
	keys = keys || Object.create(null);
	var keySchema = [];

	keySchema.push({
		AttributeName: keys.hash,
		KeyType: 'HASH'
	});

	if (keys.range) {
		keySchema.push({
			AttributeName: keys.range,
			KeyType: 'RANGE'
		});
	}

	return keySchema;
};

DynamoDB.isResourceNotFoundException = function (err) {
	return err && err.code === 'ResourceNotFoundException';
};

DynamoDB.isConditionalFailedException = function (err) {
	return (err && err.code === 'ConditionalCheckFailedException');
};

DynamoDB.isProvisionedThroughputExceededException = function (err) {
	return err && err.code === 'ProvisionedThroughputExceededException';
};

DynamoDB.isTableExistsException = function (err) {
	return (err &&
		err.code === 'ResourceInUseException' &&
		/already\sexists/.test(err.message));
};

DynamoDB.isNonExistentTableException = function (err) {
	return (err &&
		err.code === 'ResourceNotFoundException' &&
		/non-existent\stable/.test(err.message));
};
<|endoftext|>