<commit_before>import request from 'superagent';

/**
 * Kaonashi Client
 */
export default class KaonashiClient {

  /**
   * Get Note Titles
   *
   * @return {Promise<Object>}
   */
  getNoteTitles() {
    return new Promise((resolve, reject) => {
      request.get('http://localhost:8080/note')
        .end((err, res) => {
          if (err) {
            return reject(err);
          }
          resolve(res.body.data);
        });
    });
  }

  /**
   * Post Note
   *
   * @return {Promise<Object>}
   */
  postNote() {
    return new Promise((resolve, reject) => {
      request.post('http://localhost:8080/note')
        .send({data: {body: 'note body', title: 'POST'}})
        .set('Accept', 'application/json')
        .set('Content-type', 'application/json')
        .end(err => {
          if (err) {
            return reject(err);
          }
          resolve();
        });
    });
  }

}
<commit_msg>feat(helpers): add get note method<commit_after>import request from 'superagent';

/**
 * Kaonashi Client
 */
export default class KaonashiClient {

  /**
   * Get Note
   *
   * @param {string} id - note id
   * @return {Promise<Object>}
   */
  getNote(id) {
    return new Promise((resolve, reject) => {
      request.get(`http://localhost:8080/note/${id}`)
        .end((err, res) => {
          if (err) {
            return reject(err);
          }
          resolve(res.body.data);
        });
    });
  }

  /**
   * Get Note Titles
   *
   * @return {Promise<Object>}
   */
  getNoteTitles() {
    return new Promise((resolve, reject) => {
      request.get('http://localhost:8080/note')
        .end((err, res) => {
          if (err) {
            return reject(err);
          }
          resolve(res.body.data);
        });
    });
  }

  /**
   * Post Note
   *
   * @return {Promise<Object>}
   */
  postNote() {
    return new Promise((resolve, reject) => {
      request.post('http://localhost:8080/note')
        .send({data: {body: 'note body', title: 'POST'}})
        .set('Accept', 'application/json')
        .set('Content-type', 'application/json')
        .end(err => {
          if (err) {
            return reject(err);
          }
          resolve();
        });
    });
  }

}
<|endoftext|>


<commit_before>var colors = require('colors'),
    async = require('async'),
    shell = require('shelljs'),
    path = require('path'),
    Q = require('q'),
    utils = require('./utils'),
    _ = require('underscore'),
    Compiler = require('./compiler');

// rather than being compile project, this should just be
// compile. it should be able to take anything from a single
// file to a large nested directory structure. the first argument
// it takes should be either a file or directory.

exports.compile_project = function(cb){

  process.stdout.write('compiling... '.grey)
  global.options.debug.log('')

  var compiler = new Compiler();
  _.bindAll(compiler);

  // handle compile errors
  compiler.on('error', function(err){
    console.error("\n\n------------ ERROR ------------\n\n".red + err + "\n");
    utils.add_error_messages.call(this, err, this.finish);
  });

  // callback for compilation process
  compiler.once('finished', function(){
    process.stdout.write('done!\n'.green)
    cb();
  });

  // main compile process
  // --------------------
  // tried to make the code as clear as possible, email me with questions
  // if you are confused and trying to understand or contribute!

  // analyze project should change to just analyze, and take either the
  // file or directory as input. it should however spit out the same array.
  // this is much like how any other compiler works (generate AST, parse), and
  // I feel silly for not having realized this earlier.

  // i think analyze and process should also be split out to separate functions
  // within roots' api.

  utils.analyze_project().then(create_folders).then(function(project){
    global.options.debug.log('compiling and copying files');

    async.parallel([compile_files, copy_static_files], compiler.finish)

    function compile_files(cb){
      async.map(project.compiled_files, compiler.compile, cb);
    }

    function copy_static_files(cb){
      async.map(project.static_files, compiler.copy, cb);
    }

  });

  function create_folders(project){
    global.options.debug.log('creating folders');
    shell.mkdir('-p', path.join(process.cwd(), 'public'));

    for (var key in project.folders) {
      // both assets and views dump their contents to the public root
      var folder = project.folders[key].replace(/^assets|views/, '');
      shell.mkdir('-p', path.join(process.cwd(), 'public', folder));
    }

    return Q.fcall(function(){ return project });
  }

};<commit_msg>initial structure pass<commit_after>var colors = require('colors'),
    async = require('async'),
    shell = require('shelljs'),
    path = require('path'),
    _ = require('underscore'),
    Q = require('q'),
    deferred = Q.defer(),
    utils = require('./utils'),
    Compiler = require('./compiler');

//
// initialization and event handling
//

var compiler = new Compiler();
_.bindAll(compiler);

compiler.on('error', function(err){
  console.error("\n\n------------ ERROR ------------\n\n".red + err + "\n");
  utils.add_error_messages.call(this, err, this.finish);
});

compiler.once('finished', function(){
  process.stdout.write('done!\n'.green)
  cb();
});

//
// @api public
// Given a root (folder or file), compile with roots and output to /public
//

exports.compile_project = function(root, done){

  process.stdout.write('compiling... '.grey)
  global.options.debug.log('')

  analyze(root).then(compile).then(done);

};

//
// @api private
// parse file/directory input and generate mini roots-style AST.
//

