<commit_before>/*
 * Copyright (c) 2015, Yahoo Inc. All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

'use strict';

var Promise = global.Promise || require('promise');

var glob       = require('glob');
var Handlebars = require('handlebars');
var fs         = require('graceful-fs');
var path       = require('path');

var utils = require('./utils');

module.exports = ExpressHandlebars;

// -----------------------------------------------------------------------------

function ExpressHandlebars(config) {
    // Config properties with defaults.
    utils.assign(this, {
        handlebars     : Handlebars,
        extname        : '.handlebars',
        layoutsDir     : 'views/layouts/',
        partialsDir    : 'views/partials/',
        defaultLayout  : undefined,
        helpers        : undefined,
        compilerOptions: undefined,
    }, config);

    // Express view engine integration point.
    this.engine = this.renderView.bind(this);

    // Normalize `extname`.
    if (this.extname.charAt(0) !== '.') {
        this.extname = '.' + this.extname;
    }

    // Internal caches of compiled and precompiled templates.
    this.compiled    = Object.create(null);
    this.precompiled = Object.create(null);

    // Private internal file system cache.
    this._fsCache = Object.create(null);
}

ExpressHandlebars.prototype.getPartials = function (options) {
    var partialsDirs = Array.isArray(this.partialsDir) ?
            this.partialsDir : [this.partialsDir];

    partialsDirs = partialsDirs.map(function (dir) {
        var dirPath;
        var dirTemplates;
        var dirNamespace;

        // Support `partialsDir` collection with object entries that contain a
        // templates promise and a namespace.
        if (typeof dir === 'string') {
            dirPath = dir;
        } else if (typeof dir === 'object') {
            dirTemplates = dir.templates;
            dirNamespace = dir.namespace;
            dirPath      = dir.dir;
        }

        // We must have some path to templates, or templates themselves.
        if (!(dirPath || dirTemplates)) {
            throw new Error('A partials dir must be a string or config object');
        }

        // Make sure we're have a promise for the templates.
        var templatesPromise = dirTemplates ? Promise.resolve(dirTemplates) :
                this.getTemplates(dirPath, options);

        return templatesPromise.then(function (templates) {
            return {
                templates: templates,
                namespace: dirNamespace,
            };
        });
    }, this);

    return Promise.all(partialsDirs).then(function (dirs) {
        var getTemplateName = this._getTemplateName.bind(this);

        return dirs.reduce(function (partials, dir) {
            var templates = dir.templates;
            var namespace = dir.namespace;
            var filePaths = Object.keys(templates);

            filePaths.forEach(function (filePath) {
                var partialName       = getTemplateName(filePath, namespace);
                partials[partialName] = templates[filePath];
            });

            return partials;
        }, {});
    }.bind(this));
};

ExpressHandlebars.prototype.getTemplate = function (filePath, options) {
    filePath = path.resolve(filePath);
    options || (options = {});

    var precompiled = options.precompiled;
    var cache       = precompiled ? this.precompiled : this.compiled;
    var template    = options.cache && cache[filePath];

    if (template) {
        return template;
    }

    // Optimistically cache template promise to reduce file system I/O, but
    // remove from cache if there was a problem.
    template = cache[filePath] = this._getFile(filePath, {cache: options.cache})
        .then(function (file) {
            if (precompiled) {
                return this._precompileTemplate(file, this.compilerOptions);
            }

            return this._compileTemplate(file, this.compilerOptions);
        }.bind(this));

    return template.catch(function (err) {
        delete cache[filePath];
        throw err;
    });
};

ExpressHandlebars.prototype.getTemplates = function (dirPath, options) {
    options || (options = {});
    var cache = options.cache;

    return this._getDir(dirPath, {cache: cache}).then(function (filePaths) {
        var templates = filePaths.map(function (filePath) {
            return this.getTemplate(path.join(dirPath, filePath), options);
        }, this);

        return Promise.all(templates).then(function (templates) {
            return filePaths.reduce(function (hash, filePath, i) {
                hash[filePath] = templates[i];
                return hash;
            }, {});
        });
    }.bind(this));
};

ExpressHandlebars.prototype.render = function (filePath, context, options) {
    options || (options = {});

    return Promise.all([
        this.getTemplate(filePath, {cache: options.cache}),
        options.partials || this.getPartials({cache: options.cache}),
    ]).then(function (templates) {
        var template = templates[0];
        var partials = templates[1];
        var helpers  = options.helpers || this.helpers;

        // Add ExpressHandlebars metadata to the data channel so that it's
        // accessible within the templates and helpers, namespaced under:
        // `@exphbs.*`
        var data = utils.assign({}, options.data, {
            exphbs: utils.assign({}, options, {
                filePath: filePath,
                helpers : helpers,
                partials: partials,
            }),
        });

        return this._renderTemplate(template, context, {
            data    : data,
            helpers : helpers,
            partials: partials,
        });
    }.bind(this));
};

ExpressHandlebars.prototype.renderView = function (viewPath, options, callback) {
    options || (options = {});

    var context = options;

    // Express provides `settings.views` which is the path to the views dir that
    // the developer set on the Express app. When this value exists, it's used
    // to compute the view's name. Layouts and Partials directories are relative
    // to `settings.view` path
    var view;
    var viewsPath = options.settings && options.settings.views;
    if (viewsPath) {
        view = this._getTemplateName(path.relative(viewsPath, viewPath));
        this.partialsDir = path.join(viewsPath, 'partials/');
        this.layoutsDir = path.join(viewsPath, 'layouts/');
    }

    // Merge render-level and instance-level helpers together.
    var helpers = utils.assign({}, this.helpers, options.helpers);

    // Merge render-level and instance-level partials together.
    var partials = Promise.all([
        this.getPartials({cache: options.cache}),
        Promise.resolve(options.partials),
    ]).then(function (partials) {
        return utils.assign.apply(null, [{}].concat(partials));
    });

    // Pluck-out ExpressHandlebars-specific options and Handlebars-specific
    // rendering options.
    options = {
        cache : options.cache,
        view  : view,
        layout: 'layout' in options ? options.layout : this.defaultLayout,

        data    : options.data,
        helpers : helpers,
        partials: partials,
    };

    this.render(viewPath, context, options)
        .then(function (body) {
            var layoutPath = this._resolveLayoutPath(options.layout);

            if (layoutPath) {
                return this.render(
                    layoutPath,
                    utils.assign({}, context, {body: body}),
                    utils.assign({}, options, {layout: undefined})
                );
            }

            return body;
        }.bind(this))
        .then(utils.passValue(callback))
        .catch(utils.passError(callback));
};

// -- Protected Hooks ----------------------------------------------------------

ExpressHandlebars.prototype._compileTemplate = function (template, options) {
    return this.handlebars.compile(template, options);
};

ExpressHandlebars.prototype._precompileTemplate = function (template, options) {
    return this.handlebars.precompile(template, options);
};

ExpressHandlebars.prototype._renderTemplate = function (template, context, options) {
    return template(context, options);
};

// -- Private ------------------------------------------------------------------

ExpressHandlebars.prototype._getDir = function (dirPath, options) {
    dirPath = path.resolve(dirPath);
    options || (options = {});

    var cache = this._fsCache;
    var dir   = options.cache && cache[dirPath];

    if (dir) {
        return dir.then(function (dir) {
            return dir.concat();
        });
    }

    var pattern = '**/*' + this.extname;

    // Optimistically cache dir promise to reduce file system I/O, but remove
    // from cache if there was a problem.
    dir = cache[dirPath] = new Promise(function (resolve, reject) {
        glob(pattern, {
            cwd   : dirPath,
            follow: true
        }, function (err, dir) {
            if (err) {
                reject(err);
            } else {
                resolve(dir);
            }
        });
    });

    return dir.then(function (dir) {
        return dir.concat();
    }).catch(function (err) {
        delete cache[dirPath];
        throw err;
    });
};

ExpressHandlebars.prototype._getFile = function (filePath, options) {
    filePath = path.resolve(filePath);
    options || (options = {});

    var cache = this._fsCache;
    var file  = options.cache && cache[filePath];

    if (file) {
        return file;
    }

    // Optimistically cache file promise to reduce file system I/O, but remove
    // from cache if there was a problem.
    file = cache[filePath] = new Promise(function (resolve, reject) {
        fs.readFile(filePath, 'utf8', function (err, file) {
            if (err) {
                reject(err);
            } else {
                resolve(file);
            }
        });
    });

    return file.catch(function (err) {
        delete cache[filePath];
        throw err;
    });
};

ExpressHandlebars.prototype._getTemplateName = function (filePath, namespace) {
    var extRegex = new RegExp(this.extname + '$');
    var name     = filePath.replace(extRegex, '');

    if (namespace) {
        name = namespace + '/' + name;
    }

    return name;
};

ExpressHandlebars.prototype._resolveLayoutPath = function (layoutPath) {
    if (!layoutPath) {
        return null;
    }

    if (!path.extname(layoutPath)) {
        layoutPath += this.extname;
    }

    return path.resolve(this.layoutsDir, layoutPath);
};
<commit_msg>solves #160<commit_after>/*
 * Copyright (c) 2015, Yahoo Inc. All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

'use strict';

var Promise = global.Promise || require('promise');

var glob       = require('glob');
var Handlebars = require('handlebars');
var fs         = require('graceful-fs');
var path       = require('path');

var utils = require('./utils');

module.exports = ExpressHandlebars;

// -----------------------------------------------------------------------------

function ExpressHandlebars(config) {
    // Config properties with defaults.
    utils.assign(this, {
        handlebars     : Handlebars,
        extname        : '.handlebars',
        layoutsDir     : 'views/layouts/',
        partialsDir    : 'views/partials/',
        defaultLayout  : undefined,
        helpers        : undefined,
        compilerOptions: undefined,
    }, config);

    // Express view engine integration point.
    this.engine = this.renderView.bind(this);

    // Normalize `extname`.
    if (this.extname.charAt(0) !== '.') {
        this.extname = '.' + this.extname;
    }

    // Internal caches of compiled and precompiled templates.
    this.compiled    = Object.create(null);
    this.precompiled = Object.create(null);

    // Private internal file system cache.
    this._fsCache = Object.create(null);
}

ExpressHandlebars.prototype.getPartials = function (options) {
    var partialsDirs = Array.isArray(this.partialsDir) ?
            this.partialsDir : [this.partialsDir];

    partialsDirs = partialsDirs.map(function (dir) {
        var dirPath;
        var dirTemplates;
        var dirNamespace;

        // Support `partialsDir` collection with object entries that contain a
        // templates promise and a namespace.
        if (typeof dir === 'string') {
            dirPath = dir;
        } else if (typeof dir === 'object') {
            dirTemplates = dir.templates;
            dirNamespace = dir.namespace;
            dirPath      = dir.dir;
        }

        // We must have some path to templates, or templates themselves.
        if (!(dirPath || dirTemplates)) {
            throw new Error('A partials dir must be a string or config object');
        }

        // Make sure we're have a promise for the templates.
        var templatesPromise = dirTemplates ? Promise.resolve(dirTemplates) :
                this.getTemplates(dirPath, options);

        return templatesPromise.then(function (templates) {
            return {
                templates: templates,
                namespace: dirNamespace,
            };
        });
    }, this);

    return Promise.all(partialsDirs).then(function (dirs) {
        var getTemplateName = this._getTemplateName.bind(this);

        return dirs.reduce(function (partials, dir) {
            var templates = dir.templates;
            var namespace = dir.namespace;
            var filePaths = Object.keys(templates);

            filePaths.forEach(function (filePath) {
                var partialName       = getTemplateName(filePath, namespace);
                partials[partialName] = templates[filePath];
            });

            return partials;
        }, {});
    }.bind(this));
};

ExpressHandlebars.prototype.getTemplate = function (filePath, options) {
    filePath = path.resolve(filePath);
    options || (options = {});

    var precompiled = options.precompiled;
    var cache       = precompiled ? this.precompiled : this.compiled;
    var template    = options.cache && cache[filePath];

    if (template) {
        return template;
    }

    // Optimistically cache template promise to reduce file system I/O, but
    // remove from cache if there was a problem.
    template = cache[filePath] = this._getFile(filePath, {cache: options.cache})
        .then(function (file) {
            if (precompiled) {
                return this._precompileTemplate(file, this.compilerOptions);
            }

            return this._compileTemplate(file, this.compilerOptions);
        }.bind(this));

    return template.catch(function (err) {
        delete cache[filePath];
        throw err;
    });
};

ExpressHandlebars.prototype.getTemplates = function (dirPath, options) {
    options || (options = {});
    var cache = options.cache;

    return this._getDir(dirPath, {cache: cache}).then(function (filePaths) {
        var templates = filePaths.map(function (filePath) {
            return this.getTemplate(path.join(dirPath, filePath), options);
        }, this);

        return Promise.all(templates).then(function (templates) {
            return filePaths.reduce(function (hash, filePath, i) {
                hash[filePath] = templates[i];
                return hash;
            }, {});
        });
    }.bind(this));
};

ExpressHandlebars.prototype.render = function (filePath, context, options) {
    options || (options = {});

    return Promise.all([
        this.getTemplate(filePath, {cache: options.cache}),
        options.partials || this.getPartials({cache: options.cache}),
    ]).then(function (templates) {
        var template = templates[0];
        var partials = templates[1];
        var helpers  = options.helpers || this.helpers;

        // Add ExpressHandlebars metadata to the data channel so that it's
        // accessible within the templates and helpers, namespaced under:
        // `@exphbs.*`
        var data = utils.assign({}, options.data, {
            exphbs: utils.assign({}, options, {
                filePath: filePath,
                helpers : helpers,
                partials: partials,
            }),
        });

        return this._renderTemplate(template, context, {
            data    : data,
            helpers : helpers,
            partials: partials,
        });
    }.bind(this));
};

ExpressHandlebars.prototype.renderView = function (viewPath, options, callback) {
    options || (options = {});

    var context = options;

    // Express provides `settings.views` which is the path to the views dir that
    // the developer set on the Express app. When this value exists, it's used
    // to compute the view's name. Layouts and Partials directories are relative
    // to `settings.view` path
    var view;
    var viewsPath = options.settings && options.settings.views;
    if (viewsPath) {
        view = this._getTemplateName(path.relative(viewsPath, viewPath));
        this.partialsDir = path.join(viewsPath, 'partials/');
        this.layoutsDir = path.join(viewsPath, 'layouts/');
    }

    // Merge render-level and instance-level helpers together.
    var helpers = utils.assign({}, this.helpers, options.helpers);

    // Merge render-level and instance-level partials together.
    var partials = Promise.all([
        this.getPartials({cache: options.cache}),
        Promise.resolve(options.partials),
    ]).then(function (partials) {
        return utils.assign.apply(null, [{}].concat(partials));
    });

    // Pluck-out ExpressHandlebars-specific options and Handlebars-specific
    // rendering options.
    options = {
        cache : options.cache,
        view  : view,
        layout: 'layout' in options ? options.layout : this.defaultLayout,

        data    : options.data,
        helpers : helpers,
        partials: partials,
    };

    this.render(viewPath, context, options)
        .then(function (body) {
            var layoutPath = this._resolveLayoutPath(options.layout);

            if (layoutPath) {
                return this.render(
                    layoutPath,
                    utils.assign({}, context, {body: body}),
                    utils.assign({}, options, {layout: undefined})
                );
            }

            return body;
        }.bind(this))
        .then(utils.passValue(callback))
        .catch(utils.passError(callback));
};

// -- Protected Hooks ----------------------------------------------------------

ExpressHandlebars.prototype._compileTemplate = function (template, options) {
    return this.handlebars.compile(template.trim(), options);
};

ExpressHandlebars.prototype._precompileTemplate = function (template, options) {
    return this.handlebars.precompile(template, options);
};

ExpressHandlebars.prototype._renderTemplate = function (template, context, options) {
    return template(context, options).trim();
};

// -- Private ------------------------------------------------------------------

ExpressHandlebars.prototype._getDir = function (dirPath, options) {
    dirPath = path.resolve(dirPath);
    options || (options = {});

    var cache = this._fsCache;
    var dir   = options.cache && cache[dirPath];

    if (dir) {
        return dir.then(function (dir) {
            return dir.concat();
        });
    }

    var pattern = '**/*' + this.extname;

    // Optimistically cache dir promise to reduce file system I/O, but remove
    // from cache if there was a problem.
    dir = cache[dirPath] = new Promise(function (resolve, reject) {
        glob(pattern, {
            cwd   : dirPath,
            follow: true
        }, function (err, dir) {
            if (err) {
                reject(err);
            } else {
                resolve(dir);
            }
        });
    });

    return dir.then(function (dir) {
        return dir.concat();
    }).catch(function (err) {
        delete cache[dirPath];
        throw err;
    });
};

ExpressHandlebars.prototype._getFile = function (filePath, options) {
    filePath = path.resolve(filePath);
    options || (options = {});

    var cache = this._fsCache;
    var file  = options.cache && cache[filePath];

    if (file) {
        return file;
    }

    // Optimistically cache file promise to reduce file system I/O, but remove
    // from cache if there was a problem.
    file = cache[filePath] = new Promise(function (resolve, reject) {
        fs.readFile(filePath, 'utf8', function (err, file) {
            if (err) {
                reject(err);
            } else {
                resolve(file);
            }
        });
    });

    return file.catch(function (err) {
        delete cache[filePath];
        throw err;
    });
};

ExpressHandlebars.prototype._getTemplateName = function (filePath, namespace) {
    var extRegex = new RegExp(this.extname + '$');
    var name     = filePath.replace(extRegex, '');

    if (namespace) {
        name = namespace + '/' + name;
    }

    return name;
};

ExpressHandlebars.prototype._resolveLayoutPath = function (layoutPath) {
    if (!layoutPath) {
        return null;
    }

    if (!path.extname(layoutPath)) {
        layoutPath += this.extname;
    }

    return path.resolve(this.layoutsDir, layoutPath);
};
<|endoftext|>


<commit_before>import createLogger from '@xen-orchestra/log'
import XoCollection from 'xo-collection'
import XoUniqueIndex from 'xo-collection/unique-index'
import mixin from '@xen-orchestra/mixin'
import { createClient as createRedisClient } from 'redis'
import { EventEmitter } from 'events'
import { noSuchObject } from 'xo-common/api-errors'
import {
  forEach,
  includes,
  isEmpty,
  isFunction,
  isString,
  iteratee,
  map as mapToArray,
  stubTrue,
} from 'lodash'

import mixins from './xo-mixins'
import Connection from './connection'
import { generateToken, noop } from './utils'

// ===================================================================

const log = createLogger('xo:xo')

@mixin(mapToArray(mixins))
export default class Xo extends EventEmitter {
  constructor(config) {
    super()

    // a lot of mixins adds listener for start/stop/… events
    this.setMaxListeners(0)

    this._config = config

    this._objects = new XoCollection()
    this._objects.createIndex('byRef', new XoUniqueIndex('_xapiRef'))

    // Connections to users.
    this._nextConId = 0
    this._connections = { __proto__: null }

    this._httpRequestWatchers = { __proto__: null }

    // Connects to Redis.
    {
      const { renameCommands, socket: path, uri: url } = config.redis || {}

      this._redis = createRedisClient({
        path,
        rename_commands: renameCommands,
        url,
      })
    }

    this.on('start', () => this._watchObjects())
  }

  // -----------------------------------------------------------------

  // Returns an object from its key or UUID.
  getObject(key, type) {
    const {
      all,
      indexes: { byRef },
    } = this._objects

    const obj = all[key] || byRef[key]
    if (!obj) {
      throw noSuchObject(key, type)
    }

    if (
      type != null &&
      ((isString(type) && type !== obj.type) || !includes(type, obj.type)) // Array
    ) {
      throw noSuchObject(key, type)
    }

    return obj
  }

  getObjects({ filter, limit } = {}) {
    const { all } = this._objects

    if (filter === undefined) {
      if (limit === undefined || limit === Infinity) {
        return all
      }
      filter = stubTrue
    } else {
      filter = iteratee(filter)
      if (limit === undefined) {
        limit = Infinity
      }
    }

    const results = { __proto__: null }
    for (const id in all) {
      const object = all[id]
      if (filter(object, id, all)) {
        if (limit-- <= 0) {
          break
        }
        results[id] = object
      }
    }
    return results
  }

  // -----------------------------------------------------------------

  createUserConnection() {
    const { _connections: connections } = this

    const connection = new Connection()
    const id = (connection.id = this._nextConId++)

    connections[id] = connection
    connection.on('close', () => {
      delete connections[id]
    })

    return connection
  }

  // -----------------------------------------------------------------