function analyze(root){
  global.options.debug.log('analyzing project');

  var ast = {
    folders: {},
    compiled_files: [],
    static_files: []
  }

  if (fs.statSync(root).isDirectory()) {
    return parse_directory(root);
  } else {
    return Q.fcall(function(){ return parse_file(root) });
  }

  function parse_directory(file){

    // format negate pattern
    var ignores = []
    global.options.ignore_files.forEach(function(pattern){
      ignores.push("!" + pattern.toString().replace(/\//g, ""))
    });

    // read through the current project and organize the files
    var options = {
      root: process.cwd(),
      directoryFilter: ignores.concat(['!public', '!plugins']),
      fileFilter: ignores
    };

    readdirp(options, function(err, res){
      if (err) { console.error(err) }

      // populate folders
      ast.folders = _.pluck(res.directories, 'path');

      // populate compiled and copied files
      res.files.forEach(function(file){
        parse_file(file.path);
      });

      deferred.resolve(ast);

    });

    return deferred.promise;
  }

  function parse_file(file){
    if (global.options.compiled_extensions.indexOf(path.extname(file).slice(1)) >= 0) {
      ast.compiled_files.push(file);
    } else {
      ast.static_files.push(file);
    }
  }

}

//
// @api private
// compile and write the files given a roots AST.
//

function compile(ast){
  create_folders().then(function(){
    global.options.debug.log('compiling and copying files');

    async.parallel([compile_files, copy_static_files], compiler.finish);
    function compile_files(cb){ async.map(ast.compiled_files, compiler.compile, cb); }
    function copy_static_files(cb){ async.map(ast.static_files, compiler.copy, cb); }
  });

  function create_folders(project){
    global.options.debug.log('creating folders');
    shell.mkdir('-p', path.join(process.cwd(), 'public'));

    for (var key in project.folders) {
      // both assets and views dump their contents to the public root
      var folder = project.folders[key].replace(/^assets|views/, '');
      shell.mkdir('-p', path.join(process.cwd(), 'public', folder));
    }

    return Q.fcall(function(){ return project });
  }
}<|endoftext|>


<commit_before>define(function(require, exports, module) {
"use strict";

var RegExpType = require("./types").RegExp;
var Types = require("./types").Types;
var flatten = require("./utils").flatten;
var error = require("http-error");

module.exports = function Route(route, options, handler, types) {

    // options is optional
    if (typeof options == "function" || Array.isArray(options)) {
        types = handler;
        handler = options;
        options = {};
    }

    options.route = route;
    types = types || new Types();

    this.middlewares = flatten(handler);

    this.middlewares = this.middlewares.map(function(handler) {
        if (handler.length == 2) {
            handler = wrapHandler(handler);
        }
        return handler;
    });

    this.middlewares.unshift(decodeParams);

    this.method = (options.method || "GET").toLowerCase();

    var self = this;
    var keys = [];
    var params = options.params || {};
    var routeRe = normalizePath(options.route, keys, params);
    params = normalizeParams(params);

    function wrapHandler(handler) {
        return function(req, res, next) {
            handler(req.params || {}, function(err, json, headers, code) {
                if (err) return next(err);

                res.json(json, headers, code);
            });
        };
    }

    /**
     * Creates a rgular expression to match this route.
     * Url param names are stored in `keys` and the `params` are completed with
     * the default values for url parameters.
     */
    function normalizePath(path, keys, params) {
        for (var name in params) {
            var param = params[name];
            if (typeof param == "string" || param instanceof RegExp)
                params[name] = { type: param};
        }

        path = path
            .concat("/?")
            .replace(/\/:([\w\.\-\_]+)(\*?)/g, function(match, key, wildcard) {
                keys.push(key);
                if (!params[key]) {
                    params[key] = {};
                }
                // url params default to type string and optional=false
                var param = params[key];
                param.type = param.type || "string";
                param.optional = false;

                if (!param.source)
                    param.source = "url";

                if (param.source !== "url")
                    throw new Error("Url parameters must have 'url' as source but found '" + param.source + "'");

                if (wildcard)
                    return "(/*)";
                else
                    return "/([^\\/]+)";
            })
            .replace(/([\/.])/g, '\\$1')
            .replace(/\*/g, '(.*)');

        return new RegExp('^' + path + '$');
    }

    function normalizeParams(params) {
        for (var name in params) {
            var param = params[name];

            if (param.source == "query") {
                // query params default to string
                param.type = param.type || "string";
            }
            else if (!param.source || param.source == "body") {
                // body params default to json
                param.type = param.type || "json";
                param.source = "body";
            }
            else if (param.source === "url") {
                param.type = param.type || "string";
            }
            else {
                throw new Error("parameter source muste be 'url', 'query' or 'body'");
            }

            // optional defaults to false
            param.optional = !!param.optional;

            // allow regular expressions as types
            if (param.type instanceof RegExp)
                param.type = new RegExpType(param.type);

            // convert all types to type objects
            param.type = types.get(param.type);
        }

        return params;
    }

    /**
     * Check if the given path matched the route regular expression. If
     * the regular expression doesn't match or parsing fails `match` will
     * return `false`
     **/
    this.match = function(req, path) {
        var m = path.match(routeRe);
        if (!m) return false;

        var match = {};
        for (var i = 0; i < keys.length; i++) {
            var value = m[i+1];
            var key = keys[i];
            var param = params[key];
            var type = param.type;
            try {
                value = type.parse(value);
            } catch (e) {
                return false;
            }
            if (!type.check(value)) {
                return false;
            }
            match[key] = value;
        }
        req.match = match;
        return true;
    };

    /**
     * Middleware to validate the parameters. It will take `req.match` for
     * url params, decode the query and body parameters. If validation passes
     * the decoded and validated parameters are stored in `req.params`
     * otherwhise an error is returned.
     */
    function decodeParams(req, res, next) {
        var urlParams = req.match;
        if (!urlParams) return;

        var body = req.body || {};
        var query = req.parsedUrl.query;

        req.params = req.params || {};
        var errors = [];

        // marker object
        var EMPTY = {};

        // 1. check if all required params are there
        for (var key in params) {
            var param = params[key];
            if (
                (!param.optional) && (
                    (param.source == "body" && !(key in body)) ||
                    (param.source == "query" && !(key in query)) ||
                    (param.source == "url" && !(key in urlParams))
                )
            ) {
                errors.push({
                    "resource": self.name || "root",
                    "field": key,
                    "source": param.source,
                    "code": "missing_field"
                });
            }
            else {
                var type = param.type;
                var value = EMPTY;
                var isValid = true;
                switch(param.source) {
                    case "body":
                        if (param.optional && !(key in body))
                            break;

                        value = body[key]; // body is already JSON parsed
                        isValid = type.check(value);
                        break;
                    case "query":
                        if (param.optional && !(key in query))
                            break;

                        try {
                            value = type.parse(query[key]);
                        } catch(e) {
                            isValid = false;
                        }
                        isValid = isValid === false ? false : type.check(value);
                        break;
                    case "url":
                        if (param.optional && !(key in urlParams))
                            break;

                        value = urlParams[key]; // is already parsed and checked
                        isValid = true;
                        break;
                }

                if (!isValid) {
                    errors.push({
                        "resource": self.name || "root",
                        "field": key,
                        "type_expected": type.toString(),
                        "code": "invalid"
                    });
                }
                else {
                    if (value !== EMPTY)
                        req.params[key] = value;
                    else if ("default" in param)
                        req.params[key] = param.default;

                }
            }
        }

        if (errors.length) {
            var err = new error.UnprocessableEntity("Validation failed");
            err.errors = errors;
            return next(err);
        }
        next();
    }

    this.describe = function() {
        var route = {
            route: options.route,
            method: this.method
        };

        if (options.name)
            route.name = options.name;

        if (options.description)
            route.description = options.description;

        route.params = {};
        for (var name in params) {
            var param = params[name];
            route.params[name] = {
                name: param.name,
                type: param.type.toString(),
                source: param.source,
                optional: param.optional
            };
            if (param.description)
                route.params[name].description = param.description;
        }

        if (!Object.keys(route.params).length)
            delete route.params;

        return route;
    };
};

});<commit_msg>support params passed from parent<commit_after>define(function(require, exports, module) {
"use strict";

var RegExpType = require("./types").RegExp;
var Types = require("./types").Types;
var flatten = require("./utils").flatten;
var error = require("http-error");

module.exports = function Route(route, options, handler, types, parent ) {

    // options is optional
    if (typeof options == "function" || Array.isArray(options)) {
        types = handler;
        handler = options;
        options = {};
    }

    parent = parent || {};

    options.route = route;
    types = types || new Types();

    this.middlewares = flatten(handler);

    this.middlewares = this.middlewares.map(function(handler) {
        if (handler.length == 2) {
            handler = wrapHandler(handler);
        }
        return handler;
    });

    this.middlewares.unshift(decodeParams);

    this.method = (options.method || "GET").toLowerCase();

    var self = this;
    var keys = [];
    var params = options.params || {};

    for ( var key in parent.params ){
        params[key] = parent.params[key];
    }

    var routeRe = normalizePath(options.route, keys, params);
    params = normalizeParams(params);

    function wrapHandler(handler) {
        return function(req, res, next) {
            handler(req.params || {}, function(err, json, headers, code) {
                if (err) return next(err);

                res.json(json, headers, code);
            });
        };
    }

    /**
     * Creates a rgular expression to match this route.
     * Url param names are stored in `keys` and the `params` are completed with
     * the default values for url parameters.
     */
    function normalizePath(path, keys, params) {
        for (var name in params) {
            var param = params[name];
            if (typeof param == "string" || param instanceof RegExp)
                params[name] = { type: param};
        }

        path = path
            .concat("/?")
            .replace(/\/:([\w\.\-\_]+)(\*?)/g, function(match, key, wildcard) {
                keys.push(key);
                if (!params[key]) {
                    params[key] = {};
                }
                // url params default to type string and optional=false
                var param = params[key];
                param.type = param.type || "string";
                param.optional = false;

                if (!param.source)
                    param.source = "url";

                if (param.source !== "url")
                    throw new Error("Url parameters must have 'url' as source but found '" + param.source + "'");

                if (wildcard)
                    return "(/*)";
                else
                    return "/([^\\/]+)";
            })
            .replace(/([\/.])/g, '\\$1')
            .replace(/\*/g, '(.*)');

        return new RegExp('^' + path + '$');
    }

    function normalizeParams(params) {
        for (var name in params) {
            var param = params[name];

            if (param.source == "query") {
                // query params default to string
                param.type = param.type || "string";
            }
            else if (!param.source || param.source == "body") {
                // body params default to json
                param.type = param.type || "json";
                param.source = "body";
            }
            else if (param.source === "url") {
                param.type = param.type || "string";
            }
            else {
                throw new Error("parameter source muste be 'url', 'query' or 'body'");
            }

            // optional defaults to false
            param.optional = !!param.optional;

            // allow regular expressions as types
            if (param.type instanceof RegExp)
                param.type = new RegExpType(param.type);

            // convert all types to type objects
            param.type = types.get(param.type);
        }

        return params;
    }

    /**
     * Check if the given path matched the route regular expression. If
     * the regular expression doesn't match or parsing fails `match` will
     * return `false`
     **/
    this.match = function(req, path) {
        var m = path.match(routeRe);

        if (!m) return false;

        var match = {};
        for (var i = 0; i < keys.length; i++) {
            var value = m[i+1];
            var key = keys[i];
            var param = params[key];
            var type = param.type;
            try {
                value = type.parse(value);
            } catch (e) {
                return false;
            }
            if (!type.check(value)) {
                return false;
            }
            match[key] = value;
        }

        req.match = match;
        return true;
    };

    /**
     * Middleware to validate the parameters. It will take `req.match` for
     * url params, decode the query and body parameters. If validation passes
     * the decoded and validated parameters are stored in `req.params`
     * otherwhise an error is returned.
     */
    function decodeParams(req, res, next) {
        var urlParams = req.match;

        if (!urlParams) return;

        var body = req.body || {};
        var query = req.parsedUrl.query;

        req.params = req.params || {};
        var errors = [];

        // marker object
        var EMPTY = {};

        // 1. check if all required params are there
        for (var key in params) {
            var param = params[key];

            if( keys.indexOf(key) == -1 ) continue;

            if (
                (!param.optional) && (
                    (param.source == "body" && !(key in body)) ||
                    (param.source == "query" && !(key in query)) ||
                    (param.source == "url" && !(key in urlParams))
                )
            ) {
                errors.push({
                    "resource": self.name || "root",
                    "field": key,
                    "source": param.source,
                    "code": "missing_field"
                });
            }
            else {
                var type = param.type;
                var value = EMPTY;
                var isValid = true;

                switch(param.source) {
                    case "body":
                        if (param.optional && !(key in body))
                            break;

                        value = body[key]; // body is already JSON parsed
                        isValid = type.check(value);
                        break;
                    case "query":
                        if (param.optional && !(key in query))
                            break;

                        try {
                            value = type.parse(query[key]);
                        } catch(e) {
                            isValid = false;
                        }
                        isValid = isValid === false ? false : type.check(value);
                        break;
                    case "url":
                        if (param.optional && !(key in urlParams))
                            break;

                        value = urlParams[key]; // is already parsed and checked
                        isValid = true;
                        break;
                }

                if (!isValid) {
                    errors.push({
                        "resource": self.name || "root",
                        "field": key,
                        "type_expected": type.toString(),
                        "code": "invalid"
                    });
                }
                else {
                    if (value !== EMPTY)
                        req.params[key] = value;
                    else if ("default" in param)
                        req.params[key] = param.default;

                }
            }
        }

        if (errors.length) {
            var err = new error.UnprocessableEntity("Validation failed");
            err.errors = errors;
            return next(err);
        }
        next();
    }

    this.describe = function() {
        var route = {
            route: options.route,
            method: this.method
        };

        if (options.name)
            route.name = options.name;

        if (options.description)
            route.description = options.description;

        route.params = {};
        for (var name in params) {
            var param = params[name];
            route.params[name] = {
                name: param.name,
                type: param.type.toString(),
                source: param.source,
                optional: param.optional
            };
            if (param.description)
                route.params[name].description = param.description;
        }

        if (!Object.keys(route.params).length)
            delete route.params;

        return route;
    };
};

});<|endoftext|>


<commit_before>"use strict";

var RegExpType = require("./types").RegExp;
var Types = require("./types").Types;
var flatten = require("./utils").flatten;

module.exports = function Route(route, options, handler, types) {

    // options is optional
    if (typeof options == "function" || Array.isArray(options)) {
        types = handler;
        handler = options;
        options = {};
    }

    options.route = route;
    types = types || new Types();

    this.middlewares = flatten(handler);

    this.middlewares = this.middlewares.map(function(handler) {
        if (handler.length == 2) {
            handler = wrapHandler(handler);
        }
        return handler;
    });

    this.middlewares.unshift(decodeParams);

    this.method = (options.method || "GET").toLowerCase();

    var self = this;
    var keys = [];
    var params = options.params || {};
    var routeRe = normalizePath(options.route, keys, params);
    params = normalizeParams(params);

    function wrapHandler(handler) {
        return function(req, res, next) {
            handler(req.params || {}, function(err, json, headers, code) {
                if (err) return next(err);

                res.json(json, headers, code);
            });
        };
    }

    /**
     * Creates a rgular expression to match this route.
     * Url param names are stored in `keys` and the `params` are completed with
     * the default values for url parameters.
     */
    function normalizePath(path, keys, params) {
        for (var name in params) {
            var param = params[name];
            if (typeof param == "string" || param instanceof RegExp)
                params[name] = { type: param};
        }

        path = path
            .replace(/\/:([\w\.\-\_]+)(\*?)/g, function(match, key, wildcard) {
                keys.push(key);
                if (!params[key]) {
                    params[key] = {};
                }
                // url params default to type string and optional=false
                var param = params[key];
                param.type = param.type || "string";

                if (!param.source || param.source == "url")
                    param.source = "url";
                else
                    throw new Error("Url parameters must have 'url' as source but found '" + param.source + "'");
                if (wildcard)
                    return "(/*)";
                else
                    return "/([^\\/]+)";
            })
            .replace(/([\/.])/g, '\\$1')
            .replace(/\*/g, '(.*)');

        return new RegExp('^' + path + '$');
    }

    function normalizeParams(params) {
        for (var name in params) {
            var param = params[name];

            if (param.source == "query") {
                // query params default to string
                param.type = param.type || "string";
            }
            else if (!param.source || param.source == "body") {
                // body params default to json
                param.type = param.type || "json";
                param.source = "body";
            }
            else if (param.source === "url") {
                param.type = param.type || "string";
            }
            else {
                throw new Error("parameter source muste be 'url', 'query' or 'body'");
            }

            // optional defaults to false
            param.optional = !!param.optional;

            // allow regular expressions as types
            if (param.type instanceof RegExp)
                param.type = new RegExpType(param.type);

            // convert all types to type objects
            param.type = types.get(param.type);
        }

        return params;
    }

    /**
     * Check if the given path matched the route regular expression. If
     * the regular expression doesn't match or parsing fails `match` will
     * return `false`
     **/
    this.match = function(req, path) {
        var m = path.match(routeRe);
        if (!m) return false;

        var match = {};
        for (var i = 0; i < keys.length; i++) {
            var value = m[i+1];
            var key = keys[i];
            var param = params[key];
            var type = param.type;
            try {
                value = type.parse(value);
            } catch (e) {
                return false;
            }
            if (!type.check(value)) {
                return false;
            }
            match[key] = value;
        }
        req.match = match;
        return true;
    };

    /**
     * Middleware to validate the parameters. It will take `req.match` for
     * url params, decode the query and body parameters. If validation passes
     * the decoded and validated parameters are stored in `req.params`
     * otherwhise an error is returned.
     */
    function decodeParams(req, res, next) {
        var urlParams = req.match;
        if (!urlParams) return;

        var body = req.body || {};
        var query = req.parsedUrl.query;

        req.params = req.params || {};
        var errors = [];

        // marker object
        var EMPTY = {};

        // 1. check if all required params are there
        for (var key in params) {
            var param = params[key];
            if (
                (!param.optional) && (
                    (param.source == "body" && !(key in body)) ||
                    (param.source == "query" && !(key in query)) ||
                    (param.source == "url" && !(key in urlParams))
                )
            ) {
                errors.push({
                    "resource": self.name || "root",
                    "field": key,
                    "source": param.source,
                    "code": "missing_field"
                });
            }
            else {
                var type = param.type;
                var value = EMPTY;
                var isValid = true;
                switch(param.source) {
                    case "body":
                        if (param.optional && !(key in body))
                            break;

                        value = body[key]; // body is already JSON parsed
                        isValid = type.check(value);
                        break;
                    case "query":
                        if (param.optional && !(key in query))
                            break;

                        try {
                            value = type.parse(query[key]);
                        } catch(e) {
                            isValid = false;
                        }
                        isValid = isValid === false ? false : type.check(value);
                        break;
                    case "url":
                        if (param.optional && !(key in urlParams))
                            break;

                        value = urlParams[key]; // is already parsed and checked
                        isValid = true;
                        break;
                }

                if (!isValid) {
                    errors.push({
                        "resource": self.name || "root",
                        "field": key,
                        "type_expected": type.toString(),
                        "code": "invalid"
                    });
                }
                else {
                    if (value !== EMPTY)
                        req.params[key] = value;
                    else if ("default" in param)
                        req.params[key] = param.default;

                }
            }
        }

        if (errors.length) {
            res.writeHead(422, {"Content-Type": "application/json"});
            res.end(JSON.stringify({
                "message": "Validation failed",
                errors: errors
            }));
            return;
        }
        next();
    }

    this.describe = function() {
        var route = {
            route: options.route,
            method: this.method
        };

        if (options.name)
            route.name = options.name;

        if (options.description)
            route.description = options.description;

        route.params = {};
        for (var name in params) {
            var param = params[name];
            route.params[name] = {
                name: param.name,
                type: param.type.toString(),
                source: param.source,
                optional: param.optional
            };
            if (param.description)
                route.params[name].description = param.description;
        }

        if (!Object.keys(route.params).length)
            delete route.params;

        return route;
    };
};<commit_msg>let the error handler report the error<commit_after>"use strict";

var RegExpType = require("./types").RegExp;
var Types = require("./types").Types;
var flatten = require("./utils").flatten;
var error = require("http-error");

module.exports = function Route(route, options, handler, types) {

    // options is optional
    if (typeof options == "function" || Array.isArray(options)) {
        types = handler;
        handler = options;
        options = {};
    }

    options.route = route;
    types = types || new Types();

    this.middlewares = flatten(handler);

    this.middlewares = this.middlewares.map(function(handler) {
        if (handler.length == 2) {
            handler = wrapHandler(handler);
        }
        return handler;
    });

    this.middlewares.unshift(decodeParams);

    this.method = (options.method || "GET").toLowerCase();

    var self = this;
    var keys = [];
    var params = options.params || {};
    var routeRe = normalizePath(options.route, keys, params);
    params = normalizeParams(params);

    function wrapHandler(handler) {
        return function(req, res, next) {
            handler(req.params || {}, function(err, json, headers, code) {
                if (err) return next(err);

                res.json(json, headers, code);
            });
        };
    }

    /**
     * Creates a rgular expression to match this route.
     * Url param names are stored in `keys` and the `params` are completed with
     * the default values for url parameters.
     */
    function normalizePath(path, keys, params) {
        for (var name in params) {
            var param = params[name];
            if (typeof param == "string" || param instanceof RegExp)
                params[name] = { type: param};
        }

        path = path
            .replace(/\/:([\w\.\-\_]+)(\*?)/g, function(match, key, wildcard) {
                keys.push(key);
                if (!params[key]) {
                    params[key] = {};
                }
                // url params default to type string and optional=false
                var param = params[key];
                param.type = param.type || "string";

                if (!param.source || param.source == "url")
                    param.source = "url";
                else
                    throw new Error("Url parameters must have 'url' as source but found '" + param.source + "'");
                if (wildcard)
                    return "(/*)";
                else
                    return "/([^\\/]+)";
            })
            .replace(/([\/.])/g, '\\$1')
            .replace(/\*/g, '(.*)');

        return new RegExp('^' + path + '$');
    }

    function normalizeParams(params) {
        for (var name in params) {
            var param = params[name];

            if (param.source == "query") {
                // query params default to string
                param.type = param.type || "string";
            }
            else if (!param.source || param.source == "body") {
                // body params default to json
                param.type = param.type || "json";
                param.source = "body";
            }
            else if (param.source === "url") {
                param.type = param.type || "string";
            }
            else {
                throw new Error("parameter source muste be 'url', 'query' or 'body'");
            }

            // optional defaults to false
            param.optional = !!param.optional;

            // allow regular expressions as types
            if (param.type instanceof RegExp)
                param.type = new RegExpType(param.type);

            // convert all types to type objects
            param.type = types.get(param.type);
        }

        return params;
    }

    /**
     * Check if the given path matched the route regular expression. If
     * the regular expression doesn't match or parsing fails `match` will
     * return `false`
     **/
    this.match = function(req, path) {
        var m = path.match(routeRe);
        if (!m) return false;

        var match = {};
        for (var i = 0; i < keys.length; i++) {
            var value = m[i+1];
            var key = keys[i];
            var param = params[key];
            var type = param.type;
            try {
                value = type.parse(value);
            } catch (e) {
                return false;
            }
            if (!type.check(value)) {
                return false;
            }
            match[key] = value;
        }
        req.match = match;
        return true;
    };

    /**
     * Middleware to validate the parameters. It will take `req.match` for
     * url params, decode the query and body parameters. If validation passes
     * the decoded and validated parameters are stored in `req.params`
     * otherwhise an error is returned.
     */
    function decodeParams(req, res, next) {
        var urlParams = req.match;
        if (!urlParams) return;

        var body = req.body || {};
        var query = req.parsedUrl.query;

        req.params = req.params || {};
        var errors = [];

        // marker object
        var EMPTY = {};

        // 1. check if all required params are there
        for (var key in params) {
            var param = params[key];
            if (
                (!param.optional) && (
                    (param.source == "body" && !(key in body)) ||
                    (param.source == "query" && !(key in query)) ||
                    (param.source == "url" && !(key in urlParams))
                )
            ) {
                errors.push({
                    "resource": self.name || "root",
                    "field": key,
                    "source": param.source,
                    "code": "missing_field"
                });
            }
            else {
                var type = param.type;
                var value = EMPTY;
                var isValid = true;
                switch(param.source) {
                    case "body":
                        if (param.optional && !(key in body))
                            break;

                        value = body[key]; // body is already JSON parsed
                        isValid = type.check(value);
                        break;
                    case "query":
                        if (param.optional && !(key in query))
                            break;

                        try {
                            value = type.parse(query[key]);
                        } catch(e) {
                            isValid = false;
                        }
                        isValid = isValid === false ? false : type.check(value);
                        break;
                    case "url":
                        if (param.optional && !(key in urlParams))
                            break;

                        value = urlParams[key]; // is already parsed and checked
                        isValid = true;
                        break;
                }

                if (!isValid) {
                    errors.push({
                        "resource": self.name || "root",
                        "field": key,
                        "type_expected": type.toString(),
                        "code": "invalid"
                    });
                }
                else {
                    if (value !== EMPTY)
                        req.params[key] = value;
                    else if ("default" in param)
                        req.params[key] = param.default;

                }
            }
        }

        if (errors.length) {
            var err = new error.UnprocessableEntity("Validation failed");
            err.errors = errors;
            return next(err);
        }
        next();
    }

    this.describe = function() {
        var route = {
            route: options.route,
            method: this.method
        };

        if (options.name)
            route.name = options.name;

        if (options.description)
            route.description = options.description;

        route.params = {};
        for (var name in params) {
            var param = params[name];
            route.params[name] = {
                name: param.name,
                type: param.type.toString(),
                source: param.source,
                optional: param.optional
            };
            if (param.description)
                route.params[name].description = param.description;
        }

        if (!Object.keys(route.params).length)
            delete route.params;

        return route;
    };
};<|endoftext|>


<commit_before>Router.configure({
	layoutTemplate: 'Main',
	yieldTemplates: {
		'Header': { to: 'header'},
		'Footer': { to: 'footer'}
	}
});

if(Meteor.isClient) {
	Accounts.onLogin(function() {
		Router.go('User');
	});

	Accounts.onLogout(function() {
		Router.go('Home');
	});
}

Router.route('Home',{
	path:'/',
	name:'Home'
});

Router.route('User',{
	path:'/user',
	name:'User'
});

Router.route('/user_dashboard/:id',{
	template: 'UserDashboard',
	name: 'UserDashboard',
	waitOn: function() {
        return Meteor.subscribe('user');
    },
    onBeforeAction: function() {
    	var id= this.params.id;
        Session.set('userId', id);
        console.log('userId: '+ Session.get('userId'));
        this.next();
    }
});
//------------------------------------
Router.route('Note',{
	path:'/note',
	name:'Note'
});

/*Router.route('publishNote',{
	path:'/publish_note',
	name:'publishNote'
});*/

Router.route('CreateNote',{
	path:'/new_note',
	name:'CreateNote'
});

Router.route('YourNotes',{
	path:'/user/showNotes',
	name:'YourNotes'
});

Router.route('SingleNote', {
	path: '/posts/:id',
	name: 'SingleNote',
	waitOn: function() {
        return Meteor.subscribe('posts');
    },
    onBeforeAction: function() {
    	var id= this.params.id;
        Session.set('postId', id);
        console.log('postId: '+ Session.get('postId'));
        this.next();
    }
});

Router.route('EditNote',{
	path:'/edit_note',
	name:'EditNote'
});

Router.route('SharedNotesInGroup',{
	path:'/shared_notes',
	name:'SharedNotesInGroup'
});

Router.route('CreateNoteInGroup',{
	path:'/create_groupNotes',
	name:'CreateNoteInGroup'
});

Router.route('SingleNoteOfGroup', {
	path: '/group_notes/:id',
	name: 'SingleNoteOfGroup',
	waitOn: function() {
        return Meteor.subscribe('posts');
    },
    onBeforeAction: function() {
    	var id= this.params.id;
        Session.set('postId', id);
        console.log('group_postId: '+ Session.get('postId'));
        this.next();
    }
});

/*Router.route('/group/:id/create_groupNotes',{
	template:'CreateNoteInGroup',
	name:'CreateNoteInGroup',
	waitOn: function() {
        return Meteor.subscribe('groups', 'posts');
    },
    onBeforeAction: function() {
        Session.set('group', this.params.id);
        this.next();
    }
});
*/
Router.route('EditNoteOfGroup',{
	path:'/edit_groupNote',
	name:'EditNoteOfGroup'
});
//--------------------------------------
Router.route('CreateGroup',{
	path:'/new_group',
	name:'CreateGroup'
});

Router.route('YourGroup',{
	path:'/user/showGroups',
	name:'YourGroup'
});

Router.route('SingleGroup', {
	path: '/group/:id',
	name: 'SingleGroup',
	waitOn: function() {
        return Meteor.subscribe('groups');
    },
    onBeforeAction: function() {
        Session.set('groupId', this.params.id);
        console.log('groupid: '+ Session.get('groupId'));
        this.next();
    }
});

Router.route('Members',{
	path:'/group_member',
	name:'Members'
});

Router.route('Invite',{
	path:'/group_invite',
	name:'Invite'
});
//------------Todo----------------------
Router.route('CreateTodo',{
	path:'/Create_todo',
	name:'CreateTodo'
});

Router.route('YourTodo',{
	path:'/user/showTodo',
	name:'YourTodo'
});

//------------Group Task----------------------
Router.route('CreateTask',{
	path:'/create_groupTask',
	name:'CreateTask'
});

Router.route('GroupTask',{
	path:'/group_task',
	name:'GroupTask'
});

Router.route('groupDiscuss',{
	path:'/groupDiscuss',
	name:'groupDiscuss'
});

Router.route('postMessage',{
	path:'/postMessage',
	name:'postMessage'
});

//--------------Media---------------------------------------------

Router.route('AddMedia',{
	path:'/add_media',
	name:'AddMedia'
});

Router.route('AddMediaInGroup',{
	path:'/add_groupMedia',
	name:'AddMediaInGroup'
});

Router.route('SharedMediaInGroup',{
	path:'/shared_media',
	name:'SharedMediaInGroup'
});

Router.route('YourMedia',{
	path:'/user/showMedia',
	name:'YourMedia'
});

Router.route('images',{
	path:'/images',
	name:'images'
});

Router.route('files',{
	path:'/files',
	name:'files'
});

Router.route('audio',{
	path:'/audio',
	name:'audio'
});

Router.route('video',{
	path:'/video',
	name:'video'
});
if (Meteor.isClient) {

  // Scroll to top or requested hash after loading each page
  Router.onAfterAction(function() {
    Meteor.setTimeout(function () {
      var hash = $(window.location.hash);
      var scrollTo = hash.length ? hash.offset().top : 0;
      $("html, body").animate({ scrollTop: scrollTo }, 700, "easeInOutQuart");
    }, 0);
  });

  // Route-related helpers
  Template.registerHelper("absoluteUrl", function(path) {
    return Meteor.absoluteUrl(path);
  });

  Template.registerHelper("currentRouteIs", function(name) {
    var current = Router.current();
    return current && current.route && current.route.name === name || false;
  });

  Template.registerHelper("activeRoute", function(name) {
    var current = Router.current();
    return current && current.route && current.route.name === name && "active" || "";
  });

}

Router.route('Updates',{
	path:'/user/updates',
	name:'Updates'
});

Router.route('Search',{
	path:'/user/search',
	name:'Search'
});

<commit_msg>Updated<commit_after>Router.configure({
	layoutTemplate: 'Main',
	yieldTemplates: {
		'Header': { to: 'header'},
		'Footer': { to: 'footer'}
	}
});

if(Meteor.isClient) {
	Accounts.onLogin(function() {
		Router.go('User');
	});

	Accounts.onLogout(function() {
		Router.go('Home');
	});
}

Router.route('Home',{
	path:'/',
	name:'Home'
});

Router.route('User',{
	path:'/user',
	name:'User'
});

Router.route('VerifyUser',{
	path:'/verify',
	name:'VerifyUser'
});

Router.route('/user_dashboard/:id',{
	template: 'UserDashboard',
	name: 'UserDashboard',
	waitOn: function() {
        return Meteor.subscribe('user');
    },
    onBeforeAction: function() {
    	var id= this.params.id;
        Session.set('userId', id);
        console.log('userId: '+ Session.get('userId'));
        this.next();
    }
});
//------------------------------------
Router.route('Note',{
	path:'/note',
	name:'Note'
});

/*Router.route('publishNote',{
	path:'/publish_note',
	name:'publishNote'
});*/

Router.route('CreateNote',{
	path:'/new_note',
	name:'CreateNote'
});

Router.route('YourNotes',{
	path:'/user/showNotes',
	name:'YourNotes'
});

Router.route('SingleNote', {
	path: '/posts/:id',
	name: 'SingleNote',
	waitOn: function() {
        return Meteor.subscribe('posts');
    },
    onBeforeAction: function() {
    	var id= this.params.id;
        Session.set('postId', id);
        console.log('postId: '+ Session.get('postId'));
        this.next();
    }
});

Router.route('EditNote',{
	path:'/edit_note',
	name:'EditNote'
});

Router.route('SharedNotesInGroup',{
	path:'/group/:id/shared_notes',
	name:'SharedNotesInGroup',
	waitOn: function() {
        return Meteor.subscribe('groups', 'posts');
    },
    onBeforeAction: function() {
        Session.set('group', this.params.id);
        this.next();
    }
});

Router.route('CreateNoteInGroup',{
	path:'/create_groupNotes',
	name:'CreateNoteInGroup'
});

Router.route('SingleNoteOfGroup', {
	path: '/group_notes/:id',
	name: 'SingleNoteOfGroup',
	waitOn: function() {
        return Meteor.subscribe('posts');
    },
    onBeforeAction: function() {
    	var id= this.params.id;
        Session.set('postId', id);
        console.log('group_postId: '+ Session.get('postId'));
        this.next();
    }
});

/*Router.route('/group/:id/create_groupNotes',{
	template:'CreateNoteInGroup',
	name:'CreateNoteInGroup',
	waitOn: function() {
        return Meteor.subscribe('groups', 'posts');
    },
    onBeforeAction: function() {
        Session.set('group', this.params.id);
        this.next();
    }
});
*/
Router.route('EditNoteOfGroup',{
	path:'/edit_groupNote',
	name:'EditNoteOfGroup'
});
//--------------------------------------
Router.route('CreateGroup',{
	path:'/new_group',
	name:'CreateGroup'
});

Router.route('YourGroup',{
	path:'/user/showGroups',
	name:'YourGroup'
});

Router.route('SingleGroup', {
	path: '/group/:id',
	name: 'SingleGroup',
	waitOn: function() {
        return Meteor.subscribe('groups');
    },
    onBeforeAction: function() {
        Session.set('groupId', this.params.id);
        console.log('groupid: '+ Session.get('groupId'));
        this.next();
    }
});

Router.route('Members',{
	path:'/group/:id/members',
	name:'Members',
	waitOn: function() {
        return Meteor.subscribe('groups');
    },
    onBeforeAction: function() {
        Session.set('groupId', this.params.id);
        console.log('groupid: '+ Session.get('groupId'));
        this.next();
    }
});

Router.route('Invite',{
	path:'/group_invite',
	name:'Invite'
});
//------------Todo----------------------
Router.route('CreateTodo',{
	path:'/Create_todo',
	name:'CreateTodo'
});

Router.route('YourTodo',{
	path:'/user/showTodo',
	name:'YourTodo'
});

//------------Group Task----------------------
Router.route('CreateTask',{
	path:'/create_groupTask',
	name:'CreateTask'
});

Router.route('GroupTask',{
	path:'/group/:id/group_task',
	name:'GroupTask',
	waitOn: function() {
        return Meteor.subscribe('groups');
    },
    onBeforeAction: function() {
        Session.set('groupId', this.params.id);
        console.log('groupid: '+ Session.get('groupId'));
        this.next();
    }
});

Router.route('groupDiscuss',{
	path:'/groupDiscuss',
	name:'groupDiscuss'
});

Router.route('postMessage',{
	path:'/postMessage',
	name:'postMessage'
});

//--------------Media---------------------------------------------

Router.route('AddMedia',{
	path:'/add_media',
	name:'AddMedia'
});

Router.route('AddMediaInGroup',{
	path:'/add_groupMedia',
	name:'AddMediaInGroup'
});

Router.route('SharedMediaInGroup',{
	path:'/group/:id/shared_media/',
	name:'SharedMediaInGroup',
	waitOn: function() {
        return Meteor.subscribe('groups');
    },
    onBeforeAction: function() {
        Session.set('groupId', this.params.id);
        console.log('groupid: '+ Session.get('groupId'));
        this.next();
    }
});

Router.route('YourMedia',{
	path:'/user/showMedia',
	name:'YourMedia'
});

Router.route('images',{
	path:'/images',
	name:'images'
});

Router.route('files',{
	path:'/files',
	name:'files'
});

Router.route('audio',{
	path:'/audio',
	name:'audio'
});

Router.route('video',{
	path:'/video',
	name:'video'
});
if (Meteor.isClient) {

  // Scroll to top or requested hash after loading each page
  Router.onAfterAction(function() {
    Meteor.setTimeout(function () {
      var hash = $(window.location.hash);
      var scrollTo = hash.length ? hash.offset().top : 0;
      $("html, body").animate({ scrollTop: scrollTo }, 700, "easeInOutQuart");
    }, 0);
  });

  // Route-related helpers
  Template.registerHelper("absoluteUrl", function(path) {
    return Meteor.absoluteUrl(path);
  });

  Template.registerHelper("currentRouteIs", function(name) {
    var current = Router.current();
    return current && current.route && current.route.name === name || false;
  });

  Template.registerHelper("activeRoute", function(name) {
    var current = Router.current();
    return current && current.route && current.route.name === name && "active" || "";
  });

}

Router.route('Updates',{
	path:'/user/updates',
	name:'Updates'
});

Router.route('Search',{
	path:'/user/search',
	name:'Search'
});

<|endoftext|>


<commit_before>var request = require('request'),
	crypto = require('crypto'),
	_ = require('underscore'),
	fs = require('fs'),
	async = require('async');

var LiveRules = function(endPoint, user, password) {
	this._api = endPoint;
	this._auth = "Basic " + new Buffer(user + ':' + password).toString('base64');
};

/**
 * Add rules
 * @param rules Array of rules as strings or objects in the form {value: String, tag: String}
 * @param cb Function callback
 */
LiveRules.prototype.add = function(rules, cb) {
	var json = {
		rules : rules.map(function(rule) {
			return (typeof rule == 'string') ? {value: rule} : rule;
		})
	};
	request.post({
		url : this._api,
		json: json,
		headers : {'Authorization' : this._auth}
	}, function(err, response, body) {
		if (err) cb(err);
		else if (response.statusCode >= 200 && response.statusCode < 300) cb();
		else {
			var errStr = 'Unable to add rules. Request failed with status code: ' + response.statusCode;
			if (body && body.error) errStr += '.\n' + body.error.message;
			cb(new Error(errStr));
		}
	});
};

/**
 * Delete rules
 * @param rules Array of rules as strings or objects in the form {value: String, tag: String}
 * @param cb Function callback
 */
LiveRules.prototype.remove = function(rules, cb) {
	var json = {
		rules : rules.map(function(rule) {
			return (typeof rule == 'string') ? {value: rule} : rule;
		})
	};
	request.del({
		url : this._api,
		json: json,
		headers : {'Authorization' : this._auth}
	}, function(err, response, body) {
		if (err) cb(err);
		else if (response.statusCode >= 200 && response.statusCode < 300) cb();
		else cb(new Error('Unable to delete rules. Request failed with status code: ' + response.statusCode));
	});
};

/**
* Replace existing tracking rules
* @param rules Array of rules as strings or objects in the form {value: String, tag: String}
* @param cb Function callback
*/
LiveRules.prototype.update = function(rules, cb) {
	var self = this;
	self.removeAll(function(err) {
		if (err) cb(err);
		else self.add(rules, cb);
	});
};

/**
* Get current tracking rules
* @param cb Function callback
*/
LiveRules.prototype.getAll = function(cb) {
	var self = this;
	request({
		url : self._api,
		headers : {'Authorization' : self._auth}
	}, function(err, response, body) {
		if (err) cb(err);
		else if (response.statusCode >= 200 && response.statusCode < 300) {
			try {
				var rules = JSON.parse(body).rules;
				cb(null, rules);
			} catch(e) {
				cb(e);
			}
		}
		else {
			cb(new Error('Unable to fetch rules. Request failed with status code: ' + response.statusCode));
		}
	});
};

/**
* Remove current tracking rules
* @param cb Function callback
*/
LiveRules.prototype.removeAll = function(cb) {
	var self = this;
	self.getAll(function(err, rules) {
		if (err) cb(err);
		else self.remove(rules, cb);
	});
};

var GnipRules = function(options) {
	this.options = _.extend({
		user : '',
		password : '',
		url : null,
		debug : false
	}, options || {});

	this._api = this.options.url;
	this._auth = "Basic " + new Buffer(this.options.user + ':' + this.options.password).toString('base64');
	this._cacheFile = __dirname + '/' + crypto.createHash('md5').update(this._api).digest('hex') + '.cache';
	this.live = new LiveRules(this._api, this.options.user, this.options.password);

	if (!fs.existsSync(this._cacheFile)) {
		fs.writeFileSync(this._cacheFile, '[]', 'utf8');
	}
};

GnipRules.prototype._deleteRulesBatch = function(rules, max, cb) {
	var self = this;
	var groups = [];
	max = max || 4000;

	for (var i = 0; i < rules.length; i += max) {
		groups.push(rules.slice(i, i+max));
	}
	async.forEachSeries(groups, function(group, cb) {
		self.live.remove(group, cb);
	}, cb);
};

GnipRules.prototype._addRulesBatch = function(rules, max, cb) {
	var self = this;
	var groups = [];
	max = max || 4000;

	for (var i = 0; i < rules.length; i += max) {
		groups.push(rules.slice(i, i+max));
	}
	async.forEachSeries(groups, function(group, cb) {
		self.live.add(group, cb);
	}, cb);
};

GnipRules.prototype.getAll = function(cb) {
	fs.readFile(this._cacheFile, 'utf8', function(err, contents) {
		if (err) cb(err);
		else {
			try {
				cb(null, JSON.parse(contents));
			} catch (e) {
				cb(null, []);
			}
		}
	});
};

GnipRules.prototype.clearCache = function(cb) {
	fs.writeFile(this._cacheFile, '[]', 'utf8', cb);
};

GnipRules.prototype.update = function(rules, cb) {

	var self = this;

	rules = rules.map(function(rule) {
		return (typeof rule == 'string') ? {value: rule} : rule;
	});

	fs.readFile(self._cacheFile, 'utf8', function(err, contents) {
		if (err) cb(err);
		else {
			var currentRules;
			try {
				currentRules = JSON.parse(contents);
			} catch (e) {
				currentRules = [];
			}

			// which rules to delete?
			var rulesPlain = rules.map(function(rule) {
				return rule.value;
			});
			var deleteRules = currentRules.filter(function(rule) {
				return rulesPlain.indexOf(rule.value) == -1;
			});

			// which rules to add?
			var currentRulesPlain = currentRules.map(function(rule) {
				return rule.value;
			});
			var addRules = rules.filter(function(rule) {
				return currentRulesPlain.indexOf(rule.value) == -1;
			});

			async.series([
				function(cb) {
					self._deleteRulesBatch(deleteRules, 5000, cb);
				},
				function(cb) {
					self._addRulesBatch(addRules, 5000, cb);
				},
				function(cb) {
					fs.writeFile(self._cacheFile, JSON.stringify(rules), 'utf8', cb);
				}
			], function(err) {
				if (err) cb(err);
				else {
					cb(null, {
						added : addRules,
						addedCount : addRules.length,
						deleted : deleteRules,
						deletedCount : deleteRules.length
					});
				}
			});
		}
	});
}

module.exports = GnipRules;<commit_msg>Added options to change the batch size<commit_after>var request = require('request'),
	crypto = require('crypto'),
	_ = require('underscore'),
	fs = require('fs'),
	async = require('async');

var LiveRules = function(endPoint, user, password) {
	this._api = endPoint;
	this._auth = "Basic " + new Buffer(user + ':' + password).toString('base64');
};

/**
 * Add rules
 * @param rules Array of rules as strings or objects in the form {value: String, tag: String}
 * @param cb Function callback
 */
LiveRules.prototype.add = function(rules, cb) {
	var json = {
		rules : rules.map(function(rule) {
			return (typeof rule == 'string') ? {value: rule} : rule;
		})
	};
	request.post({
		url : this._api,
		json: json,
		headers : {'Authorization' : this._auth}
	}, function(err, response, body) {
		if (err) cb(err);
		else if (response.statusCode >= 200 && response.statusCode < 300) cb();
		else {
			var errStr = 'Unable to add rules. Request failed with status code: ' + response.statusCode;
			if (body && body.error) errStr += '.\n' + body.error.message;
			cb(new Error(errStr));
		}
	});
};

/**
 * Delete rules
 * @param rules Array of rules as strings or objects in the form {value: String, tag: String}
 * @param cb Function callback
 */
LiveRules.prototype.remove = function(rules, cb) {
	var json = {
		rules : rules.map(function(rule) {
			return (typeof rule == 'string') ? {value: rule} : rule;
		})
	};
	request.del({
		url : this._api,
		json: json,
		headers : {'Authorization' : this._auth}
	}, function(err, response, body) {
		if (err) cb(err);
		else if (response.statusCode >= 200 && response.statusCode < 300) cb();
		else cb(new Error('Unable to delete rules. Request failed with status code: ' + response.statusCode));
	});
};

/**
* Replace existing tracking rules
* @param rules Array of rules as strings or objects in the form {value: String, tag: String}
* @param cb Function callback
*/
LiveRules.prototype.update = function(rules, cb) {
	var self = this;
	self.removeAll(function(err) {
		if (err) cb(err);
		else self.add(rules, cb);
	});
};

/**
* Get current tracking rules
* @param cb Function callback
*/
LiveRules.prototype.getAll = function(cb) {
	var self = this;
	request({
		url : self._api,
		headers : {'Authorization' : self._auth}
	}, function(err, response, body) {
		if (err) cb(err);
		else if (response.statusCode >= 200 && response.statusCode < 300) {
			try {
				var rules = JSON.parse(body).rules;
				cb(null, rules);
			} catch(e) {
				cb(e);
			}
		}
		else {
			cb(new Error('Unable to fetch rules. Request failed with status code: ' + response.statusCode));
		}
	});
};

/**
* Remove current tracking rules
* @param cb Function callback
*/
LiveRules.prototype.removeAll = function(cb) {
	var self = this;
	self.getAll(function(err, rules) {
		if (err) cb(err);
		else self.remove(rules, cb);
	});
};

var GnipRules = function(options) {
	this.options = _.extend({
		user : '',
		password : '',
		url : null,
		debug : false,
		batch_size: 5000
	}, options || {});

	this._api = this.options.url;
	this._auth = "Basic " + new Buffer(this.options.user + ':' + this.options.password).toString('base64');
	this._cacheFile = __dirname + '/' + crypto.createHash('md5').update(this._api).digest('hex') + '.cache';
	this.live = new LiveRules(this._api, this.options.user, this.options.password);

	if (!fs.existsSync(this._cacheFile)) {
		fs.writeFileSync(this._cacheFile, '[]', 'utf8');
	}
};

GnipRules.prototype._deleteRulesBatch = function(rules, max, cb) {
	var self = this;
	var groups = [];
	max = max || 4000;

	for (var i = 0; i < rules.length; i += max) {
		groups.push(rules.slice(i, i+max));
	}
	async.forEachSeries(groups, function(group, cb) {
		self.live.remove(group, cb);
	}, cb);
};

GnipRules.prototype._addRulesBatch = function(rules, max, cb) {
	var self = this;
	var groups = [];
	max = max || 4000;

	for (var i = 0; i < rules.length; i += max) {
		groups.push(rules.slice(i, i+max));
	}
	async.forEachSeries(groups, function(group, cb) {
		self.live.add(group, cb);
	}, cb);
};

GnipRules.prototype.getAll = function(cb) {
	fs.readFile(this._cacheFile, 'utf8', function(err, contents) {
		if (err) cb(err);
		else {
			try {
				cb(null, JSON.parse(contents));
			} catch (e) {
				cb(null, []);
			}
		}
	});
};

GnipRules.prototype.clearCache = function(cb) {
	fs.writeFile(this._cacheFile, '[]', 'utf8', cb);
};

GnipRules.prototype.update = function(rules, cb) {

	var self = this;

	rules = rules.map(function(rule) {
		return (typeof rule == 'string') ? {value: rule} : rule;
	});

	fs.readFile(self._cacheFile, 'utf8', function(err, contents) {
		if (err) cb(err);
		else {
			var currentRules;
			try {
				currentRules = JSON.parse(contents);
			} catch (e) {
				currentRules = [];
			}

			// which rules to delete?
			var rulesPlain = rules.map(function(rule) {
				return rule.value;
			});
			var deleteRules = currentRules.filter(function(rule) {
				return rulesPlain.indexOf(rule.value) == -1;
			});

			// which rules to add?
			var currentRulesPlain = currentRules.map(function(rule) {
				return rule.value;
			});
			var addRules = rules.filter(function(rule) {
				return currentRulesPlain.indexOf(rule.value) == -1;
			});

			async.series([
				function(cb) {
					self._deleteRulesBatch(deleteRules, self.options.batch_size, cb);
				},
				function(cb) {
					self._addRulesBatch(addRules, self.options.batch_size, cb);
				},
				function(cb) {
					fs.writeFile(self._cacheFile, JSON.stringify(rules), 'utf8', cb);
				}
			], function(err) {
				if (err) cb(err);
				else {
					cb(null, {
						added : addRules,
						addedCount : addRules.length,
						deleted : deleteRules,
						deletedCount : deleteRules.length
					});
				}
			});
		}
	});
}

module.exports = GnipRules;
<|endoftext|>


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

var Scope = module.exports = function(dsl, parent) {
    this.dsl = dsl;
    this.parent = parent;
    if (this.parent) {
        this.parent.children.push(this);
    }
    this.types = {};
    this.allowedProps = [];
    this.children = [];
};

Scope.prototype.error = function(path, msg) {
    this.dsl.error(path, msg);
    console.log("Path: %s; Error: %s", path, msg);
};

Scope.prototype.getType = function(type) {
    var ctor = this.getTypeOrNull(type);
    if (!ctor) {
        throw new Error("Validator type " + type + " not found");
    }
    return ctor;
};

Scope.prototype.getTypeOrNull = function(type) {
    if (this.types[type]) {
        return this.types[type];
    } else if (this.parent) {
        return this.parent.getTypeOrNull(type);
    } else {
        return null;
    }
};

Scope.prototype.hasType = function(type) {
    return this.types[type] || (this.parent && this.parent.hasType(type));
};

Scope.prototype.addType = function(type, ctor) {
    if (this.hasType(type)) {
        throw new Error("Validator type " + type + " already defined in current scope");
    } else {
        this.types[type] = ctor;
    }
};

Scope.prototype.rewriteType = function(type, ctor) {
    this.types[type] = ctor;
};

Scope.prototype.hasProperty = function(prop) {
    return (this.allowedProps.indexOf(prop) >= 0) || (this.parent && this.parent.hasProperty(prop)) || false;
};

Scope.prototype.addProperty = function(prop) {
    if (this.hasProperty(prop)) {
        throw new Error("Scope property " + prop + " already defined in current scope");
    }
    this.allowedProps.push(prop);
};

Scope.prototype.getValidator = function(props, schema) {
    if (!props.type) {
        throw new Error("Property type missing in Validator parameters");
    }

    var type = this.getType(props.type);
    props = _.defaults(props, type.props);

    _.forOwn(props, function(value, name, obj) {
        if (!this.hasProperty(name) && !type.hasProperty(name)) {
            throw new Error("Validator property " + name + " is unknown in current scope");
        }
    }, this);

    return new type(this, props, schema);
};
<commit_msg>Update logging<commit_after>var _ = require('lodash');

var Scope = module.exports = function(dsl, parent) {
    this.dsl = dsl;
    this.parent = parent;
    if (this.parent) {
        this.parent.children.push(this);
    }
    this.types = {};
    this.allowedProps = [];
    this.children = [];
};

Scope.prototype.error = function(path, msg) {
    this.dsl.error(path, msg);
    console.log("Path: %s; Error: %s", path || '', msg);
};

Scope.prototype.getType = function(type) {
    var ctor = this.getTypeOrNull(type);
    if (!ctor) {
        throw new Error("Validator type " + type + " not found");
    }
    return ctor;
};

Scope.prototype.getTypeOrNull = function(type) {
    if (this.types[type]) {
        return this.types[type];
    } else if (this.parent) {
        return this.parent.getTypeOrNull(type);
    } else {
        return null;
    }
};

Scope.prototype.hasType = function(type) {
    return this.types[type] || (this.parent && this.parent.hasType(type));
};

Scope.prototype.addType = function(type, ctor) {
    if (this.hasType(type)) {
        throw new Error("Validator type " + type + " already defined in current scope");
    } else {
        this.types[type] = ctor;
    }
};

Scope.prototype.rewriteType = function(type, ctor) {
    this.types[type] = ctor;
};

Scope.prototype.hasProperty = function(prop) {
    return (this.allowedProps.indexOf(prop) >= 0) || (this.parent && this.parent.hasProperty(prop)) || false;
};

Scope.prototype.addProperty = function(prop) {
    if (this.hasProperty(prop)) {
        throw new Error("Scope property " + prop + " already defined in current scope");
    }
    this.allowedProps.push(prop);
};

Scope.prototype.getValidator = function(props, schema) {
    if (!props.type) {
        throw new Error("Property type missing in Validator parameters");
    }

    var type = this.getType(props.type);
    props = _.defaults(props, type.props);

    _.forOwn(props, function(value, name, obj) {
        if (!this.hasProperty(name) && !type.hasProperty(name)) {
            throw new Error("Validator property " + name + " is unknown in current scope");
        }
    }, this);

    return new type(this, props, schema);
};
<|endoftext|>


<commit_before>/**
 *
 * This component is the skeleton around the actual pages, and should only
 * contain code that should be seen on all pages. (e.g. navigation bar)
 *
 */

import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { bindActionCreators, compose } from 'redux';
import 'flag-icon-css/css/flag-icon.css';
import 'react-select/dist/react-select.css';
import { Switch, Route } from 'react-router-dom';

import { pluginId } from 'app';

import injectSaga from 'utils/injectSaga';

import HomePage from 'containers/HomePage';

import { menuFetch, environmentsFetch } from './actions';
import { makeSelectLoading } from './selectors';
import styles from './styles.scss';

import saga from './sagas';

/* eslint-disable react/require-default-props  */
class App extends React.Component {
  componentDidMount() {
    this.props.menuFetch();
    this.props.environmentsFetch();
  }

  render() {
    return (
      <div className={`${pluginId} ${styles.app}`}>
        <Switch>
          <Route path="/plugins/settings-manager/:slug/:env" component={HomePage} />
          <Route path="/plugins/settings-manager/:slug" component={HomePage} />
          <Route path="/plugins/settings-manager" component={HomePage} />
        </Switch>

    );
  }
}

App.contextTypes = {
  router: PropTypes.object.isRequired,
};

App.propTypes = {
  environmentsFetch: PropTypes.func.isRequired,
  // loading: PropTypes.bool.isRequired,
  menuFetch: PropTypes.func.isRequired,
};

export function mapDispatchToProps(dispatch) {
  return bindActionCreators(
    {
      menuFetch,
      environmentsFetch,
    },
    dispatch
  );
}

const mapStateToProps = createStructuredSelector({
  loading: makeSelectLoading(),
});

// Wrap the component to inject dispatch and state into it
const withConnect = connect(mapStateToProps, mapDispatchToProps);

const withSaga = injectSaga({ key: 'global', saga });

export default compose(
  withSaga,
  withConnect,
)(App);
<commit_msg>Add 404 in settings manager<commit_after>/**
 *
 * This component is the skeleton around the actual pages, and should only
 * contain code that should be seen on all pages. (e.g. navigation bar)
 *
 */

import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { bindActionCreators, compose } from 'redux';
import 'flag-icon-css/css/flag-icon.css';
import 'react-select/dist/react-select.css';
import { Switch, Route } from 'react-router-dom';
import { isEmpty } from 'lodash';
import { pluginId } from 'app';

import injectSaga from 'utils/injectSaga';

import HomePage from 'containers/HomePage';

import { menuFetch, environmentsFetch } from './actions';
import { makeSelectLoading, makeSelectSections } from './selectors';
import styles from './styles.scss';

import saga from './sagas';

/* eslint-disable react/require-default-props  */
class App extends React.Component {
  componentDidMount() {
    this.props.menuFetch();
    this.props.environmentsFetch();
  }

  componentWillUpdate(nextProps) {
    if (!isEmpty(nextProps.sections) && nextProps.location.pathname !== '/plugins/settings-manager') {
      const allowedPath = nextProps.sections.reduce((acc, current) => {
        const slugs = current.items.reduce((acc, current) => {
          acc.push(current.slug);

          return acc;
        }, []);
        return acc.concat(slugs);
      }, []);

      const slug = nextProps.location.pathname.split('/')[3];
      const shouldRedirect = allowedPath.filter(el => el === slug).length === 0;

      if (shouldRedirect) {
        this.props.history.push('/404');
      }
    }
  }

  render() {
    return (
      <div className={`${pluginId} ${styles.app}`}>
        <Switch>
          <Route path="/plugins/settings-manager/:slug/:env" component={HomePage} />
          <Route path="/plugins/settings-manager/:slug" component={HomePage} />
          <Route path="/plugins/settings-manager" component={HomePage} />
        </Switch>

    );
  }
}

App.contextTypes = {
  router: PropTypes.object.isRequired,
};

App.propTypes = {
  environmentsFetch: PropTypes.func.isRequired,
  history: PropTypes.object.isRequired,
  menuFetch: PropTypes.func.isRequired,
  sections: PropTypes.array.isRequired,
};

export function mapDispatchToProps(dispatch) {
  return bindActionCreators(
    {
      menuFetch,
      environmentsFetch,
    },
    dispatch
  );
}

const mapStateToProps = createStructuredSelector({
  loading: makeSelectLoading(),
  sections: makeSelectSections(),
});

// Wrap the component to inject dispatch and state into it
const withConnect = connect(mapStateToProps, mapDispatchToProps);

const withSaga = injectSaga({ key: 'global', saga });

export default compose(
  withSaga,
  withConnect,
)(App);
<|endoftext|>


<commit_before>/**
 * Library methods for styles and css tasks
 *
 * @author Nikhil Modak
 */

'use strict';

var SKIN_FILES_PATTERN = '**/*(*.css|*.scss|*.sass|*.ftl|*.png|*.svg|*.jpg|*.jpeg|skin.properties)';
var lazyReq = require('lazy-req')(require);
var sass = lazyReq('gulp-sass');
var connect = lazyReq('gulp-connect');
var sourcemaps = lazyReq('gulp-sourcemaps');
var rename = lazyReq('gulp-rename');
var plumber = lazyReq('gulp-plumber');
var path = lazyReq('path');
var replace = lazyReq('gulp-replace');
var concat = lazyReq('gulp-concat');
var gulpif = lazyReq('gulp-if');
var fs = lazyReq('fs');

module.exports = function (gulp, gutil) {
  var server;

  function getServer() {
    return server ? server : server = require('./server.js')(gulp, gutil);
  }

  var cors = function (req, res, next) {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Headers', '*');
    next();
  };

  return {
    process: function (patterns, dest, files) {
      return gulp.src(patterns)
       .pipe(gutil.env.filterFiles(files))
       .pipe(gulp.dest(dest));
    },
    compile: function (lr) {
      var server = getServer();
      var version = server.localSkinCompileVersion();
      var feature = server.localSkinCompileFeature();
      var skin = server.localSkinCompileSkin();
      var dest = server.localSkinCompileDest();
      var skinPath = 'res/feature/' + feature + '/' + version + '/res/skins/' + skin + '/sass/skin.scss';
      var includePaths = [
        'res/feature/responsivepeak/' + version + '/res/skins/',
        'res/feature/responsivebase/' + version + '/res/skins/',
        'res/feature/responsivebase/common/res/skins/'
      ];

      return gulp.src(skinPath)
        .pipe(lr ? plumber()() : gutil.noop())
        .pipe(sourcemaps().init())
        .pipe(sass()({
          includePaths: includePaths
        }))
        .pipe(sourcemaps().write())
        .pipe(rename()(skin + '.css'))
        .pipe(gulp.dest(dest))
        .pipe(lr ? lr() : gutil.noop());
    },
    server: function () {
      connect().server({
        root: [
          getServer().localServerDir(),
          'web'
        ],
        port: getServer().localServerPort(),
        middleware: function () {
          return [cors];
        }
      });
    },
    vars: function () {
      var baseFeaturePath = 'res/feature/responsivebase';
      var peakFeaturePath = 'res/feature/responsivepeak';
      var baseSassPath = 'res/skins/bootstrap_base/sass';
      var peakSassPath = 'res/skins/responsive_peak/sass';
      var varsPath = 'codebook/_variables-*.scss';

      function getSkinVersions(dir) {
        return fs().readdirSync(dir)
          .filter(function(file) {
            return fs().statSync(path().join(dir, file)).isDirectory();
          });
      }

      var peakVars = getSkinVersions(peakFeaturePath).map(function(version) {
        return gulp.src([path().join(baseFeaturePath, version, '**', varsPath),
            path().join(peakFeaturePath, version, '**', varsPath)])
          .pipe(replace()('!default', ''))
          .pipe(replace()(/^\s*(\$.*)/gm, '// $1'))
          .pipe(rename()('_variables.scss'))
          .pipe(gulpif()(function(file){ return file.path.indexOf(baseFeaturePath) >= 0; },
            gulp.dest(path().join(baseFeaturePath, version, baseSassPath))))
          .pipe(concat()('_variables.scss'))
          .pipe(gulp.dest(path().join(peakFeaturePath, version, peakSassPath)));
      });
      return peakVars;
    },
    FILES_PATTERN: SKIN_FILES_PATTERN
  };
};
<commit_msg>LIA-45946 changed generated vars formatting to be closer to existing file<commit_after>/**
 * Library methods for styles and css tasks
 *
 * @author Nikhil Modak
 */

'use strict';

var SKIN_FILES_PATTERN = '**/*(*.css|*.scss|*.sass|*.ftl|*.png|*.svg|*.jpg|*.jpeg|skin.properties)';
var lazyReq = require('lazy-req')(require);
var sass = lazyReq('gulp-sass');
var connect = lazyReq('gulp-connect');
var sourcemaps = lazyReq('gulp-sourcemaps');
var rename = lazyReq('gulp-rename');
var plumber = lazyReq('gulp-plumber');
var path = lazyReq('path');
var replace = lazyReq('gulp-replace');
var concat = lazyReq('gulp-concat');
var gulpif = lazyReq('gulp-if');
var fs = lazyReq('fs');

module.exports = function (gulp, gutil) {
  var server;

  function getServer() {
    return server ? server : server = require('./server.js')(gulp, gutil);
  }

  var cors = function (req, res, next) {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Headers', '*');
    next();
  };

  return {
    process: function (patterns, dest, files) {
      return gulp.src(patterns)
       .pipe(gutil.env.filterFiles(files))
       .pipe(gulp.dest(dest));
    },
    compile: function (lr) {
      var server = getServer();
      var version = server.localSkinCompileVersion();
      var feature = server.localSkinCompileFeature();
      var skin = server.localSkinCompileSkin();
      var dest = server.localSkinCompileDest();
      var skinPath = 'res/feature/' + feature + '/' + version + '/res/skins/' + skin + '/sass/skin.scss';
      var includePaths = [
        'res/feature/responsivepeak/' + version + '/res/skins/',
        'res/feature/responsivebase/' + version + '/res/skins/',
        'res/feature/responsivebase/common/res/skins/'
      ];

      return gulp.src(skinPath)
        .pipe(lr ? plumber()() : gutil.noop())
        .pipe(sourcemaps().init())
        .pipe(sass()({
          includePaths: includePaths
        }))
        .pipe(sourcemaps().write())
        .pipe(rename()(skin + '.css'))
        .pipe(gulp.dest(dest))
        .pipe(lr ? lr() : gutil.noop());
    },
    server: function () {
      connect().server({
        root: [
          getServer().localServerDir(),
          'web'
        ],
        port: getServer().localServerPort(),
        middleware: function () {
          return [cors];
        }
      });
    },
    vars: function () {
      var baseFeaturePath = 'res/feature/responsivebase';
      var peakFeaturePath = 'res/feature/responsivepeak';
      var baseSassPath = 'res/skins/bootstrap_base/sass';
      var peakSassPath = 'res/skins/responsive_peak/sass';
      var varsPath = 'codebook/_variables-*.scss';

      function getSkinVersions(dir) {
        return fs().readdirSync(dir)
          .filter(function(file) {
            return fs().statSync(path().join(dir, file)).isDirectory();
          });
      }

      var peakVars = getSkinVersions(peakFeaturePath).map(function(version) {
        return gulp.src([path().join(baseFeaturePath, version, '**', varsPath),
            path().join(peakFeaturePath, version, '**', varsPath)])
          .pipe(replace()('!default', ''))
          .pipe(replace()(/^\s*(\$.*)/gm, '//$1'))
          .pipe(replace()(/(\s*);/gm, ';'))
          .pipe(rename()('_variables.scss'))
          .pipe(gulpif()(function(file){ return file.path.indexOf(baseFeaturePath) >= 0; },
            gulp.dest(path().join(baseFeaturePath, version, baseSassPath))))
          .pipe(concat()('_variables.scss'))
          .pipe(gulp.dest(path().join(peakFeaturePath, version, peakSassPath)));
      });
      return peakVars;
    },
    FILES_PATTERN: SKIN_FILES_PATTERN
  };
};
<|endoftext|>


<commit_before>var _ = require('underscore')
var request = require('request')
var util = require('util')
var S = require('string')

module.exports = function (config) {

  var slackApi = config.slackApi

  /**
   * Get application config from slackConfig
   * @param {Object} snsMessage
   * @return {Object}
   */
  var getApplicationConfig = function (snsMessage) {
    return _.find(config.applications, function (app) {
      return app.applicationName === snsMessage.applicationName
    })
  }

  /**
   * Get the statusData
   * @param {Object} applicationConfig
   * @param {Object} snsMessage
   * @return {Object}
   */
  var getStatusData = function (applicationConfig, status) {
    var applicationTemplate = _.find(applicationConfig.templates, function (template) {
      return template.status === status
    })

    if (!applicationTemplate) {
      console.warn(util.format('SlackLib: No application template for status: %s', status))
    }

    return {
      status: applicationTemplate.status,
      template: applicationTemplate.template
    }
  }

  /**
   * Get the hipchat room ids based on config from s3 bucket.
   * @param {Object} snsMessage
   * @return {Array}
   */
  var getChannels = function (snsMessage) {
    var applicationConfig = getApplicationConfig(snsMessage)
    return applicationConfig.channels
  }

  /**
   * Composes the notification message base on sns message data and sns status
   * @param {Object} snsMessage
   * @return {Object}
   */
  var composeMessage = function (snsMessage, revision, metaTags) {
    var applicationConfig = getApplicationConfig(snsMessage)
    var snsStatus = snsMessage.status.toLowerCase()
    var errorInformation = snsMessage.errorInformation || {
      errorMessage: 'Something went wrong with the deploy - missing error message.'
    }
    var deploymentId = snsMessage.deploymentId
    var deploymentGroupName = snsMessage.deploymentGroupName
    var deploymentUri = 'https://console.aws.amazon.com/codedeploy/home?region=us-east-1#/deployments/' + deploymentId
    var deploymentHtml = '<' + deploymentUri + '|' + deploymentId + '>'
    var applicationName = snsMessage.applicationName
    var applicationUri = 'https://console.aws.amazon.com/codedeploy/home?region=us-east-1#/applications/' + applicationName
    var applicationHtml = '<' + applicationUri + '|' + applicationName + '>'
    var statusData = getStatusData(applicationConfig, snsStatus)
    var template = statusData.template
    var msg

    var userMessage = function (metaTags) {
      var userTemplate = getStatusData(applicationConfig, 'user')

      if (metaTags.user && userTemplate) {
        return util.format(userTemplate, metaTags.user)
      }
      console.log(util.format('SlackLib: No user associated with deployment: %s', deploymentId))
      return
    }

    var combineMessages = function (msg, color, metaTags) {
      var user = userMessage(metaTags)
      if (user)
        return msg + ' ' + user
      return msg
    }

    if (!template) {
      console.log(util.format('SlackLib: No template exists for status: %s', snsStatus))
    }

    switch (snsStatus) {
      case 'created':
        msg = util.format(template, deploymentHtml, applicationHtml, revision, deploymentGroupName)
        return combineMessages(msg, 'yellow', metaTags)
      case 'succeeded':
        msg = util.format(template, deploymentHtml, applicationHtml, revision, deploymentGroupName)
        return combineMessages(msg, 'green', metaTags)
      case 'failed':
        msg = util.format(template, deploymentHtml, applicationHtml, revision, deploymentGroupName, errorInformation.errorMessage)
        return combineMessages(msg, 'red', metaTags)
      case 'stopped':
        msg = util.format(template, deploymentHtml, applicationHtml, revision, deploymentGroupName)
        return combineMessages(msg, 'gray', metaTags)
      case 'unknown':
      default:
        msg = util.format(template, deploymentHtml, applicationHtml, revision, deploymentGroupName, snsStatus)
        return combineMessages(msg, 'random', metaTags)
    }
  }

  /**
   * Sends a notification to the slack api.
   * @param {Object} snsMessage
   */
  var postMessage = function (snsMessage, revision, metaTags) {
    var cm = composeMessage(snsMessage, revision, metaTags)
    var channels = getChannels(snsMessage)
    var applicationConfig = getApplicationConfig(snsMessage)
    var statusData = getStatusData(applicationConfig, snsMessage.status.toLowerCase())
    var promises = []

    _.each(channels, function (c) {
      var params = {
        channel: c.channel,
        text: cm,
        username: c.username,
        icon_emoji: c.icon_emoji
      }

      promises.push(new Promise(function (resolve, reject) {
        request({
          url: slackApi.webhook,
          method: 'POST',
          json: true,
          body: params
        }, function (err, response, body) {
          console.log('body', body)
          if (err) return reject(err)
          if (!S(response.statusCode).startsWith(20)) {
            console.warn(util.format('SlackLib: Slack api returned http status code of: %s, %s', response.statusCode, body))
            reject(response)
          }
          try {
            console.log('SlackLib: Slack api reponse', body)
            return resolve(body)
          } catch (e) {
            return reject(e)
          }
        })
      }))
    })
    console.log(promises)

    return promises
  }

  /**
   * Expose API
   */
  return {
    postMessage: postMessage
  }
}
<commit_msg>Adding slack support via webhooks.<commit_after>var _ = require('underscore')
var request = require('request')
var util = require('util')
var S = require('string')

module.exports = function (config) {

  var slackApi = config.slackApi

  /**
   * Get application config from slackConfig
   * @param {Object} snsMessage
   * @return {Object}
   */
  var getApplicationConfig = function (snsMessage) {
    return _.find(config.applications, function (app) {
      return app.applicationName === snsMessage.applicationName
    })
  }

  /**
   * Get the statusData
   * @param {Object} applicationConfig
   * @param {Object} snsMessage
   * @return {Object}
   */
  var getStatusData = function (applicationConfig, status) {
    var applicationTemplate = _.find(applicationConfig.templates, function (template) {
      return template.status === status
    })

    if (!applicationTemplate) {
      console.warn(util.format('SlackLib: No application template for status: %s', status))
    }

    return {
      status: applicationTemplate.status,
      template: applicationTemplate.template
    }
  }

  /**
   * Get the hipchat room ids based on config from s3 bucket.
   * @param {Object} snsMessage
   * @return {Array}
   */
  var getChannels = function (snsMessage) {
    var applicationConfig = getApplicationConfig(snsMessage)
    return applicationConfig.channels
  }

  /**
   * Composes the notification message base on sns message data and sns status
   * @param {Object} snsMessage
   * @return {Object}
   */
  var composeMessage = function (snsMessage, revision, metaTags) {
    var applicationConfig = getApplicationConfig(snsMessage)
    var snsStatus = snsMessage.status.toLowerCase()
    var errorInformation = snsMessage.errorInformation || {
      errorMessage: 'Something went wrong with the deploy - missing error message.'
    }
    var deploymentId = snsMessage.deploymentId
    var deploymentGroupName = snsMessage.deploymentGroupName
    var deploymentUri = 'https://console.aws.amazon.com/codedeploy/home?region=us-east-1#/deployments/' + deploymentId
    var deploymentHtml = '<' + deploymentUri + '|' + deploymentId + '>'
    var applicationName = snsMessage.applicationName
    var applicationUri = 'https://console.aws.amazon.com/codedeploy/home?region=us-east-1#/applications/' + applicationName
    var applicationHtml = '<' + applicationUri + '|' + applicationName + '>'
    var statusData = getStatusData(applicationConfig, snsStatus)
    var template = statusData.template
    var msg

    var userMessage = function (metaTags) {
      var userTemplate = getStatusData(applicationConfig, 'user')

      if (metaTags.user && userTemplate) {
        return util.format(userTemplate, metaTags.user)
      }
      console.log(util.format('SlackLib: No user associated with deployment: %s', deploymentId))
      return
    }

    var combineMessages = function (msg, color, metaTags) {
      var user = userMessage(metaTags)
      if (user)
        return msg + ' ' + user
      return msg
    }

    if (!template) {
      console.log(util.format('SlackLib: No template exists for status: %s', snsStatus))
    }

    switch (snsStatus) {
      case 'created':
        msg = util.format(template, deploymentHtml, applicationHtml, revision, deploymentGroupName)
        return combineMessages(msg, 'yellow', metaTags)
      case 'succeeded':
        msg = util.format(template, deploymentHtml, applicationHtml, revision, deploymentGroupName)
        return combineMessages(msg, 'green', metaTags)
      case 'failed':
        msg = util.format(template, deploymentHtml, applicationHtml, revision, deploymentGroupName, errorInformation.errorMessage)
        return combineMessages(msg, 'red', metaTags)
      case 'stopped':
        msg = util.format(template, deploymentHtml, applicationHtml, revision, deploymentGroupName)
        return combineMessages(msg, 'gray', metaTags)
      case 'unknown':
      default:
        msg = util.format(template, deploymentHtml, applicationHtml, revision, deploymentGroupName, snsStatus)
        return combineMessages(msg, 'random', metaTags)
    }
  }

  /**
   * Sends a notification to the slack api.
   * @param {Object} snsMessage
   */
  var postMessage = function (snsMessage, revision, metaTags) {
    var cm = composeMessage(snsMessage, revision, metaTags)
    var channels = getChannels(snsMessage)
    var applicationConfig = getApplicationConfig(snsMessage)
    var statusData = getStatusData(applicationConfig, snsMessage.status.toLowerCase())
    var promises = []

    _.each(channels, function (c) {
      var params = {
        channel: c.channel,
        text: cm,
        username: c.username,
        icon_emoji: c.icon_emoji
      }

      promises.push(new Promise(function (resolve, reject) {
        request({
          url: slackApi.webhook,
          method: 'POST',
          json: true,
          body: params
        }, function (err, response, body) {
          console.log('body', body)
          if (err) return reject(err)
          if (!S(response.statusCode).startsWith(20)) {
            console.warn(util.format('SlackLib: Slack api returned http status code of: %s, %s', response.statusCode, body))
            reject(response)
          }
          try {
            console.log('SlackLib: Slack api reponse', body)
            return resolve(body)
          } catch (e) {
            return reject(e)
          }
        })
      }))
    })

    return promises
  }

  /**
   * Expose API
   */
  return {
    postMessage: postMessage
  }
}
<|endoftext|>