  _handleHttpRequest(req, res, next) {
    const { url } = req

    const { _httpRequestWatchers: watchers } = this
    const watcher = watchers[url]
    if (!watcher) {
      next()
      return
    }
    if (!watcher.persistent) {
      delete watchers[url]
    }

    const { fn, data } = watcher
    new Promise(resolve => {
      resolve(fn.call(this, req, res, data, next))
    }).then(
      result => {
        if (result != null) {
          if (typeof result.pipe === 'function') {
            result.pipe(res)
          } else {
            res.end(JSON.stringify(result))
          }
        }
      },
      error => {
        log.error('HTTP request error', { error })

        if (!res.headersSent) {
          res.writeHead(500)
        }
        res.end('unknown error')
      }
    )
  }

  async registerHttpRequest(fn, data, { suffix = '' } = {}) {
    const { _httpRequestWatchers: watchers } = this

    const url = await (function generateUniqueUrl() {
      return generateToken().then(token => {
        const url = `/api/${token}${suffix}`

        return url in watchers ? generateUniqueUrl() : url
      })
    })()

    watchers[url] = {
      data,
      fn,
    }

    return url
  }

  async registerHttpRequestHandler(
    url,
    fn,
    { data = undefined, persistent = true } = {}
  ) {
    const { _httpRequestWatchers: watchers } = this

    if (url in watchers) {
      throw new Error(`a handler is already registered for ${url}`)
    }

    watchers[url] = {
      data,
      fn,
      persistent,
    }
  }

  async unregisterHttpRequestHandler(url) {
    delete this._httpRequestWatchers[url]
  }

  // -----------------------------------------------------------------

  // Plugins can use this method to expose methods directly on XO.
  defineProperty(name, value, thisArg = null) {
    if (name in this) {
      throw new Error(`Xo#${name} is already defined`)
    }

    // For security, prevent from accessing `this`.
    if (isFunction(value)) {
      value = (value =>
        function() {
          return value.apply(thisArg, arguments)
        })(value)
    }

    Object.defineProperty(this, name, {
      configurable: true,
      value,
    })

    let unset = () => {
      delete this[name]
      unset = noop
    }
    return () => unset()
  }

  // Convenience method to define multiple properties at once.
  defineProperties(props, thisArg) {
    const unsets = []
    const unset = () => forEach(unsets, unset => unset())

    try {
      forEach(props, (value, name) => {
        unsets.push(this.defineProperty(name, value, thisArg))
      })
    } catch (error) {
      unset()
      throw error
    }

    return unset
  }

  // -----------------------------------------------------------------

  // Watches objects changes.
  //
  // Some should be forwarded to connected clients.
  // Some should be persistently saved.
  _watchObjects() {
    const { _connections: connections, _objects: objects } = this

    let entered, exited
    function reset() {
      entered = { __proto__: null }
      exited = { __proto__: null }
    }
    reset()

    function onAdd(items) {
      forEach(items, (item, id) => {
        entered[id] = item
      })
    }
    objects.on('add', onAdd)
    objects.on('update', onAdd)

    objects.on('remove', items => {
      forEach(items, (_, id) => {
        // We don't care about the value here, so we choose `0`
        // because it is small in JSON.
        exited[id] = 0
      })
    })

    objects.on('finish', () => {
      const enteredMessage = !isEmpty(entered) && {
        type: 'enter',
        items: entered,
      }
      const exitedMessage = !isEmpty(exited) && {
        type: 'exit',
        items: exited,
      }

      if (!enteredMessage && !exitedMessage) {
        return
      }

      forEach(connections, connection => {
        // Notifies only authenticated clients.
        if (connection.has('user_id') && connection.notify) {
          if (enteredMessage) {
            connection.notify('all', enteredMessage)
          }
          if (exitedMessage) {
            connection.notify('all', exitedMessage)
          }
        }
      })

      reset()
    })
  }
}
<commit_msg>chore(xo-server/registerHttpRequest): simplify code<commit_after>import createLogger from '@xen-orchestra/log'
import XoCollection from 'xo-collection'
import XoUniqueIndex from 'xo-collection/unique-index'
import mixin from '@xen-orchestra/mixin'
import { createClient as createRedisClient } from 'redis'
import { EventEmitter } from 'events'
import { noSuchObject } from 'xo-common/api-errors'
import {
  forEach,
  includes,
  isEmpty,
  isFunction,
  isString,
  iteratee,
  map as mapToArray,
  stubTrue,
} from 'lodash'

import mixins from './xo-mixins'
import Connection from './connection'
import { generateToken, noop } from './utils'

// ===================================================================

const log = createLogger('xo:xo')

@mixin(mapToArray(mixins))
export default class Xo extends EventEmitter {
  constructor(config) {
    super()

    // a lot of mixins adds listener for start/stop/… events
    this.setMaxListeners(0)

    this._config = config

    this._objects = new XoCollection()
    this._objects.createIndex('byRef', new XoUniqueIndex('_xapiRef'))

    // Connections to users.
    this._nextConId = 0
    this._connections = { __proto__: null }

    this._httpRequestWatchers = { __proto__: null }

    // Connects to Redis.
    {
      const { renameCommands, socket: path, uri: url } = config.redis || {}

      this._redis = createRedisClient({
        path,
        rename_commands: renameCommands,
        url,
      })
    }

    this.on('start', () => this._watchObjects())
  }

  // -----------------------------------------------------------------

  // Returns an object from its key or UUID.
  getObject(key, type) {
    const {
      all,
      indexes: { byRef },
    } = this._objects

    const obj = all[key] || byRef[key]
    if (!obj) {
      throw noSuchObject(key, type)
    }

    if (
      type != null &&
      ((isString(type) && type !== obj.type) || !includes(type, obj.type)) // Array
    ) {
      throw noSuchObject(key, type)
    }

    return obj
  }

  getObjects({ filter, limit } = {}) {
    const { all } = this._objects

    if (filter === undefined) {
      if (limit === undefined || limit === Infinity) {
        return all
      }
      filter = stubTrue
    } else {
      filter = iteratee(filter)
      if (limit === undefined) {
        limit = Infinity
      }
    }

    const results = { __proto__: null }
    for (const id in all) {
      const object = all[id]
      if (filter(object, id, all)) {
        if (limit-- <= 0) {
          break
        }
        results[id] = object
      }
    }
    return results
  }

  // -----------------------------------------------------------------

  createUserConnection() {
    const { _connections: connections } = this

    const connection = new Connection()
    const id = (connection.id = this._nextConId++)

    connections[id] = connection
    connection.on('close', () => {
      delete connections[id]
    })

    return connection
  }

  // -----------------------------------------------------------------

  _handleHttpRequest(req, res, next) {
    const { url } = req

    const { _httpRequestWatchers: watchers } = this
    const watcher = watchers[url]
    if (!watcher) {
      next()
      return
    }
    if (!watcher.persistent) {
      delete watchers[url]
    }

    const { fn, data } = watcher
    new Promise(resolve => {
      resolve(fn.call(this, req, res, data, next))
    }).then(
      result => {
        if (result != null) {
          if (typeof result.pipe === 'function') {
            result.pipe(res)
          } else {
            res.end(JSON.stringify(result))
          }
        }
      },
      error => {
        log.error('HTTP request error', { error })

        if (!res.headersSent) {
          res.writeHead(500)
        }
        res.end('unknown error')
      }
    )
  }

  async registerHttpRequest(fn, data, { suffix = '' } = {}) {
    const { _httpRequestWatchers: watchers } = this
    let url

    do {
      url = `/api/${await generateToken()}${suffix}`
    } while (url in watchers)

    watchers[url] = {
      data,
      fn,
    }
    return url
  }

  async registerHttpRequestHandler(
    url,
    fn,
    { data = undefined, persistent = true } = {}
  ) {
    const { _httpRequestWatchers: watchers } = this

    if (url in watchers) {
      throw new Error(`a handler is already registered for ${url}`)
    }

    watchers[url] = {
      data,
      fn,
      persistent,
    }
  }

  async unregisterHttpRequestHandler(url) {
    delete this._httpRequestWatchers[url]
  }

  // -----------------------------------------------------------------

  // Plugins can use this method to expose methods directly on XO.
  defineProperty(name, value, thisArg = null) {
    if (name in this) {
      throw new Error(`Xo#${name} is already defined`)
    }

    // For security, prevent from accessing `this`.
    if (isFunction(value)) {
      value = (value =>
        function() {
          return value.apply(thisArg, arguments)
        })(value)
    }

    Object.defineProperty(this, name, {
      configurable: true,
      value,
    })

    let unset = () => {
      delete this[name]
      unset = noop
    }
    return () => unset()
  }

  // Convenience method to define multiple properties at once.
  defineProperties(props, thisArg) {
    const unsets = []
    const unset = () => forEach(unsets, unset => unset())

    try {
      forEach(props, (value, name) => {
        unsets.push(this.defineProperty(name, value, thisArg))
      })
    } catch (error) {
      unset()
      throw error
    }

    return unset
  }

  // -----------------------------------------------------------------

  // Watches objects changes.
  //
  // Some should be forwarded to connected clients.
  // Some should be persistently saved.
  _watchObjects() {
    const { _connections: connections, _objects: objects } = this

    let entered, exited
    function reset() {
      entered = { __proto__: null }
      exited = { __proto__: null }
    }
    reset()

    function onAdd(items) {
      forEach(items, (item, id) => {
        entered[id] = item
      })
    }
    objects.on('add', onAdd)
    objects.on('update', onAdd)

    objects.on('remove', items => {
      forEach(items, (_, id) => {
        // We don't care about the value here, so we choose `0`
        // because it is small in JSON.
        exited[id] = 0
      })
    })

    objects.on('finish', () => {
      const enteredMessage = !isEmpty(entered) && {
        type: 'enter',
        items: entered,
      }
      const exitedMessage = !isEmpty(exited) && {
        type: 'exit',
        items: exited,
      }

      if (!enteredMessage && !exitedMessage) {
        return
      }

      forEach(connections, connection => {
        // Notifies only authenticated clients.
        if (connection.has('user_id') && connection.notify) {
          if (enteredMessage) {
            connection.notify('all', enteredMessage)
          }
          if (exitedMessage) {
            connection.notify('all', exitedMessage)
          }
        }
      })

      reset()
    })
  }
}
<|endoftext|>


<commit_before>var gpio = require("gpio");

// pin 38 GPIO 20 - for trigger
let trig = gpio.export(38, {
   direction: "out",
   ready: function() {
   }
});

// pin 37 GPIO 26 - for echo
let echo = gpio.export(37, {
   direction: "in",
   ready: function() {
   }
});

function trigger() {
  this.trig.set(1);
  setTimeout(function off() {
    this.trig.set(0);
  }.bind(this), 10);
}

function getDistance() {

  console.log("getting distance inside proximity 1");
  var pulseStart = new Date();
  var pulseEnd = new Date();

  trig.set(function() {
    console.log("trig 1 value: ")
    console.log(trig.value);    // should log 1

    setTimeout(function off() {
      trig.reset();

      while(echo.value == 0) {
        pulseStart = new Date();
      }

      while(echo.value == 1) {
        pulseEnd = new Date();
      }

      let duration = pulseEnd.getTime() - pulseStart.getTime();
      let distance = duration * 17150;
      let centimeters = Math.round(distance * 100) / 100;
      console.log("centimeters: " + centimeters);

    }, 10);

  });

};

module.exports.getDistance = getDistance;
<commit_msg>Fixing proximity.js 2<commit_after>var gpio = require("gpio");

// pin 38 GPIO 20 - for trigger
let trig = gpio.export(38, {
   direction: "out",
   ready: function() {
   }
});

// pin 37 GPIO 26 - for echo
let echo = gpio.export(37, {
   direction: "in",
   ready: function() {
   }
});

function getDistance() {

  console.log("getting distance inside proximity 1");
  var pulseStart = new Date();
  var pulseEnd = new Date();

  this.trig.set(function() {
    console.log("trig 1 value: ")
    console.log(trig.value);    // should log 1

    setTimeout(function off() {
      this.trig.reset();

      while(this.echo.value == 0) {
        pulseStart = new Date();
      }

      while(this.echo.value == 1) {
        pulseEnd = new Date();
      }

      let duration = pulseEnd.getTime() - pulseStart.getTime();
      let distance = duration * 17150;
      let centimeters = Math.round(distance * 100) / 100;
      console.log("centimeters: " + centimeters);

    }, 10);

  });

};

module.exports.getDistance = getDistance;
<|endoftext|>


<commit_before>const moment = require('moment');
const RSS = require('rss');
const ejs = require('ejs');

const config = require('config/config.js');

const Campaign = require('models/Campaign.js');
const Action = require('models/Action.js');

const getContext = (action) => {

  const useTodaysDate = action.ongoing || !moment(action.start_time).isValid();
  const dateToUse = useTodaysDate ? moment() : moment(action.start_time);
  const pubDate = dateToUse.toISOString();
  const prettyDate = dateToUse.format('dddd, MMMM Do YYYY, h:mm a');
  const hasLocation = action.city || action.state;

  return {
    action,
    prettyDate,
    hasLocation,
    pubDate,
  };
}

module.exports = async (app) => {

  app.get('/api/rss/campaign/:slug', async (req, res, next) => {

    try {
      const slug = req.params.slug;
      const campaign = await Campaign.findOne({ slug });

      console.log(campaign.profile_image_url);
      const feed = new RSS({
        title: `${campaign.title} upcoming volunteer opportunities`,
        description: campaign.description,
        feed_url: `${config.urls.api}/api/rss/campaign/${slug}`,
        site_url: campaign.public_url,
        image_url: campaign.profile_image_url,
        pubDate: moment().toISOString(),
      });

      const actions = campaign.actions.filter(action => {
        return moment(action.end_time).isAfter(moment()) || action.ongoing
      });

      await Promise.all(actions.map( async (action) => {
        Object.assign(action, await Action.details(action, true));
        action.campaign_title = campaign.title;

        const context = getContext(action);

        ejs.renderFile(config.paths.base + '/views/action-description-rss.ejs', context, function (err, textBody) {
          if (err) {
            throw new Error(err);
          } else {
            feed.item({
              title: action.title,
              author: campaign.title,
              guid: action.id,
              description: textBody,
              url: action.public_url,
              pubDate: context.pubDate,
            });
          }
        });
      }));

      return res.send(feed.xml());

    } catch (e) {
      next(e);
    }
  });

  app.get('/api/rss/:search', async (req, res, next) => {

    try {
      const search = req.params.search;
      const actionSearch = await Action.search({
        keywords: [search],
        dates: {
          startDate: moment().format(),
          ongoing: true,
        },
      });

      const actions = actionSearch.actions;

      const feed = new RSS({
        title: `UpRise Volunteer Opportunities`,
        description: `Opportunities matching keyword: ${search}`,
        feed_url: `${config.urls.api}/api/rss/${search}`,
        site_url: `${config.urls.client}/${search}`,
        // TODO: Replace this with a relative link to an image we are hosting!
        image_url: 'https://upriseweb.files.wordpress.com/2017/06/cropped-headerlogowspace.png',
        pubDate: moment().format(),
      });

      await Promise.all(actions.map( async (action) => {
        Object.assign(action, await Action.details(action, false));

        const context = getContext(action);

        ejs.renderFile(config.paths.base + '/views/action-description-rss.ejs', context, function (err, textBody) {
          if (err) {
            throw new Error(err);
          } else {
            feed.item({
              title: `${action.campaign_title} - ${action.title}`,
              author: action.campaign_title,
              guid: action.id,
              description: textBody,
              url: action.public_url,
              pubDate: context.pubDate,
            });
          }
        });

      }));

      return res.send(feed.xml());

    } catch (e) {
      next(e);
    }
  });

}

<commit_msg>Better date handling in rss feed.<commit_after>const moment = require('moment-timezone');
const zipcodeToTimezone = require('zipcode-to-timezone');
const RSS = require('rss');
const ejs = require('ejs');

const config = require('config/config.js');

const Campaign = require('models/Campaign.js');
const Action = require('models/Action.js');

const getContext = (action) => {

  const timezone = (action.zipcode && zipcodeToTimezone.lookup(action.zipcode)) ? zipcodeToTimezone.lookup(action.zipcode) : 'America/New_York';

  const hasDate = !action.ongoing || moment(action.start_time).isValid();
  const date = moment(action.start_time).tz(timezone);
  const prettyDate = hasDate ? date.format('dddd, MMMM Do YYYY, h:mma z') : undefined;

  const hasLocation = action.city || action.state;

  return {
    action,
    date,
    prettyDate,
    hasLocation,
    hasDate,
  };
}

module.exports = async (app) => {

  app.get('/api/rss/campaign/:slug', async (req, res, next) => {

    try {
      const slug = req.params.slug;
      const campaign = await Campaign.findOne({ slug });

      const feed = new RSS({
        title: `${campaign.title} upcoming volunteer opportunities`,
        description: campaign.description,
        feed_url: `${config.urls.api}/api/rss/campaign/${slug}`,
        site_url: campaign.public_url,
        image_url: campaign.profile_image_url,
        pubDate: moment().toDate(),
      });

      const actions = campaign.actions.filter(action => {
        return moment(action.end_time).isAfter(moment()) || action.ongoing
      });

      await Promise.all(actions.map( async (action) => {
        Object.assign(action, await Action.details(action, true));
        action.campaign_title = campaign.title;

        const context = getContext(action);

        ejs.renderFile(config.paths.base + '/views/action-description-rss.ejs', context, function (err, textBody) {
          if (err) {
            throw new Error(err);
          } else {
            feed.item({
              title: action.title,
              author: campaign.title,
              guid: action.id,
              description: textBody,
              url: action.public_url,
              date: context.hasDate && context.date.toDate(),
            });
          }
        });
      }));

      return res.send(feed.xml());

    } catch (e) {
      next(e);
    }
  });

  app.get('/api/rss/:search', async (req, res, next) => {

    try {
      const search = req.params.search;
      const actionSearch = await Action.search({
        keywords: [search],
        dates: {
          startDate: moment().format(),
          ongoing: true,
        },
      });

      const actions = actionSearch.actions;

      const feed = new RSS({
        title: `UpRise Volunteer Opportunities`,
        description: `Opportunities matching keyword: ${search}`,
        feed_url: `${config.urls.api}/api/rss/${search}`,
        site_url: `${config.urls.client}/${search}`,
        // TODO: Replace this with a relative link to an image we are hosting!
        image_url: 'https://upriseweb.files.wordpress.com/2017/06/cropped-headerlogowspace.png',
        pubDate: moment().toDate(),
      });

      await Promise.all(actions.map( async (action) => {
        Object.assign(action, await Action.details(action, false));

        const context = getContext(action);

        ejs.renderFile(config.paths.base + '/views/action-description-rss.ejs', context, function (err, textBody) {
          if (err) {
            throw new Error(err);
          } else {
            feed.item({
              title: `${action.campaign_title} - ${action.title}`,
              author: action.campaign_title,
              guid: action.id,
              description: textBody,
              url: action.public_url,
              date: context.hasDate && context.date.toDate(),
            });
          }
        });

      }));

      return res.send(feed.xml());

    } catch (e) {
      next(e);
    }
  });

}

<|endoftext|>


<commit_before>let async = require('async');
//require("../utils/debug_util.js")(__filename, async);
let utils = require('../../utils/utils.js');

class ContractDeployer {
  constructor(options) {
    const self = this;

    this.logger = options.logger;
    this.events = options.events;
    this.plugins = options.plugins;

    self.events.setCommandHandler('deploy:contract', (contract, cb) => {
      self.checkAndDeployContract(contract, null, cb);
    });
  }

  // TODO: determining the arguments could also be in a module since it's not
  // part of ta 'normal' contract deployment
  determineArguments(suppliedArgs, contract, callback) {
    const self = this;

    let args = suppliedArgs;
    if (!Array.isArray(args)) {
      args = [];
      let abi = contract.abiDefinition.find((abi) => abi.type === 'constructor');

      for (let input of abi.inputs) {
        let inputValue = suppliedArgs[input.name];
        if (!inputValue) {
          this.logger.error(__("{{inputName}} has not been defined for {{className}} constructor", {inputName: input.name, className: contract.className}));
        }
        args.push(inputValue || "");
      }
    }

    async.map(args, (arg, nextEachCb) => {
      if (arg[0] === "$") {
        let contractName = arg.substr(1);
        self.events.request('contracts:contract', contractName, (referedContract) => {
          nextEachCb(null, referedContract.deployedAddress);
        });
      } else if (Array.isArray(arg)) {
        async.map(arg, (sub_arg, nextSubEachCb) => {
          if (sub_arg[0] === "$") {
            let contractName = sub_arg.substr(1);

            self.events.request('contracts:contract', contractName, (referedContract) => {
              nextSubEachCb(null, referedContract.deployedAddress);
            });
          } else {
            nextSubEachCb(null, sub_arg);
          }
        }, (err, subRealArgs) => {
          nextEachCb(null, subRealArgs);
        });
      } else {
        nextEachCb(null, arg);
      }
    }, callback);
  }

  checkAndDeployContract(contract, params, callback) {
    let self = this;
    contract.error = false;

    if (contract.deploy === false) {
      self.events.emit("deploy:contract:undeployed", contract);
      return callback();
    }

    async.waterfall([
      function requestBlockchainConnector(callback) {
        self.events.request("blockchain:object", (blockchain) => {
          self.blockchain = blockchain;
          callback();
        });
      },

      function _determineArguments(next) {
        self.determineArguments(params || contract.args, contract, (err, realArgs) => {
          if (err) {
            return next(err);
          }
          contract.realArgs = realArgs;
          next();
        });
      },
      function deployIt(next) {
        if (contract.address !== undefined) {
          try {
            utils.toChecksumAddress(contract.address);
          } catch(e) {
            self.logger.error(__("error deploying %s", contract.className));
            self.logger.error(e.message);
            contract.error = e.message;
            self.events.emit("deploy:contract:error", contract);
            return next(e.message);
          }
          contract.deployedAddress = contract.address;
          self.logger.info(contract.className.bold.cyan + __(" already deployed at ").green + contract.address.bold.cyan);
          self.events.emit("deploy:contract:deployed", contract);
          return next();
        }

        // TODO find a better way to do that
        if (process.env.isTest) {
          return self.deployContract(contract, next);
        }
        // TODO: this should be a plugin API instead, if not existing, it should by default deploy the contract
        self.events.request("deploy:contract:shouldDeploy", contract, function(trackedContract) {
          if (!trackedContract) {
            return self.deployContract(contract, next);
          }

          self.blockchain.getCode(trackedContract.address, function(_getCodeErr, codeInChain) {
            if (codeInChain !== "0x") {
              self.contractAlreadyDeployed(contract, trackedContract, next);
            } else {
              self.deployContract(contract, next);
            }
          });
        });
      }
    ], callback);
  }

  contractAlreadyDeployed(contract, trackedContract, callback) {
    const self = this;
    self.logger.info(contract.className.bold.cyan + __(" already deployed at ").green + trackedContract.address.bold.cyan);
    contract.deployedAddress = trackedContract.address;
    self.events.emit("deploy:contract:deployed", contract);

    // TODO: can be moved into a afterDeploy event
    // just need to figure out the gasLimit coupling issue
    self.events.request('code-generator:contract:vanilla', contract, contract._gasLimit, (contractCode) => {
      self.events.request('runcode:eval', contractCode);
      return callback();
    });
  }

  deployContract(contract, callback) {
    let self = this;
    let accounts = [];
    let contractParams = (contract.realArgs || contract.args).slice();
    let contractCode = contract.code;
    let deploymentAccount = self.blockchain.defaultAccount();
    let deployObject;

    async.waterfall([
      // TODO: can potentially go to a beforeDeploy plugin
      function getAccounts(next) {
        self.blockchain.getAccounts(function (err, _accounts) {
          if (err) {
            return next(new Error(err));
          }
          accounts = _accounts;

          // applying deployer account configuration, if any
          if (typeof contract.fromIndex === 'number') {
            deploymentAccount = accounts[contract.fromIndex];
            if (deploymentAccount === undefined) {
              return next(__("error deploying") + " " + contract.className + ": " + __("no account found at index") + " " + contract.fromIndex + __(" check the config"));
            }
          }
          if (typeof contract.from === 'string' && typeof contract.fromIndex !== 'undefined') {
            self.logger.warn(__('Both "from" and "fromIndex" are defined for contract') + ' "' + contract.className + '". ' + __('Using "from" as deployer account.'));
          }
          if (typeof contract.from === 'string') {
            deploymentAccount = contract.from;
          }

          deploymentAccount = deploymentAccount || accounts[0];
          next();
        });
      },
      function doLinking(next) {
        self.events.request('contracts:list', (_err, contracts) => {
          for (let contractObj of contracts) {
            let filename = contractObj.filename;
            let deployedAddress = contractObj.deployedAddress;
            if (deployedAddress) {
              deployedAddress = deployedAddress.substr(2);
            }
            let linkReference = '__' + filename + ":" + contractObj.className;
            if (contractCode.indexOf(linkReference.substr(0, 38)) < 0) { // substr to simulate the cut that solc does
              continue;
            }
            if (linkReference.length > 40) {
              return next(new Error(__("{{linkReference}} is too long, try reducing the path of the contract ({{filename}}) and/or its name {{contractName}}", {linkReference: linkReference, filename: filename, contractName: contractObj.className})));
            }
            let toReplace = linkReference + "_".repeat(40 - linkReference.length);
            if (deployedAddress === undefined) {
              let libraryName = contractObj.className;
              return next(new Error(__("{{contractName}} needs {{libraryName}} but an address was not found, did you deploy it or configured an address?", {contractName: contract.className, libraryName: libraryName})));
            }
            contractCode = contractCode.replace(new RegExp(toReplace, "g"), deployedAddress);
          }
          // saving code changes back to contract object
          contract.code = contractCode;
          next();
        });
      },
      function applyBeforeDeploy(next) {
        self.plugins.emitAndRunActionsForEvent('deploy:contract:beforeDeploy', {contract: contract}, next);
      },
      function getGasPriceForNetwork(next) {
        self.events.request("blockchain:gasPrice", (gasPrice) => {
          contract.gasPrice = contract.gasPrice || gasPrice;
          next();
        });
      },
      function createDeployObject(next) {
        let contractObject = self.blockchain.ContractObject({abi: contract.abiDefinition});

        try {
          const dataCode = contractCode.startsWith('0x') ? contractCode : "0x" + contractCode;
          deployObject = self.blockchain.deployContractObject(contractObject, {arguments: contractParams, data: dataCode});
        } catch(e) {
          if (e.message.indexOf('Invalid number of parameters for "undefined"') >= 0) {
            return next(new Error(__("attempted to deploy %s without specifying parameters", contract.className)));
          } else {
            return next(new Error(e));
          }
        }
        next();
      },
      function estimateCorrectGas(next) {
        if (contract.gas === 'auto') {
          return self.blockchain.estimateDeployContractGas(deployObject, (err, gasValue) => {
            if (err) {
              return next(err);
            }
            contract.gas = gasValue;
            next();
          });
        }
        next();
      },
      function deployTheContract(next) {
        let estimatedCost = contract.gas * contract.gasPrice;
        self.logger.info(__("deploying") + " " + contract.className.bold.cyan + " " + __("with").green + " " + contract.gas + " " + __("gas at the price of").green + " " + contract.gasPrice + " " + __("Wei, estimated cost:").green + " " + estimatedCost + " Wei".green);

        self.blockchain.deployContractFromObject(deployObject, {
          from: deploymentAccount,
          gas: contract.gas,
          gasPrice: contract.gasPrice
        }, function(error, receipt) {
          if (error) {
            contract.error = error.message;
            self.events.emit("deploy:contract:error", contract);
            return next(new Error("error deploying =" + contract.className + "= due to error: " + error.message));
          }
          self.logger.info(contract.className.bold.cyan + " " + __("deployed at").green + " " + receipt.contractAddress.bold.cyan + " " + __("using").green + " " + receipt.gasUsed + " " + __("gas").green);
          contract.deployedAddress = receipt.contractAddress;
          contract.transactionHash = receipt.transactionHash;
          receipt.className = contract.className;
          self.events.emit("deploy:contract:receipt", receipt);
          self.events.emit("deploy:contract:deployed", contract);

          // TODO: can be moved into a afterDeploy event
          // just need to figure out the gasLimit coupling issue
          self.events.request('code-generator:contract:vanilla', contract, contract._gasLimit, (contractCode) => {
            self.events.request('runcode:eval', contractCode);
            self.plugins.runActionsForEvent('deploy:contract:deployed', {contract: contract}, () => {
              return next(null, receipt);
            });
          });
        });
      }
    ], callback);
  }

}

module.exports = ContractDeployer;
<commit_msg>Enabling contract:deploy:beforeDeploy plugins to access deploymentAccount value by adding it into the contract object.<commit_after>let async = require('async');
//require("../utils/debug_util.js")(__filename, async);
let utils = require('../../utils/utils.js');

class ContractDeployer {
  constructor(options) {
    const self = this;

    this.logger = options.logger;
    this.events = options.events;
    this.plugins = options.plugins;

    self.events.setCommandHandler('deploy:contract', (contract, cb) => {
      self.checkAndDeployContract(contract, null, cb);
    });
  }

  // TODO: determining the arguments could also be in a module since it's not
  // part of ta 'normal' contract deployment
  determineArguments(suppliedArgs, contract, callback) {
    const self = this;

    let args = suppliedArgs;
    if (!Array.isArray(args)) {
      args = [];
      let abi = contract.abiDefinition.find((abi) => abi.type === 'constructor');

      for (let input of abi.inputs) {
        let inputValue = suppliedArgs[input.name];
        if (!inputValue) {
          this.logger.error(__("{{inputName}} has not been defined for {{className}} constructor", {inputName: input.name, className: contract.className}));
        }
        args.push(inputValue || "");
      }
    }

    async.map(args, (arg, nextEachCb) => {
      if (arg[0] === "$") {
        let contractName = arg.substr(1);
        self.events.request('contracts:contract', contractName, (referedContract) => {
          nextEachCb(null, referedContract.deployedAddress);
        });
      } else if (Array.isArray(arg)) {
        async.map(arg, (sub_arg, nextSubEachCb) => {
          if (sub_arg[0] === "$") {
            let contractName = sub_arg.substr(1);

            self.events.request('contracts:contract', contractName, (referedContract) => {
              nextSubEachCb(null, referedContract.deployedAddress);
            });
          } else {
            nextSubEachCb(null, sub_arg);
          }
        }, (err, subRealArgs) => {
          nextEachCb(null, subRealArgs);
        });
      } else {
        nextEachCb(null, arg);
      }
    }, callback);
  }

  checkAndDeployContract(contract, params, callback) {
    let self = this;
    contract.error = false;

    if (contract.deploy === false) {
      self.events.emit("deploy:contract:undeployed", contract);
      return callback();
    }

    async.waterfall([
      function requestBlockchainConnector(callback) {
        self.events.request("blockchain:object", (blockchain) => {
          self.blockchain = blockchain;
          callback();
        });
      },

      function _determineArguments(next) {
        self.determineArguments(params || contract.args, contract, (err, realArgs) => {
          if (err) {
            return next(err);
          }
          contract.realArgs = realArgs;
          next();
        });
      },
      function deployIt(next) {
        if (contract.address !== undefined) {
          try {
            utils.toChecksumAddress(contract.address);
          } catch(e) {
            self.logger.error(__("error deploying %s", contract.className));
            self.logger.error(e.message);
            contract.error = e.message;
            self.events.emit("deploy:contract:error", contract);
            return next(e.message);
          }
          contract.deployedAddress = contract.address;
          self.logger.info(contract.className.bold.cyan + __(" already deployed at ").green + contract.address.bold.cyan);
          self.events.emit("deploy:contract:deployed", contract);
          return next();
        }

        // TODO find a better way to do that
        if (process.env.isTest) {
          return self.deployContract(contract, next);
        }
        // TODO: this should be a plugin API instead, if not existing, it should by default deploy the contract
        self.events.request("deploy:contract:shouldDeploy", contract, function(trackedContract) {
          if (!trackedContract) {
            return self.deployContract(contract, next);
          }

          self.blockchain.getCode(trackedContract.address, function(_getCodeErr, codeInChain) {
            if (codeInChain !== "0x") {
              self.contractAlreadyDeployed(contract, trackedContract, next);
            } else {
              self.deployContract(contract, next);
            }
          });
        });
      }
    ], callback);
  }

  contractAlreadyDeployed(contract, trackedContract, callback) {
    const self = this;
    self.logger.info(contract.className.bold.cyan + __(" already deployed at ").green + trackedContract.address.bold.cyan);
    contract.deployedAddress = trackedContract.address;
    self.events.emit("deploy:contract:deployed", contract);

    // TODO: can be moved into a afterDeploy event
    // just need to figure out the gasLimit coupling issue
    self.events.request('code-generator:contract:vanilla', contract, contract._gasLimit, (contractCode) => {
      self.events.request('runcode:eval', contractCode);
      return callback();
    });
  }

  deployContract(contract, callback) {
    let self = this;
    let accounts = [];
    let contractParams = (contract.realArgs || contract.args).slice();
    let deploymentAccount = self.blockchain.defaultAccount();
    let deployObject;

    async.waterfall([
      // TODO: can potentially go to a beforeDeploy plugin
      function getAccounts(next) {
        self.blockchain.getAccounts(function (err, _accounts) {
          if (err) {
            return next(new Error(err));
          }
          accounts = _accounts;

          // applying deployer account configuration, if any
          if (typeof contract.fromIndex === 'number') {
            deploymentAccount = accounts[contract.fromIndex];
            if (deploymentAccount === undefined) {
              return next(__("error deploying") + " " + contract.className + ": " + __("no account found at index") + " " + contract.fromIndex + __(" check the config"));
            }
          }
          if (typeof contract.from === 'string' && typeof contract.fromIndex !== 'undefined') {
            self.logger.warn(__('Both "from" and "fromIndex" are defined for contract') + ' "' + contract.className + '". ' + __('Using "from" as deployer account.'));
          }
          if (typeof contract.from === 'string') {
            deploymentAccount = contract.from;
          }

          deploymentAccount = deploymentAccount || accounts[0];
          contract.deploymentAccount = deploymentAccount;
          next();
        });
      },
      function doLinking(next) {
        let contractCode = contract.code;
        self.events.request('contracts:list', (_err, contracts) => {
          for (let contractObj of contracts) {
            let filename = contractObj.filename;
            let deployedAddress = contractObj.deployedAddress;
            if (deployedAddress) {
              deployedAddress = deployedAddress.substr(2);
            }
            let linkReference = '__' + filename + ":" + contractObj.className;
            if (contractCode.indexOf(linkReference.substr(0, 38)) < 0) { // substr to simulate the cut that solc does
              continue;
            }
            if (linkReference.length > 40) {
              return next(new Error(__("{{linkReference}} is too long, try reducing the path of the contract ({{filename}}) and/or its name {{contractName}}", {linkReference: linkReference, filename: filename, contractName: contractObj.className})));
            }
            let toReplace = linkReference + "_".repeat(40 - linkReference.length);
            if (deployedAddress === undefined) {
              let libraryName = contractObj.className;
              return next(new Error(__("{{contractName}} needs {{libraryName}} but an address was not found, did you deploy it or configured an address?", {contractName: contract.className, libraryName: libraryName})));
            }
            contractCode = contractCode.replace(new RegExp(toReplace, "g"), deployedAddress);
          }
          // saving code changes back to the contract object
          contract.code = contractCode;
          next();
        });
      },
      function applyBeforeDeploy(next) {
        self.plugins.emitAndRunActionsForEvent('deploy:contract:beforeDeploy', {contract: contract}, next);
      },
      function getGasPriceForNetwork(next) {
        self.events.request("blockchain:gasPrice", (gasPrice) => {
          contract.gasPrice = contract.gasPrice || gasPrice;
          next();
        });
      },
      function createDeployObject(next) {
        let contractCode   = contract.code;
        let contractObject = self.blockchain.ContractObject({abi: contract.abiDefinition});

        try {
          const dataCode = contractCode.startsWith('0x') ? contractCode : "0x" + contractCode;
          deployObject = self.blockchain.deployContractObject(contractObject, {arguments: contractParams, data: dataCode});
        } catch(e) {
          if (e.message.indexOf('Invalid number of parameters for "undefined"') >= 0) {
            return next(new Error(__("attempted to deploy %s without specifying parameters", contract.className)));
          } else {
            return next(new Error(e));
          }
        }
        next();
      },
      function estimateCorrectGas(next) {
        if (contract.gas === 'auto') {
          return self.blockchain.estimateDeployContractGas(deployObject, (err, gasValue) => {
            if (err) {
              return next(err);
            }
            contract.gas = gasValue;
            next();
          });
        }
        next();
      },
      function deployTheContract(next) {
        let estimatedCost = contract.gas * contract.gasPrice;
        self.logger.info(__("deploying") + " " + contract.className.bold.cyan + " " + __("with").green + " " + contract.gas + " " + __("gas at the price of").green + " " + contract.gasPrice + " " + __("Wei, estimated cost:").green + " " + estimatedCost + " Wei".green);

        self.blockchain.deployContractFromObject(deployObject, {
          from: contract.deploymentAccount,
          gas: contract.gas,
          gasPrice: contract.gasPrice
        }, function(error, receipt) {
          if (error) {
            contract.error = error.message;
            self.events.emit("deploy:contract:error", contract);
            return next(new Error("error deploying =" + contract.className + "= due to error: " + error.message));
          }
          self.logger.info(contract.className.bold.cyan + " " + __("deployed at").green + " " + receipt.contractAddress.bold.cyan + " " + __("using").green + " " + receipt.gasUsed + " " + __("gas").green);
          contract.deployedAddress = receipt.contractAddress;
          contract.transactionHash = receipt.transactionHash;
          receipt.className = contract.className;
          self.events.emit("deploy:contract:receipt", receipt);
          self.events.emit("deploy:contract:deployed", contract);

          // TODO: can be moved into a afterDeploy event
          // just need to figure out the gasLimit coupling issue
          self.events.request('code-generator:contract:vanilla', contract, contract._gasLimit, (contractCode) => {
            self.events.request('runcode:eval', contractCode);
            self.plugins.runActionsForEvent('deploy:contract:deployed', {contract: contract}, () => {
              return next(null, receipt);
            });
          });
        });
      }
    ], callback);
  }

}

module.exports = ContractDeployer;
<|endoftext|>


<commit_before>import React from 'react';

import SPELLS from 'common/SPELLS';
import SpellLink from 'common/SpellLink';
import Analyzer from 'parser/core/Analyzer';
import SpellUsable from 'parser/shared/modules/SpellUsable';
import Abilities from 'parser/shared/modules/Abilities';

const ALLOWED_CASTS_DURING_DRW = [
  SPELLS.DEATH_STRIKE.id,
  SPELLS.HEART_STRIKE.id,
  SPELLS.BLOOD_BOIL.id,
  SPELLS.MARROWREND.id,
  SPELLS.CONSUMPTION_TALENT.id, // todo => test if new consumption talent actually works with DRW
];

class DancingRuneWeapon extends Analyzer {
  static dependencies = {
    spellUsable: SpellUsable,
    abilities: Abilities,
  };

  castsDuringDRW = [];

  on_byPlayer_cast(event) {
    if (!this.selectedCombatant.hasBuff(SPELLS.DANCING_RUNE_WEAPON_BUFF.id)) {
      return;
    }

    //push all casts during DRW that were on the GCD in array
    if (event.ability.guid !== SPELLS.RAISE_ALLY.id && //probably usefull to rezz someone even if it's a personal DPS-loss
      this.abilities.getAbility(event.ability.guid) !== undefined &&
      this.abilities.getAbility(event.ability.guid).gcd) {
      this.castsDuringDRW.push(event.ability.guid);
    }
  }

  on_byPlayer_applybuff(event) {
    // start DRWs cooldown when used pre-pull
    if (event.ability.guid === SPELLS.DANCING_RUNE_WEAPON_BUFF.id && event.prepull) {
      this.spellUsable.beginCooldown(SPELLS.DANCING_RUNE_WEAPON.id, this.owner.fight.start_time);
    }
  }

  get goodDRWCasts() {
    return this.castsDuringDRW.filter((val, index) => {
      return ALLOWED_CASTS_DURING_DRW.includes(val);
    });
  }

  get SuggestionThresholds() {
    return {
      actual: this.goodDRWCasts.length / this.castsDuringDRW.length,
      isLessThan: {
        minor: 1,
        average: 0.9,
        major: .8,
      },
      style: 'percentage',
    };
  }

  spellLinks(id, index) {
    if (id === SPELLS.CONSUMPTION_TALENT.id) {
      return and (if in AoE)<SpellLink id={id} />;
    } else if (index + 2 === ALLOWED_CASTS_DURING_DRW.length) {
      return <SpellLink id={id} /> ;
    } else {
      return <SpellLink id={id} />, ;
    }
  }

  get goodDRWSpells() {
    return (

        Try and prioritize {ALLOWED_CASTS_DURING_DRW.map((id, index) => this.spellLinks(id, index))}

    );
  }

  suggestions(when) {
    when(this.SuggestionThresholds)
      .addSuggestion((suggest, actual, recommended) => {
        return suggest(<>Avoid casting spells during <SpellLink id={SPELLS.DANCING_RUNE_WEAPON.id} /> that don't benefit from the coppies such as <SpellLink id={SPELLS.BLOODDRINKER_TALENT.id} /> and <SpellLink id={SPELLS.DEATH_AND_DECAY.id} />. Check the cooldown-tab below for more detailed breakdown.{this.goodDRWSpells}</>)
          .icon(SPELLS.DANCING_RUNE_WEAPON.icon)
          .actual(`${ this.goodDRWCasts.length } out of ${ this.castsDuringDRW.length} casts during DRW were good`)
          .recommended(`${this.castsDuringDRW.length} recommended`);
      });
  }
}

export default DancingRuneWeapon;
<commit_msg>BDK, remove manual DRW cooldown<commit_after>import React from 'react';

import SPELLS from 'common/SPELLS';
import SpellLink from 'common/SpellLink';
import Analyzer from 'parser/core/Analyzer';
import SpellUsable from 'parser/shared/modules/SpellUsable';
import Abilities from 'parser/shared/modules/Abilities';

const ALLOWED_CASTS_DURING_DRW = [
  SPELLS.DEATH_STRIKE.id,
  SPELLS.HEART_STRIKE.id,
  SPELLS.BLOOD_BOIL.id,
  SPELLS.MARROWREND.id,
  SPELLS.CONSUMPTION_TALENT.id, // todo => test if new consumption talent actually works with DRW
];

class DancingRuneWeapon extends Analyzer {
  static dependencies = {
    spellUsable: SpellUsable,
    abilities: Abilities,
  };

  castsDuringDRW = [];

  on_byPlayer_cast(event) {
    if (!this.selectedCombatant.hasBuff(SPELLS.DANCING_RUNE_WEAPON_BUFF.id)) {
      return;
    }

    //push all casts during DRW that were on the GCD in array
    if (event.ability.guid !== SPELLS.RAISE_ALLY.id && //probably usefull to rezz someone even if it's a personal DPS-loss
      this.abilities.getAbility(event.ability.guid) !== undefined &&
      this.abilities.getAbility(event.ability.guid).gcd) {
      this.castsDuringDRW.push(event.ability.guid);
    }
  }

  get goodDRWCasts() {
    return this.castsDuringDRW.filter((val, index) => {
      return ALLOWED_CASTS_DURING_DRW.includes(val);
    });
  }

  get SuggestionThresholds() {
    return {
      actual: this.goodDRWCasts.length / this.castsDuringDRW.length,
      isLessThan: {
        minor: 1,
        average: 0.9,
        major: .8,
      },
      style: 'percentage',
    };
  }

  spellLinks(id, index) {
    if (id === SPELLS.CONSUMPTION_TALENT.id) {
      return and (if in AoE)<SpellLink id={id} />;
    } else if (index + 2 === ALLOWED_CASTS_DURING_DRW.length) {
      return <SpellLink id={id} /> ;
    } else {
      return <SpellLink id={id} />, ;
    }
  }

  get goodDRWSpells() {
    return (

        Try and prioritize {ALLOWED_CASTS_DURING_DRW.map((id, index) => this.spellLinks(id, index))}

    );
  }

  suggestions(when) {
    when(this.SuggestionThresholds)
      .addSuggestion((suggest, actual, recommended) => {
        return suggest(<>Avoid casting spells during <SpellLink id={SPELLS.DANCING_RUNE_WEAPON.id} /> that don't benefit from the coppies such as <SpellLink id={SPELLS.BLOODDRINKER_TALENT.id} /> and <SpellLink id={SPELLS.DEATH_AND_DECAY.id} />. Check the cooldown-tab below for more detailed breakdown.{this.goodDRWSpells}</>)
          .icon(SPELLS.DANCING_RUNE_WEAPON.icon)
          .actual(`${ this.goodDRWCasts.length } out of ${ this.castsDuringDRW.length} casts during DRW were good`)
          .recommended(`${this.castsDuringDRW.length} recommended`);
      });
  }
}

export default DancingRuneWeapon;
<|endoftext|>


<commit_before>var cluster = require('cluster');
var pigato = require('pigato');
var async = require('async');
var _ = require('lodash');
var cmd = require('commander');

cmd
.option('--bn <val>', 'Num of Brokers', 1)
.option('--wn <val>', 'Num of Workers (for each Broker)', 1)
.option('--cn <val>', 'Num of Clients', 1)
.option('--m <val>', 'Use memory cache (1=enabled|0=disabled) (default=0)', 0)
.option('--p <val>', 'Num of messages (for each Client)', 100000);

cmd.on('--help', function() {
  console.log('Examples:');
  console.log('\tnode ' + cmd.name() + ' --bn 4 --wn 2 --cn 2 --p 50000');
});

cmd.parse(process.argv);

_.each(['bn', 'wn', 'cn', 'p', 'm'], function(k) {
  cmd[k] = +cmd[k];
});

var chunk = 'foo';

if (cluster.isMaster) {
  for (var i = 0; i < (cmd.bn + (cmd.bn * cmd.wn) + cmd.cn); i++) {
    cluster.fork();
  }

  var kills = 0;
  cluster.on('exit', function(worker, code, signal) {
    kills++;
    if (kills === cmd.cn) {
      for (var id in cluster.workers) {
        cluster.workers[id].kill();
      }
    }
  });
} else {

  var workerID = cluster.worker.workerID || cluster.worker.id;

  if (workerID <= cmd.bn) {
    var broker = new pigato.Broker('tcp://*:5555' + workerID);
    broker.start(function() {
      console.log("BROKER " + workerID);
    });

  } else if (workerID <= cmd.bn + (cmd.bn * cmd.wn)) {
    var b = (workerID % cmd.bn) + 1;
    var worker = new pigato.Worker('tcp://127.0.0.1:5555' + b, 'echo');
    worker.on('request', function(inp, res) {
      if (cmd.m) {
        res.opts.cache = 50000;
      }
      res.end(inp + 'FINAL');
    });
    worker.start();
    console.log("WORKER (BROKER " + b + ")");

  } else {
    var bs = [];

    for (var i = 0; i < cmd.bn; i++) {
      bs.push('tcp://127.0.0.1:5555' + (i + 1));
    }

    var client = new pigato.Client(bs);
    client.start();
    console.log("CLIENT (" + cmd.p + " reqs)");

    var timer;
    var rcnt = 0;

    function done() {
      var elapsed = process.hrtime(timer);
      var dts = elapsed[0] + (elapsed[1] / 1000000000);
      console.log("CLIENT GOT answer", dts + " milliseconds. " + (cmd.p / dts).toFixed(2) + " requests/sec.");
      client.stop();
      process.exit(-1);
    }

    function send() {
      for (var k = 0; k < cmd.p; k++) {
        client.request(
          'echo', chunk + (k % 1000),
          { timeout: -1 }
        )
        .on('data', function() {})
        .on('end', function() {
          rcnt++;

          if (rcnt < cmd.p) {
            return;
          }

          done();
        });
      }
    }

    setTimeout(function() {
      timer = process.hrtime();
      send();
    }, 1000);
  }
}
<commit_msg>Minor improvements<commit_after>var cluster = require('cluster');
var pigato = require('pigato');
var async = require('async');
var _ = require('lodash');
var cmd = require('commander');

cmd
.option('--bn <val>', 'Num of Brokers', 1)
.option('--wn <val>', 'Num of Workers (for each Broker)', 1)
.option('--cn <val>', 'Num of Clients', 1)
.option('--p <val>', 'Num of messages (for each Client)', 100000)
.option('--m <val>', 'Use memory cache (1=enabled|0=disabled) (default=0)', 0);

cmd.on('--help', function() {
  console.log('Examples:');
  console.log('\tnode ' + cmd.name() + ' --bn 4 --wn 2 --cn 2 --p 50000');
});

cmd.parse(process.argv);

_.each(['bn', 'wn', 'cn', 'p', 'm'], function(k) {
  cmd[k] = +cmd[k];
});

var chunk = 'foo';

if (cluster.isMaster) {
  console.log("RUNNING CONF");
  console.log("\t", [
    cmd.bn + " brokers",
    cmd.wn + " workers",
    cmd.cn + " clients",
    "cache " + (cmd.m ? 'on' : 'off'),
    cmd.p + " requests"
  ].join(", "));
  for (var i = 0; i < (cmd.bn + (cmd.bn * cmd.wn) + cmd.cn); i++) {
    cluster.fork();
  }

  var kills = 0;
  cluster.on('exit', function(worker, code, signal) {
    kills++;
    if (kills === cmd.cn) {
      for (var id in cluster.workers) {
        cluster.workers[id].kill();
      }
    }
  });
} else {

  var workerID = cluster.worker.workerID || cluster.worker.id;

  if (workerID <= cmd.bn) {
    var broker = new pigato.Broker('tcp://*:5555' + workerID);
    broker.start(function() {
      console.log("BROKER " + workerID);
    });

  } else if (workerID <= cmd.bn + (cmd.bn * cmd.wn)) {
    var b = (workerID % cmd.bn) + 1;
    var worker = new pigato.Worker('tcp://127.0.0.1:5555' + b, 'echo');
    worker.on('request', function(inp, res) {
      if (cmd.m) {
        res.opts.cache = 50000;
      }
      res.end(inp + 'FINAL');
    });
    worker.start();
    console.log("WORKER (BROKER " + b + ")");

  } else {
    var bs = [];

    for (var i = 0; i < cmd.bn; i++) {
      bs.push('tcp://127.0.0.1:5555' + (i + 1));
    }

    var client = new pigato.Client(bs);
    client.start();
    console.log("CLIENT (" + cmd.p + " reqs)");

    var timer;
    var rcnt = 0;

    function done() {
      var elapsed = process.hrtime(timer);
      var dts = elapsed[0] + (elapsed[1] / 1000000000);
      console.log("CLIENT GOT answer", dts + " milliseconds. " + (cmd.p / dts).toFixed(2) + " requests/sec.");
      client.stop();
      process.exit(-1);
    }

    function send() {
      for (var k = 0; k < cmd.p; k++) {
        client.request(
          'echo', chunk + (k % 1000),
          { timeout: -1 }
        )
        .on('data', function() {})
        .on('end', function() {
          rcnt++;

          if (rcnt < cmd.p) {
            return;
          }

          done();
        });
      }
    }

    setTimeout(function() {
      timer = process.hrtime();
      send();
    }, 1000);
  }
}
<|endoftext|>


<commit_before>/*
Copyright 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/* joining.js: tests for the various paths when joining a room */

require('skin-sdk');

var jssdk = require('matrix-js-sdk');

var sdk = require('matrix-react-sdk');
var peg = require('matrix-react-sdk/lib/MatrixClientPeg');
var dis = require('matrix-react-sdk/lib/dispatcher');
var MatrixChat = sdk.getComponent('structures.MatrixChat');
var RoomDirectory = sdk.getComponent('structures.RoomDirectory');
var RoomPreviewBar = sdk.getComponent('rooms.RoomPreviewBar');
var RoomView = sdk.getComponent('structures.RoomView');

var React = require('react');
var ReactDOM = require('react-dom');
var ReactTestUtils = require('react-addons-test-utils');
var expect = require('expect');
var q = require('q');

var test_utils = require('../test-utils');
var MockHttpBackend = require('../mock-request');

var HS_URL='http://localhost';
var IS_URL='http://localhost';
var USER_ID='@me:localhost';
var ACCESS_TOKEN='access_token';

describe('joining a room', function () {
    describe('over federation', function () {
        var parentDiv;
        var httpBackend;
        var matrixChat;

        beforeEach(function() {
            test_utils.beforeEach(this);
            httpBackend = new MockHttpBackend();
            jssdk.request(httpBackend.requestFn);
            parentDiv = document.createElement('div');

            // uncomment this to actually add the div to the UI, to help with
            // debugging (but slow things down)
            // document.body.appendChild(parentDiv);
        });

        afterEach(function() {
            if (parentDiv) {
                ReactDOM.unmountComponentAtNode(parentDiv);
                parentDiv.remove();
                parentDiv = null;
            }
        });

        it('should not get stuck at a spinner', function(done) {
            var ROOM_ALIAS = '#alias:localhost';
            var ROOM_ID = '!id:localhost';

            httpBackend.when('PUT', '/presence/'+encodeURIComponent(USER_ID)+'/status')
                .respond(200, {});
            httpBackend.when('GET', '/pushrules').respond(200, {});
            httpBackend.when('POST', '/filter').respond(200, { filter_id: 'fid' });
            httpBackend.when('GET', '/sync').respond(200, {});
            httpBackend.when('GET', '/publicRooms').respond(200, {chunk: []});
            httpBackend.when('GET', '/directory/room/'+encodeURIComponent(ROOM_ALIAS)).respond(200, { room_id: ROOM_ID });

            // start with a logged-in client
            peg.replaceUsingCreds({
                homeserverUrl: HS_URL,
                identityServerUrl: IS_URL,
                userId: USER_ID,
                accessToken: ACCESS_TOKEN,
                guest: false,
            });

            var mc = <MatrixChat config={{}}/>;
            matrixChat = ReactDOM.render(mc, parentDiv);

            // switch to the Directory
            dis.dispatch({
                action: 'view_room_directory',
            });

            var roomView;
            httpBackend.flush().then(() => {
                var roomDir = ReactTestUtils.findRenderedComponentWithType(
                    matrixChat, RoomDirectory);

                // enter an alias in the input, and simulate enter
                var input = ReactTestUtils.findRenderedDOMComponentWithTag(
                    roomDir, 'input');
                input.value = ROOM_ALIAS;
                ReactTestUtils.Simulate.keyUp(input, {key: 'Enter'});

                // that should create a roomview which will start a peek; wait
                // for the peek.
                httpBackend.when('GET', '/rooms/'+encodeURIComponent(ROOM_ID)+"/initialSync")
                    .respond(401, {errcode: 'M_GUEST_ACCESS_FORBIDDEN'});
                return httpBackend.flush();
            }).then(() => {
                httpBackend.verifyNoOutstandingExpectation();

                // we should now have a roomview, with a preview bar
                roomView = ReactTestUtils.findRenderedComponentWithType(
                    matrixChat, RoomView);

                var previewBar = ReactTestUtils.findRenderedComponentWithType(
                    roomView, RoomPreviewBar);

                var joinLink = ReactTestUtils.findRenderedDOMComponentWithTag(
                    previewBar, 'a');

                ReactTestUtils.Simulate.click(joinLink);

                // that will fire off a request to check our displayname, followed by a
                // join request
                httpBackend.when('GET', '/profile/'+encodeURIComponent(USER_ID))
                    .respond(200, {displayname: 'boris'});
                httpBackend.when('POST', '/join/'+encodeURIComponent(ROOM_ALIAS))
                    .respond(200, {room_id: ROOM_ID});
                return httpBackend.flush();
            }).then(() => {
                httpBackend.verifyNoOutstandingExpectation();

                // the roomview should now be loading
                expect(roomView.state.room).toBe(null);
                expect(roomView.state.joining).toBe(true);

                // there should be a spinner
                ReactTestUtils.findRenderedDOMComponentWithClass(
                    roomView, "mx_Spinner");

                // now send the room down the /sync pipe
                httpBackend.when('GET', '/sync').
                    respond(200, {
                        rooms: {
                            join: {
                                [ROOM_ID]: {
                                    state: {},
                                    timeline: {
                                        events: [],
                                        limited: true,
                                    },
                                },
                            },
                        },
                    });
                return httpBackend.flush();
            }).then(() => {
                // now the room should have loaded
                expect(roomView.state.room).toExist();
                expect(roomView.state.joining).toBe(false);
            }).done(done, done);
        });
    });
});
<commit_msg>Fix joining test<commit_after>/*
Copyright 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/* joining.js: tests for the various paths when joining a room */

require('skin-sdk');

var jssdk = require('matrix-js-sdk');

var sdk = require('matrix-react-sdk');
var peg = require('matrix-react-sdk/lib/MatrixClientPeg');
var dis = require('matrix-react-sdk/lib/dispatcher');
var MatrixChat = sdk.getComponent('structures.MatrixChat');
var RoomDirectory = sdk.getComponent('structures.RoomDirectory');
var RoomPreviewBar = sdk.getComponent('rooms.RoomPreviewBar');
var RoomView = sdk.getComponent('structures.RoomView');

var React = require('react');
var ReactDOM = require('react-dom');
var ReactTestUtils = require('react-addons-test-utils');
var expect = require('expect');
var q = require('q');

var test_utils = require('../test-utils');
var MockHttpBackend = require('../mock-request');

var HS_URL='http://localhost';
var IS_URL='http://localhost';
var USER_ID='@me:localhost';
var ACCESS_TOKEN='access_token';

describe('joining a room', function () {
    describe('over federation', function () {
        var parentDiv;
        var httpBackend;
        var matrixChat;

        beforeEach(function() {
            test_utils.beforeEach(this);
            httpBackend = new MockHttpBackend();
            jssdk.request(httpBackend.requestFn);
            parentDiv = document.createElement('div');

            // uncomment this to actually add the div to the UI, to help with
            // debugging (but slow things down)
            // document.body.appendChild(parentDiv);
        });

        afterEach(function() {
            if (parentDiv) {
                ReactDOM.unmountComponentAtNode(parentDiv);
                parentDiv.remove();
                parentDiv = null;
            }
        });

        it('should not get stuck at a spinner', function(done) {
            var ROOM_ALIAS = '#alias:localhost';
            var ROOM_ID = '!id:localhost';

            httpBackend.when('PUT', '/presence/'+encodeURIComponent(USER_ID)+'/status')
                .respond(200, {});
            httpBackend.when('GET', '/pushrules').respond(200, {});
            httpBackend.when('POST', '/filter').respond(200, { filter_id: 'fid' });
            httpBackend.when('GET', '/sync').respond(200, {});
            httpBackend.when('GET', '/publicRooms').respond(200, {chunk: []});
            httpBackend.when('GET', '/directory/room/'+encodeURIComponent(ROOM_ALIAS)).respond(200, { room_id: ROOM_ID });

            // start with a logged-in client
            localStorage.setItem("mx_hs_url", HS_URL );
            localStorage.setItem("mx_is_url", IS_URL );
            localStorage.setItem("mx_access_token", ACCESS_TOKEN );
            localStorage.setItem("mx_user_id", USER_ID);

            var mc = <MatrixChat config={{}}/>;
            matrixChat = ReactDOM.render(mc, parentDiv);

            // switch to the Directory
            dis.dispatch({
                action: 'view_room_directory',
            });

            var roomView;
            httpBackend.flush().then(() => {
                var roomDir = ReactTestUtils.findRenderedComponentWithType(
                    matrixChat, RoomDirectory);

                // enter an alias in the input, and simulate enter
                var input = ReactTestUtils.findRenderedDOMComponentWithTag(
                    roomDir, 'input');
                input.value = ROOM_ALIAS;
                ReactTestUtils.Simulate.keyUp(input, {key: 'Enter'});

                // that should create a roomview which will start a peek; wait
                // for the peek.
                httpBackend.when('GET', '/rooms/'+encodeURIComponent(ROOM_ID)+"/initialSync")
                    .respond(401, {errcode: 'M_GUEST_ACCESS_FORBIDDEN'});
                return httpBackend.flush();
            }).then(() => {
                httpBackend.verifyNoOutstandingExpectation();

                // we should now have a roomview, with a preview bar
                roomView = ReactTestUtils.findRenderedComponentWithType(
                    matrixChat, RoomView);

                var previewBar = ReactTestUtils.findRenderedComponentWithType(
                    roomView, RoomPreviewBar);

                var joinLink = ReactTestUtils.findRenderedDOMComponentWithTag(
                    previewBar, 'a');

                ReactTestUtils.Simulate.click(joinLink);

                // that will fire off a request to check our displayname, followed by a
                // join request
                httpBackend.when('GET', '/profile/'+encodeURIComponent(USER_ID))
                    .respond(200, {displayname: 'boris'});
                httpBackend.when('POST', '/join/'+encodeURIComponent(ROOM_ALIAS))
                    .respond(200, {room_id: ROOM_ID});
                return httpBackend.flush();
            }).then(() => {
                httpBackend.verifyNoOutstandingExpectation();

                // the roomview should now be loading
                expect(roomView.state.room).toBe(null);
                expect(roomView.state.joining).toBe(true);

                // there should be a spinner
                ReactTestUtils.findRenderedDOMComponentWithClass(
                    roomView, "mx_Spinner");

                // now send the room down the /sync pipe
                httpBackend.when('GET', '/sync').
                    respond(200, {
                        rooms: {
                            join: {
                                [ROOM_ID]: {
                                    state: {},
                                    timeline: {
                                        events: [],
                                        limited: true,
                                    },
                                },
                            },
                        },
                    });
                return httpBackend.flush();
            }).then(() => {
                // now the room should have loaded
                expect(roomView.state.room).toExist();
                expect(roomView.state.joining).toBe(false);
            }).done(done, done);
        });
    });
});
<|endoftext|>