<commit_before>'use strict';

var express = require('express'),
    authentication = require('../util/authentication'),
    router = express.Router(),
    async = require('async'),
    applicationIdRoute = '/:applicationId',
    unselectedFields = '-__v',
    removeFields = ['__v'];

/**
 * @api {get} /applications Returns all the registered applications.
 * @apiName GetApplications
 * @apiGroup Applications
 *
 * @apiParam {String} [fields] The fields to be populated in the resulting objects.
 *                              An empty string will return the complete document.
 * @apiParam {String} [sort=_id] Place - before the field for a descending sort.
 * @apiParam {Number} [limit=20]
 * @apiParam {Number} [page=1]
 *
 * @apiParamExample {json} Request-Example:
 *      {
 *          "fields": "",
 *          "sort": "name",
 *          "limit": 20,
 *          "page": 1
 *      }
 *
 * @apiSuccess(200) Success.
 *
 * @apiSuccessExample Success-Response:
 *      HTTP/1.1 200 OK
 *      {
 *          "data": [
 *          {
 *              "_id": "559a447831b7acec185bf513",
 *              "name": "Gleaner App.",
 *              "prefix": "gleaner",
 *              "host": "localhost:3300",
 *              "anonymous": [],
 *              "timeCreated": "2015-07-06T09:03:52.636Z"
 *          }],
 *          "pages": {
 *              "current": 1,
 *              "prev": 0,
 *              "hasPrev": false,
 *              "next": 2,
 *              "hasNext": false,
 *              "total": 1
 *         },
 *          "items": {
 *              "limit": 20,
 *              "begin": 1,
 *              "end": 1,
 *              "total": 1
 *          }
 *      }
 *
 */
router.get('/', authentication.authorized, function (req, res, next) {

    var query = {};
    var fields = req.body.fields || '';
    var sort = req.body.sort || '_id';
    var limit = req.body.limit || 20;
    var page = req.body.page || 1;

    req.app.db.model('application').pagedFind(query, fields, removeFields, sort, limit, page, function (err, results) {

        if (err) {
            return next(err);
        }

        res.json(results);
    });
});

/**
 * @api {post} /applications Register a new application.
 * @apiName postApplications
 * @apiGroup Applications
 *
 * @apiParam {String} prefix Application prefix.
 * @apiParam {String} host Application host.
 * @apiParam {Array<String>} anonymous Express-like routes for whom unidentified (anonymous) requests will be forwarded anyway.
 *
 * @apiParamExample {json} Request-Example:
 *      {
 *          "prefix" : "gleaner",
 *          "host" : "localhost:3300"
 *      }
 *
 * @apiSuccess(200) Success.
 *
 * @apiSuccessExample Success-Response:
 *      HTTP/1.1 200 OK
 *      {
 *          "_id": "559a447831b7acec185bf513",
 *          "prefix": "gleaner",
 *          "host": "localhost:3300",
 *          "anonymous": [],
 *          "timeCreated": "2015-07-06T09:03:52.636Z"
 *      }
 *
 * @apiError(400) PrefixRequired Prefix required!.
 *
 * @apiError(400) HostRequired Host required!.
 *
 */
router.post('/', authentication.authorized, function (req, res, next) {

    async.auto({
        validate: function (done) {
            var err;
            if (!req.body.prefix) {
                err = new Error('Prefix required!');
                return done(err);
            }

            if (!req.body.host) {
                err = new Error('Host required!');
                return done(err);
            }

            done();
        },
        roles: ['validate', function (done) {
            var rolesArray = req.body.roles;
            var routes = [];
            if (rolesArray) {
                rolesArray.forEach(function (role) {
                    role.allows.forEach(function (allow) {
                        var resources = allow.resources;
                        for (var i = 0; i < resources.length; i++) {
                            resources[i] = req.body.prefix + resources[i];
                            routes.push(resources[i]);
                        }
                    });
                });
                req.app.acl.allow(rolesArray, function (err) {
                    if (err) {
                        return done(err);
                    }
                    return done(null, routes);
                });
            } else {
                done(null, routes);
            }
        }],
        application: ['roles', function (done, results) {
            var ApplicationModel = req.app.db.model('application');
            ApplicationModel.create({
                name: req.body.name || '',
                prefix: req.body.prefix,
                host: req.body.host,
                anonymous: req.body.anonymous || [],
                routes: results.roles
            }, done);
        }],
    }, function (err, results) {
        if (err) {
            err.status = 400;
            return next(err);
        }

        var application = results.application;
        res.json({
            _id: application._id,
            prefix: application.prefix,
            host: application.host,
            timeCreated: application.timeCreated,
            anonymous: application.anonymous
        });
    });
});

/**
 * @api {get} /applications/:applicationId Gets the application information.
 * @apiName GetApplication
 * @apiGroup Applications
 *
 * @apiParam {String} applicationId ApplicationId id.
 *
 * @apiSuccess(200) Success.
 *
 * @apiSuccessExample Success-Response:
 *      HTTP/1.1 200 OK
 *      {
 *          "_id": "559a447831b7acec185bf513",
 *          "name": "My App Name",
 *          "prefix": "gleaner",
 *          "host": "localhost:3300",
 *          "anonymous": [],
 *          "timeCreated": "2015-07-06T09:03:52.636Z"
 *      }
 *
 * @apiError(400) ApplicationNotFound No application with the given user id exists.
 *
 */
router.get(applicationIdRoute, authentication.authorized, function (req, res, next) {
    var applicationId = req.params.applicationId || '';
    req.app.db.model('application').findById(applicationId).select(unselectedFields).exec(function (err, application) {
        if (err) {
            return next(err);
        }
        if (!application) {
            err = new Error('No application with the given application id exists.');
            err.status = 400;
            return next(err);
        }
        res.json(application);
    });
});

function isArray(obj) {
    return Object.prototype.toString.call(obj) === '[object Array]';
}

/**
 * @api {put} /applications/:applicationId Changes the application name.
 * @apiName PutApplication
 * @apiGroup Applications
 *
 * @apiParam {String} applicationId ApplicationId id.
 * @apiParam {String} name The new name.
 * @apiParam {String} prefix Application prefix.
 * @apiParam {String} host Application host.
 * @apiParam {Array<String>} anonymous Express-like routes for whom unidentified (anonymous) requests will be forwarded anyway.
 *                                      The routes from this array will be added only if they're not present yet.
 *
 * @apiParamExample {json} Request-Example:
 *      {
 *          "name": "Gleaner App."
 *      }
 *
 * @apiSuccess(200) Success.
 *
 * @apiSuccessExample Success-Response:
 *      HTTP/1.1 200 OK
 *      {
 *          "_id": "559a447831b7acec185bf513",
 *          "name": "Gleaner App.",
 *          "prefix": "gleaner",
 *          "host": "localhost:3300",
 *          "anonymous": [],
 *          "timeCreated": "2015-07-06T09:03:52.636Z"
 *      }
 *
 * @apiError(400) ApplicationNotFound No application with the given application id exists.
 *
 */
router.put(applicationIdRoute, authentication.authorized, function (req, res, next) {

    if (!req.params.applicationId) {
        var err = new Error('You must provide a valid application id');
        err.status = 400;
        return next(err);
    }

    var applicationId = req.params.applicationId || '';
    var query = {
        _id: applicationId
    };
    var update = {
        $set: {}
    };

    if (req.body.name) {
        update.$set.name = req.body.name;
    }
    if (req.body.prefix) {
        update.$set.prefix = req.body.prefix;
    }
    if (req.body.host) {
        update.$set.host = req.body.host;
    }
    if(isArray(req.body.anonymous)) {
        update.$addToSet = { anonymous: { $each: req.body.anonymous.filter(Boolean) } };
    }

    var options = {
        new: true,
        /*
         Since Mongoose 4.0.0 we can run validators
         (e.g. isURL validator for the host attribute of ApplicationSchema --> /schema/application)
         when performing updates with the following option.
         More info. can be found here http://mongoosejs.com/docs/validation.html
         */
        runValidators: true
    };

    req.app.db.model('application').findOneAndUpdate(query, update, options).select(unselectedFields).exec(function (err, application) {
        if (err) {
            err.status = 403;
            return next(err);
        }
        if (!application) {
            err = new Error('No application with the given application id exists.');
            err.status = 400;
            return next(err);
        }
        res.json(application);
    });
});

/**
 * @api {delete} /applications/:applicationId Removes the application.
 * @apiName DeleteApplication
 * @apiGroup Applications
 *
 * @apiParam {String} applicationId ApplicationId id.
 *
 * @apiSuccess(200) Success.
 *
 * @apiSuccessExample Success-Response:
 *      HTTP/1.1 200 OK
 *      {
 *          "message": "Success."
 *      }
 *
 * @apiError(400) ApplicationNotFound No application with the given application id exists.
 *
 */
router.delete(applicationIdRoute, authentication.authorized, function (req, res, next) {

    var applicationId = req.params.applicationId || '';

    var query = {
        _id: applicationId
    };

    req.app.db.model('application').findOneAndRemove(query, function (err, user) {
        if (err) {
            return next(err);
        }
        if (!user) {
            err = new Error('No application with the given application id exists.');
            err.status = 400;
            return next(err);
        }

        res.sendDefaultSuccessMessage();
    });
});

module.exports = router;<commit_msg>Fixed api documentation<commit_after>'use strict';

var express = require('express'),
    authentication = require('../util/authentication'),
    router = express.Router(),
    async = require('async'),
    applicationIdRoute = '/:applicationId',
    unselectedFields = '-__v',
    removeFields = ['__v'];

/**
 * @api {get} /applications Returns all the registered applications.
 * @apiName GetApplications
 * @apiGroup Applications
 *
 * @apiParam {String} [fields] The fields to be populated in the resulting objects.
 *                              An empty string will return the complete document.
 * @apiParam {String} [sort=_id] Place - before the field for a descending sort.
 * @apiParam {Number} [limit=20]
 * @apiParam {Number} [page=1]
 *
 * @apiParamExample {json} Request-Example:
 *      {
 *          "fields": "",
 *          "sort": "name",
 *          "limit": 20,
 *          "page": 1
 *      }
 *
 * @apiSuccess(200) Success.
 *
 * @apiSuccessExample Success-Response:
 *      HTTP/1.1 200 OK
 *      {
 *          "data": [
 *          {
 *              "_id": "559a447831b7acec185bf513",
 *              "name": "Gleaner App.",
 *              "prefix": "gleaner",
 *              "host": "localhost:3300",
 *              "anonymous": [],
 *              "timeCreated": "2015-07-06T09:03:52.636Z"
 *          }],
 *          "pages": {
 *              "current": 1,
 *              "prev": 0,
 *              "hasPrev": false,
 *              "next": 2,
 *              "hasNext": false,
 *              "total": 1
 *         },
 *          "items": {
 *              "limit": 20,
 *              "begin": 1,
 *              "end": 1,
 *              "total": 1
 *          }
 *      }
 *
 */
router.get('/', authentication.authorized, function (req, res, next) {

    var query = {};
    var fields = req.body.fields || '';
    var sort = req.body.sort || '_id';
    var limit = req.body.limit || 20;
    var page = req.body.page || 1;

    req.app.db.model('application').pagedFind(query, fields, removeFields, sort, limit, page, function (err, results) {

        if (err) {
            return next(err);
        }

        res.json(results);
    });
});

/**
 * @api {post} /applications Register a new application.
 * @apiName postApplications
 * @apiGroup Applications
 *
 * @apiParam {String} prefix Application prefix.
 * @apiParam {String} host Application host.
 * @apiParam {String[]} anonymous Express-like routes for whom unidentified (anonymous) requests will be forwarded anyway.
 *
 * @apiParamExample {json} Request-Example:
 *      {
 *          "prefix" : "gleaner",
 *          "host" : "localhost:3300"
 *      }
 *
 * @apiSuccess(200) Success.
 *
 * @apiSuccessExample Success-Response:
 *      HTTP/1.1 200 OK
 *      {
 *          "_id": "559a447831b7acec185bf513",
 *          "prefix": "gleaner",
 *          "host": "localhost:3300",
 *          "anonymous": [],
 *          "timeCreated": "2015-07-06T09:03:52.636Z"
 *      }
 *
 * @apiError(400) PrefixRequired Prefix required!.
 *
 * @apiError(400) HostRequired Host required!.
 *
 */
router.post('/', authentication.authorized, function (req, res, next) {

    async.auto({
        validate: function (done) {
            var err;
            if (!req.body.prefix) {
                err = new Error('Prefix required!');
                return done(err);
            }

            if (!req.body.host) {
                err = new Error('Host required!');
                return done(err);
            }

            done();
        },
        roles: ['validate', function (done) {
            var rolesArray = req.body.roles;
            var routes = [];
            if (rolesArray) {
                rolesArray.forEach(function (role) {
                    role.allows.forEach(function (allow) {
                        var resources = allow.resources;
                        for (var i = 0; i < resources.length; i++) {
                            resources[i] = req.body.prefix + resources[i];
                            routes.push(resources[i]);
                        }
                    });
                });
                req.app.acl.allow(rolesArray, function (err) {
                    if (err) {
                        return done(err);
                    }
                    return done(null, routes);
                });
            } else {
                done(null, routes);
            }
        }],
        application: ['roles', function (done, results) {
            var ApplicationModel = req.app.db.model('application');
            ApplicationModel.create({
                name: req.body.name || '',
                prefix: req.body.prefix,
                host: req.body.host,
                anonymous: req.body.anonymous || [],
                routes: results.roles
            }, done);
        }],
    }, function (err, results) {
        if (err) {
            err.status = 400;
            return next(err);
        }

        var application = results.application;
        res.json({
            _id: application._id,
            prefix: application.prefix,
            host: application.host,
            timeCreated: application.timeCreated,
            anonymous: application.anonymous
        });
    });
});

/**
 * @api {get} /applications/:applicationId Gets the application information.
 * @apiName GetApplication
 * @apiGroup Applications
 *
 * @apiParam {String} applicationId Application id.
 *
 * @apiSuccess(200) Success.
 *
 * @apiSuccessExample Success-Response:
 *      HTTP/1.1 200 OK
 *      {
 *          "_id": "559a447831b7acec185bf513",
 *          "name": "My App Name",
 *          "prefix": "gleaner",
 *          "host": "localhost:3300",
 *          "anonymous": [],
 *          "timeCreated": "2015-07-06T09:03:52.636Z"
 *      }
 *
 * @apiError(400) ApplicationNotFound No application with the given user id exists.
 *
 */
router.get(applicationIdRoute, authentication.authorized, function (req, res, next) {
    var applicationId = req.params.applicationId || '';
    req.app.db.model('application').findById(applicationId).select(unselectedFields).exec(function (err, application) {
        if (err) {
            return next(err);
        }
        if (!application) {
            err = new Error('No application with the given application id exists.');
            err.status = 400;
            return next(err);
        }
        res.json(application);
    });
});

function isArray(obj) {
    return Object.prototype.toString.call(obj) === '[object Array]';
}

/**
 * @api {put} /applications/:applicationId Changes the application name.
 * @apiName PutApplication
 * @apiGroup Applications
 *
 * @apiParam {String} applicationId ApplicationId id.
 * @apiParam {String} name The new name.
 * @apiParam {String} prefix Application prefix.
 * @apiParam {String} host Application host.
 * @apiParam {String[]} anonymous Express-like routes for whom unidentified (anonymous) requests will be forwarded anyway.
 *                                      The routes from this array will be added only if they're not present yet.
 *
 * @apiParamExample {json} Request-Example:
 *      {
 *          "name": "Gleaner App."
 *      }
 *
 * @apiSuccess(200) Success.
 *
 * @apiSuccessExample Success-Response:
 *      HTTP/1.1 200 OK
 *      {
 *          "_id": "559a447831b7acec185bf513",
 *          "name": "Gleaner App.",
 *          "prefix": "gleaner",
 *          "host": "localhost:3300",
 *          "anonymous": [],
 *          "timeCreated": "2015-07-06T09:03:52.636Z"
 *      }
 *
 * @apiError(400) ApplicationNotFound No application with the given application id exists.
 *
 */
router.put(applicationIdRoute, authentication.authorized, function (req, res, next) {

    if (!req.params.applicationId) {
        var err = new Error('You must provide a valid application id');
        err.status = 400;
        return next(err);
    }

    var applicationId = req.params.applicationId || '';
    var query = {
        _id: applicationId
    };
    var update = {
        $set: {}
    };

    if (req.body.name) {
        update.$set.name = req.body.name;
    }
    if (req.body.prefix) {
        update.$set.prefix = req.body.prefix;
    }
    if (req.body.host) {
        update.$set.host = req.body.host;
    }
    if (isArray(req.body.anonymous)) {
        update.$addToSet = {anonymous: {$each: req.body.anonymous.filter(Boolean)}};
    }

    var options = {
        new: true,
        /*
         Since Mongoose 4.0.0 we can run validators
         (e.g. isURL validator for the host attribute of ApplicationSchema --> /schema/application)
         when performing updates with the following option.
         More info. can be found here http://mongoosejs.com/docs/validation.html
         */
        runValidators: true
    };

    req.app.db.model('application').findOneAndUpdate(query, update, options).select(unselectedFields).exec(function (err, application) {
        if (err) {
            err.status = 403;
            return next(err);
        }
        if (!application) {
            err = new Error('No application with the given application id exists.');
            err.status = 400;
            return next(err);
        }
        res.json(application);
    });
});

/**
 * @api {delete} /applications/:applicationId Removes the application.
 * @apiName DeleteApplication
 * @apiGroup Applications
 *
 * @apiParam {String} applicationId ApplicationId id.
 *
 * @apiSuccess(200) Success.
 *
 * @apiSuccessExample Success-Response:
 *      HTTP/1.1 200 OK
 *      {
 *          "message": "Success."
 *      }
 *
 * @apiError(400) ApplicationNotFound No application with the given application id exists.
 *
 */
router.delete(applicationIdRoute, authentication.authorized, function (req, res, next) {

    var applicationId = req.params.applicationId || '';

    var query = {
        _id: applicationId
    };

    req.app.db.model('application').findOneAndRemove(query, function (err, user) {
        if (err) {
            return next(err);
        }
        if (!user) {
            err = new Error('No application with the given application id exists.');
            err.status = 400;
            return next(err);
        }

        res.sendDefaultSuccessMessage();
    });
});

module.exports = router;<|endoftext|>


<commit_before>#!/usr/bin/env node
// -*- mode: js -*-
// Copyright (c) 2013, Joyent, Inc. All rights reserved.

var assert = require('assert-plus');
var bunyan = require('bunyan');
var common = require('../lib/common');
var fs = require('fs');
var getopt = require('posix-getopt');
var lib = require('../lib');
var manta = require('manta');
var moray = require('moray');
var path = require('path');

///--- Global Objects

var NAME = 'mola-rebalance';
var LOG = bunyan.createLogger({
        level: (process.env.LOG_LEVEL || 'info'),
        name: NAME,
        stream: process.stdout
});
var MOLA_REBALANCE_CONFIG = (process.env.MOLA_REBALANCE_CONFIG ||
                   '/opt/smartdc/mola/etc/config.json');
var MOLA_REBALANCE_CONFIG_OBJ = JSON.parse(
        fs.readFileSync(MOLA_REBALANCE_CONFIG));
var MANTA_CLIENT = manta.createClientFromFileSync(MOLA_REBALANCE_CONFIG, LOG);
var MANTA_USER = MANTA_CLIENT.user;
var MORAY_BUCKET = 'manta_storage';
var MORAY_CONNECT_TIMEOUT = 1000;
var MORAY_PORT = 2020;

///--- Global Constants
var MP = '/' + MANTA_USER + '/stor';
var MANTA_DUMP_NAME_PREFIX = 'manta-';
var SHARKS_ASSET_FILE = '/var/tmp/mola-rebalance-sharks.json';

///--- Helpers

/* BEGIN JSSTYLED */
function getEnvCommon(opts) {
        return (' \
set -o pipefail && \
cd /assets/ && gtar -xzf ' + opts.marlinPathToAsset + ' && cd mola && \
');
}
/* END JSSTYLED */

/* BEGIN JSSTYLED */
/**
 * The map phase takes the pgdump, filters out all the directories
 * and formats them like:
 * [objectid] { [json pg row data] }
 */
function getMapCmd(opts) {
        return (getEnvCommon(opts) + ' \
zcat | ./build/node/bin/node ./bin/pg_transform.js | \
   ./build/node/bin/node ./bin/jext.js -f objectid -x | \
   msplit -d " " -f 1 -n ' + opts.numberReducers + ' \
');
}
/* END JSSTYLED */

/* BEGIN JSSTYLED */
function getRebalanceCmd(opts) {
        var sharksAsset = '/assets' + opts.sharksAssetObject;
        var tmpDir = '/var/tmp/sharkDist';
        var hostOption = '';
        if (opts.host) {
                hostOption = '-h ' + opts.host + ' ';
        }
        return (getEnvCommon(opts) + ' \
mkdir ' + tmpDir + ' && \
sort | ./build/node/bin/node ./bin/jext.js -r | \
    ./build/node/bin/node ./bin/rebalance.js \
       -s ' + sharksAsset + ' -d ' + tmpDir + ' ' + hostOption + '&& \
for i in $(ls ' + tmpDir + '); do \
   mmkdir ' + opts.jobRoot + '/do/$i; \
   mput -f ' + tmpDir + '/$i ' + opts.jobRoot + '/do/$i/$MANTA_JOB_ID$(echo $MANTA_INPUT_OBJECT | tr "/" "_"); \
done \
');
}
/* END JSSTYLED */

function parseOptions() {
        var option;
        //First take what's in the config file, override what's on the
        // command line, and use the defaults if all else fails.
        var opts = MOLA_REBALANCE_CONFIG_OBJ;
        opts.shards = opts.shards || [];
        opts.ignoreSharks = [];
        var parser = new getopt.BasicParser('a:h:i:m:nr:s:t',
                                            process.argv);
        while ((option = parser.getopt()) !== undefined && !option.error) {
                switch (option.option) {
                case 'a':
                        opts.assetFile = option.optarg;
                        break;
                case 'h':
                        opts.host = option.optarg;
                        break;
                case 'i':
                        opts.ignoreSharks.push(option.optarg);
                        break;
                case 'm':
                        opts.shards.push(option.optarg);
                        break;
                case 'n':
                        opts.noJobStart = true;
                        break;
                case 'r':
                        opts.marlinMemory = parseInt(option.optarg, 10);
                        break;
                case 's':
                        opts.storageShard = option.optarg;
                        break;
                case 't':
                        opts.jobName = 'manta_rebalance_test';
                        opts.jobRoot = MP + '/manta_rebalance_test';
                        break;
                default:
                        usage('Unknown option: ' + option.option);
                        break;
                }
        }

        if (!opts.storageShard) {
                usage('Storage shard is required.');
        }

        //Set up some defaults...
        opts.jobName = opts.jobName || 'manta_rebalance';
        opts.jobRoot = opts.jobRoot || MP + '/manta_rebalance';
        opts.directories = [
                opts.jobRoot + '/do'
        ];
        opts.assetDir = opts.jobRoot + '/assets';
        opts.assetObject = opts.assetDir + '/mola.tar.gz';
        opts.sharksAssetObject = opts.assetDir + '/sharks.json';
        opts.assetFile = opts.assetFile ||
                '/opt/smartdc/common/bundle/mola.tar.gz';

        opts.marlinMemory = opts.marlinMemory || 4096;
        opts.marlinPathToAsset = opts.assetObject.substring(1);
        opts.marlinAssetObject = opts.assetObject;

        return (opts);
}

function usage(msg) {
        if (msg) {
                console.error(msg);
        }
        var str  = 'usage: ' + path.basename(process.argv[1]);
        str += ' [-a asset_object]';
        str += ' [-h manta_storage_id]';
        str += ' [-m moray_shard]';
        str += ' [-n no_job_start]';
        str += ' [-r marlin_memory]';
        str += ' [-s storage_shard]';
        str += ' [-t output_to_test]';
        console.error(str);
        process.exit(1);
}

//Pulls the current set of active sharks and uploads to manta.  I probably
// should have the job manager do this, but whatever.
function makeSharksAsset(opts, cb) {
        getCurrentSharks(opts, function (err, sharks) {
                if (err) {
                        cb(err);
                        return;
                }
                if (Object.keys(sharks).length < 1) {
                        var message = 'no active sharks found.';
                        cb(new Error(message));
                        return;
                }
                //TODO: Could just use an in-memory object...
                fs.writeFileSync(SHARKS_ASSET_FILE, JSON.stringify(sharks));
                var stats = fs.statSync(SHARKS_ASSET_FILE);
                var o = {
                        copies: 2,
                        size: stats.size
                };

                var s = fs.createReadStream(SHARKS_ASSET_FILE);
                var p = opts.sharksAssetObject;
                s.pause();
                s.on('open', function () {
                        MANTA_CLIENT.put(p, s, o, function (e) {
                                fs.unlinkSync(SHARKS_ASSET_FILE);
                                cb(e);
                        });
                });
        });
}

function getRebalanceJob(opts, cb) {
        makeSharksAsset(_opts, function (err, sharks) {
                //As a first pass, the number of reducers should be the same
                // as the number of pg shards we have.
                opts.numberReducers = opts.objects.length;

                if (err) {
                        finish(err);
                        return;
                }

                var job = {
                        phases: [ {
                                type: 'storage-map',
                                memory: opts.marlinMemory,
                                exec: getMapCmd(opts)
                        }, {
                                type: 'reduce',
                                count: opts.numberReducers,
                                memory: opts.marlinMemory,
                                exec: getRebalanceCmd(opts),
                                assets: [ opts.sharksAssetObject ]
                        } ]
                };

                LOG.info({ job: job }, 'Rebalance Marlin Job Definition');

                cb(null, job);
        });
}

function findObjects(opts, cb) {
        common.findTablesForShards({
                mantaUser: MANTA_USER,
                mantaClient: MANTA_CLIENT,
                shards: opts.shards,
                tablePrefixes: [ MANTA_DUMP_NAME_PREFIX ]
        }, function (err, objs) {
                if (err) {
                        cb(err);
                        return;
                }
                cb(null, objs);
        });
}

function getCurrentSharks(opts, cb) {
        var client = moray.createClient({
                log: LOG,
                connectTimeout: MORAY_CONNECT_TIMEOUT,
                host: opts.storageShard,
                port: MORAY_PORT
        });

        client.on('connect', function () {
                var sharks = {};
                var req = client.findObjects(MORAY_BUCKET,
                                             '(manta_storage_id=*)', {});

                req.once('error', function (err) {
                        cb(err);
                        return;
                });

                req.on('record', function (obj) {
                        var dc = obj.value.datacenter;
                        var mantaStorageId = obj.value.manta_storage_id;
                        //Filter out host if we're migrating away from it.
                        if (mantaStorageId === opts.host) {
                                return;
                        }
                        //Filter out other ignore hosts
                        if (opts.ignoreSharks.indexOf(mantaStorageId) !== -1) {
                                return;
                        }
                        if (!sharks[dc]) {
                                sharks[dc] = [];
                        }
                        sharks[dc].push({
                                'manta_storage_id': mantaStorageId,
                                'datacenter': dc
                        });
                });

                req.once('end', function () {
                        client.close();
                        cb(null, sharks);
                });
        });
}

///--- Main

var _opts = parseOptions();

_opts.getJobDefinition = getRebalanceJob;
_opts.getJobObjects = findObjects;

function finish(err) {
        if (err) {
                LOG.fatal(err);
                process.exit(1);
        }
        MANTA_CLIENT.close();
        LOG.info('Done for now.');
        process.exit(0);
}

var jobManager = lib.createJobManager(_opts, MANTA_CLIENT, LOG);
jobManager.run(function () {
        finish();
});
<commit_msg>MANTA-1947: Marlin job for rebalancing, link awareness (fixing reducer output locations).<commit_after>#!/usr/bin/env node
// -*- mode: js -*-
// Copyright (c) 2013, Joyent, Inc. All rights reserved.

var assert = require('assert-plus');
var bunyan = require('bunyan');
var common = require('../lib/common');
var fs = require('fs');
var getopt = require('posix-getopt');
var lib = require('../lib');
var manta = require('manta');
var moray = require('moray');
var path = require('path');

///--- Global Objects

var NAME = 'mola-rebalance';
var LOG = bunyan.createLogger({
        level: (process.env.LOG_LEVEL || 'info'),
        name: NAME,
        stream: process.stdout
});
var MOLA_REBALANCE_CONFIG = (process.env.MOLA_REBALANCE_CONFIG ||
                   '/opt/smartdc/mola/etc/config.json');
var MOLA_REBALANCE_CONFIG_OBJ = JSON.parse(
        fs.readFileSync(MOLA_REBALANCE_CONFIG));
var MANTA_CLIENT = manta.createClientFromFileSync(MOLA_REBALANCE_CONFIG, LOG);
var MANTA_USER = MANTA_CLIENT.user;
var MORAY_BUCKET = 'manta_storage';
var MORAY_CONNECT_TIMEOUT = 1000;
var MORAY_PORT = 2020;

///--- Global Constants
var MP = '/' + MANTA_USER + '/stor';
var MANTA_DUMP_NAME_PREFIX = 'manta-';
var SHARKS_ASSET_FILE = '/var/tmp/mola-rebalance-sharks.json';

///--- Helpers

/* BEGIN JSSTYLED */
function getEnvCommon(opts) {
        return (' \
set -o pipefail && \
cd /assets/ && gtar -xzf ' + opts.marlinPathToAsset + ' && cd mola && \
');
}
/* END JSSTYLED */

/* BEGIN JSSTYLED */
/**
 * The map phase takes the pgdump, filters out all the directories
 * and formats them like:
 * [objectid] { [json pg row data] }
 */
function getMapCmd(opts) {
        return (getEnvCommon(opts) + ' \
zcat | ./build/node/bin/node ./bin/pg_transform.js | \
   ./build/node/bin/node ./bin/jext.js -f objectid -x | \
   msplit -d " " -f 1 -n ' + opts.numberReducers + ' \
');
}
/* END JSSTYLED */

/* BEGIN JSSTYLED */
function getRebalanceCmd(opts) {
        var sharksAsset = '/assets' + opts.sharksAssetObject;
        var tmpDir = '/var/tmp/sharkDist';
        var hostOption = '';
        if (opts.host) {
                hostOption = '-h ' + opts.host + ' ';
        }
        return (getEnvCommon(opts) + ' \
mkdir ' + tmpDir + ' && \
sort | ./build/node/bin/node ./bin/jext.js -r | \
    ./build/node/bin/node ./bin/rebalance.js \
       -s ' + sharksAsset + ' -d ' + tmpDir + ' ' + hostOption + '&& \
for i in $(ls ' + tmpDir + '); do \
   mmkdir ' + opts.jobRoot + '/do/$i; \
   mput -f ' + tmpDir + '/$i \
     ' + opts.jobRoot + '/do/$i/$MANTA_JOB_ID-X-$(uuid); \
done \
');
}
/* END JSSTYLED */

function parseOptions() {
        var option;
        //First take what's in the config file, override what's on the
        // command line, and use the defaults if all else fails.
        var opts = MOLA_REBALANCE_CONFIG_OBJ;
        opts.shards = opts.shards || [];
        opts.ignoreSharks = [];
        var parser = new getopt.BasicParser('a:h:i:m:nr:s:t',
                                            process.argv);
        while ((option = parser.getopt()) !== undefined && !option.error) {
                switch (option.option) {
                case 'a':
                        opts.assetFile = option.optarg;
                        break;
                case 'h':
                        opts.host = option.optarg;
                        break;
                case 'i':
                        opts.ignoreSharks.push(option.optarg);
                        break;
                case 'm':
                        opts.shards.push(option.optarg);
                        break;
                case 'n':
                        opts.noJobStart = true;
                        break;
                case 'r':
                        opts.marlinMemory = parseInt(option.optarg, 10);
                        break;
                case 's':
                        opts.storageShard = option.optarg;
                        break;
                case 't':
                        opts.jobName = 'manta_rebalance_test';
                        opts.jobRoot = MP + '/manta_rebalance_test';
                        break;
                default:
                        usage('Unknown option: ' + option.option);
                        break;
                }
        }

        if (!opts.storageShard) {
                usage('Storage shard is required.');
        }

        //Set up some defaults...
        opts.jobName = opts.jobName || 'manta_rebalance';
        opts.jobRoot = opts.jobRoot || MP + '/manta_rebalance';
        opts.directories = [
                opts.jobRoot + '/do'
        ];
        opts.assetDir = opts.jobRoot + '/assets';
        opts.assetObject = opts.assetDir + '/mola.tar.gz';
        opts.sharksAssetObject = opts.assetDir + '/sharks.json';
        opts.assetFile = opts.assetFile ||
                '/opt/smartdc/common/bundle/mola.tar.gz';

        opts.marlinMemory = opts.marlinMemory || 4096;
        opts.marlinPathToAsset = opts.assetObject.substring(1);
        opts.marlinAssetObject = opts.assetObject;

        return (opts);
}

function usage(msg) {
        if (msg) {
                console.error(msg);
        }
        var str  = 'usage: ' + path.basename(process.argv[1]);
        str += ' [-a asset_object]';
        str += ' [-h manta_storage_id]';
        str += ' [-m moray_shard]';
        str += ' [-n no_job_start]';
        str += ' [-r marlin_memory]';
        str += ' [-s storage_shard]';
        str += ' [-t output_to_test]';
        console.error(str);
        process.exit(1);
}

//Pulls the current set of active sharks and uploads to manta.  I probably
// should have the job manager do this, but whatever.
function makeSharksAsset(opts, cb) {
        getCurrentSharks(opts, function (err, sharks) {
                if (err) {
                        cb(err);
                        return;
                }
                if (Object.keys(sharks).length < 1) {
                        var message = 'no active sharks found.';
                        cb(new Error(message));
                        return;
                }
                //TODO: Could just use an in-memory object...
                fs.writeFileSync(SHARKS_ASSET_FILE, JSON.stringify(sharks));
                var stats = fs.statSync(SHARKS_ASSET_FILE);
                var o = {
                        copies: 2,
                        size: stats.size
                };

                var s = fs.createReadStream(SHARKS_ASSET_FILE);
                var p = opts.sharksAssetObject;
                s.pause();
                s.on('open', function () {
                        MANTA_CLIENT.put(p, s, o, function (e) {
                                fs.unlinkSync(SHARKS_ASSET_FILE);
                                cb(e);
                        });
                });
        });
}

function getRebalanceJob(opts, cb) {
        makeSharksAsset(_opts, function (err, sharks) {
                //As a first pass, the number of reducers should be the same
                // as the number of pg shards we have.
                opts.numberReducers = opts.objects.length;

                if (err) {
                        finish(err);
                        return;
                }

                var job = {
                        phases: [ {
                                type: 'storage-map',
                                memory: opts.marlinMemory,
                                exec: getMapCmd(opts)
                        }, {
                                type: 'reduce',
                                count: opts.numberReducers,
                                memory: opts.marlinMemory,
                                exec: getRebalanceCmd(opts),
                                assets: [ opts.sharksAssetObject ]
                        } ]
                };

                LOG.info({ job: job }, 'Rebalance Marlin Job Definition');

                cb(null, job);
        });
}

function findObjects(opts, cb) {
        common.findTablesForShards({
                mantaUser: MANTA_USER,
                mantaClient: MANTA_CLIENT,
                shards: opts.shards,
                tablePrefixes: [ MANTA_DUMP_NAME_PREFIX ]
        }, function (err, objs) {
                if (err) {
                        cb(err);
                        return;
                }
                cb(null, objs);
        });
}

function getCurrentSharks(opts, cb) {
        var client = moray.createClient({
                log: LOG,
                connectTimeout: MORAY_CONNECT_TIMEOUT,
                host: opts.storageShard,
                port: MORAY_PORT
        });

        client.on('connect', function () {
                var sharks = {};
                var req = client.findObjects(MORAY_BUCKET,
                                             '(manta_storage_id=*)', {});

                req.once('error', function (err) {
                        cb(err);
                        return;
                });

                req.on('record', function (obj) {
                        var dc = obj.value.datacenter;
                        var mantaStorageId = obj.value.manta_storage_id;
                        //Filter out host if we're migrating away from it.
                        if (mantaStorageId === opts.host) {
                                return;
                        }
                        //Filter out other ignore hosts
                        if (opts.ignoreSharks.indexOf(mantaStorageId) !== -1) {
                                return;
                        }
                        if (!sharks[dc]) {
                                sharks[dc] = [];
                        }
                        sharks[dc].push({
                                'manta_storage_id': mantaStorageId,
                                'datacenter': dc
                        });
                });

                req.once('end', function () {
                        client.close();
                        cb(null, sharks);
                });
        });
}

///--- Main

var _opts = parseOptions();

_opts.getJobDefinition = getRebalanceJob;
_opts.getJobObjects = findObjects;

function finish(err) {
        if (err) {
                LOG.fatal(err);
                process.exit(1);
        }
        MANTA_CLIENT.close();
        LOG.info('Done for now.');
        process.exit(0);
}

var jobManager = lib.createJobManager(_opts, MANTA_CLIENT, LOG);
jobManager.run(function () {
        finish();
});
<|endoftext|>


<commit_before>var keystone = require('keystone'),
	async = require('async'),
	request = require('request'),
	_ = require('underscore'),
	User = keystone.list('User');

exports = module.exports = function(req, res) {

	var view = new keystone.View(req, res),
		locals = res.locals;

	locals.section = 'profile';
	locals.form = req.body;
	locals.returnto = req.query.returnto;

	locals.authUser = req.session.auth;
	locals.existingUser = false;

	// Reject request if no auth data is stored in session
	if (!req.session.auth) {
		console.log('[auth.confirm] - No auth data detected, redirecting to signin.');
		console.log('------------------------------------------------------------');
		return res.redirect('/login');
	}

	// Set existing user if already logged in
	if (req.user) {
		locals.existingUser = req.user;
	}

	// Function to handle signin
	var doSignIn = function() {

		console.log('[auth.confirm] - Signing in user...');
		console.log('------------------------------------------------------------');

		var onSuccess = function(user) {
			console.log('[auth.confirm] - Successfully signed in.');
			console.log('------------------------------------------------------------');
			return res.redirect('/settings');
		}

		var onFail = function(err) {
			console.log('[auth.confirm] - Failed signing in.', err);
			console.log('------------------------------------------------------------');
			req.flash('error', 'Sorry, there was an issue signing you in, please try again.');
			return res.redirect('/login');
		}

		keystone.session.signin(locals.existingUser.id, req, res, onSuccess, onFail);

	}

	// Function to handle data confirmation process
	var checkAuth = function() {

		async.series([

			// Check for user by profile id (only if not signed in)
			function(next) {

				if (locals.existingUser) return next();

				console.log('[auth.confirm] - Searching for existing users via [' + req.session.auth.type + '] profile id...');
				console.log('------------------------------------------------------------');

				var query = User.model.findOne();
					query.where('services.' + req.session.auth.type + '.profileId', req.session.auth.profileId);
					query.exec(function(err, user) {
						if (err) {
							console.log('[auth.confirm] - Error finding existing user via profile id.', err);
							console.log('------------------------------------------------------------');
							return next(err);
						}
						if (user) {
							console.log('[auth.confirm] - Found existing user via [' + req.session.auth.type + '] profile id...');
							console.log('------------------------------------------------------------');
							locals.existingUser = user;
							return doSignIn();
						}
						return next();
					});

			},

			// Check for user by email (only if not signed in)
			function(next) {

				if (locals.existingUser) return next();

				console.log('[auth.confirm] - Searching for existing users via [' + req.session.auth.email + '] email address...');
				console.log('------------------------------------------------------------');

				var query = User.model.findOne();
					query.where('email', req.body.email);
					query.exec(function(err, user) {
						if (err) {
							console.log('[auth.confirm] - Error finding existing user via email.', err);
							console.log('------------------------------------------------------------');
							return next(err);
						}
						if (user) {
							console.log('[auth.confirm] - Found existing user via email address...');
							console.log('------------------------------------------------------------');
							locals.existingUser = user;
						}
						return next();
					});

			},

			// Create or update user
			function(next) {

				if (locals.existingUser) {

					console.log('[auth.confirm] - Existing user found, updating...');
					console.log('------------------------------------------------------------');

					var userData = {
						state: 'enabled',

						website: req.body.website,

						isVerified: true,

						services: locals.existingUser.services || {}
					};

					_.extend(userData.services[req.session.auth.type], {
						isConfigured: true,

						profileId: req.session.auth.profileId,

						username: req.session.auth.username,
						accessToken: req.session.auth.accessToken,
						refreshToken: req.session.auth.refreshToken
					});

					// console.log('[auth.confirm] - Existing user data:', userData);

					locals.existingUser.set(userData);

					locals.existingUser.save(function(err) {
						if (err) {
							console.log('[auth.confirm] - Error saving existing user.', err);
							console.log('------------------------------------------------------------');
							return next(err);
						}
						console.log('[auth.confirm] - Saved existing user.');
						console.log('------------------------------------------------------------');
						return next();
					});

				} else {

					console.log('[auth.confirm] - Creating new user...');
					console.log('------------------------------------------------------------');

					var userData = {
						name: {
							first: req.body['name.first'],
							last: req.body['name.last']
						},
						email: req.body.email,

						state: 'enabled',

						website: req.body.website,

						isVerified: true,

						services: {}
					};

					userData.services[req.session.auth.type] = {
						isConfigured: true,

						profileId: req.session.auth.profileId,

						username: req.session.auth.username,
						accessToken: req.session.auth.accessToken,
						refreshToken: req.session.auth.refreshToken
					}

					// console.log('[auth.confirm] - New user data:', userData );

					locals.existingUser = new User.model(userData);

					locals.existingUser.save(function(err) {
						if (err) {
							console.log('[auth.confirm] - Error saving new user.', err);
							console.log('------------------------------------------------------------');
							return next(err);
						}
						console.log('[auth.confirm] - Saved new user.');
						console.log('------------------------------------------------------------');
						return next();
					});

				}

			},

			// Session
			function() {
				if (req.user) {
					console.log('[auth.confirm] - Already signed in, skipping sign in.');
					console.log('------------------------------------------------------------');
					return res.redirect('/settings');
					return next();
				}
				return doSignIn();
			}

		], function(err) {
			if (err) {
				req.flash('error', 'Sorry, there was an issue signing you in, please try again.');
				return res.redirect('/login');
			}
		});

	}

	// Retrieve additional data to assist registration (email)
	view.on('render', function(next) {

		if (req.session.auth.type != 'github') return next();

		console.log('[auth.confirm] - Finding GitHub email addresses...');
		console.log('------------------------------------------------------------');

		request({
			url: 'https://api.github.com/user/emails?access_token=' + req.session.auth.accessToken,
			headers: {
				'User-Agent': 'forums.keystonejs.com'
			}
		}, function(err, data) {

			if (err) {
				console.log(err);
				console.log('[auth.confirm] - Error retrieving GitHub email addresses.');
				console.log('------------------------------------------------------------');
				return next();

			} else {

				console.log('[auth.confirm] - Retrieved GitHub email addresses...');
				console.log('------------------------------------------------------------');

				var emails = JSON.parse(data.body);

				if (emails.length) {
					_.each(emails, function(e) {
						if (!e.primary) return;
						req.session.auth.email = e.email;
						return next();
					});
				} else {
					return next();
				}

			}

		});

	});

	view.on('init', function(next) {
		if (req.user) return checkAuth();
		return next();
	});

	view.on('post', { action: 'confirm.details' }, function(next) {
		if (!req.body['name.first'] || !req.body['name.last'] || !req.body.email) {
			req.flash('error', 'Please enter a name & email.');
			return next();
		}
		return checkAuth();
	});

	view.render('auth/confirm');

}
<commit_msg>Adding error logging.<commit_after>var keystone = require('keystone'),
	async = require('async'),
	request = require('request'),
	_ = require('underscore'),
	User = keystone.list('User');

exports = module.exports = function(req, res) {

	var view = new keystone.View(req, res),
		locals = res.locals;

	locals.section = 'profile';
	locals.form = req.body;
	locals.returnto = req.query.returnto;

	locals.authUser = req.session.auth;
	locals.existingUser = false;

	// Reject request if no auth data is stored in session
	if (!req.session.auth) {
		console.log('[auth.confirm] - No auth data detected, redirecting to signin.');
		console.log('------------------------------------------------------------');
		return res.redirect('/login');
	}

	// Set existing user if already logged in
	if (req.user) {
		locals.existingUser = req.user;
	}

	// Function to handle signin
	var doSignIn = function() {

		console.log('[auth.confirm] - Signing in user...');
		console.log('------------------------------------------------------------');

		var onSuccess = function(user) {
			console.log('[auth.confirm] - Successfully signed in.');
			console.log('------------------------------------------------------------');
			return res.redirect('/settings');
		}

		var onFail = function(err) {
			console.log('[auth.confirm] - Failed signing in.', err);
			console.log('------------------------------------------------------------');
			req.flash('error', 'Sorry, there was an issue signing you in, please try again.');
			return res.redirect('/login');
		}

		console.log(locals.existingUser);

		keystone.session.signin(locals.existingUser.id, req, res, onSuccess, onFail);

	}

	// Function to handle data confirmation process
	var checkAuth = function() {

		async.series([

			// Check for user by profile id (only if not signed in)
			function(next) {

				if (locals.existingUser) return next();

				console.log('[auth.confirm] - Searching for existing users via [' + req.session.auth.type + '] profile id...');
				console.log('------------------------------------------------------------');

				var query = User.model.findOne();
					query.where('services.' + req.session.auth.type + '.profileId', req.session.auth.profileId);
					query.exec(function(err, user) {
						if (err) {
							console.log('[auth.confirm] - Error finding existing user via profile id.', err);
							console.log('------------------------------------------------------------');
							return next(err);
						}
						if (user) {
							console.log('[auth.confirm] - Found existing user via [' + req.session.auth.type + '] profile id...');
							console.log('------------------------------------------------------------');
							locals.existingUser = user;
							return doSignIn();
						}
						return next();
					});

			},

			// Check for user by email (only if not signed in)
			function(next) {

				if (locals.existingUser) return next();

				console.log('[auth.confirm] - Searching for existing users via [' + req.session.auth.email + '] email address...');
				console.log('------------------------------------------------------------');

				var query = User.model.findOne();
					query.where('email', req.body.email);
					query.exec(function(err, user) {
						if (err) {
							console.log('[auth.confirm] - Error finding existing user via email.', err);
							console.log('------------------------------------------------------------');
							return next(err);
						}
						if (user) {
							console.log('[auth.confirm] - Found existing user via email address...');
							console.log('------------------------------------------------------------');
							locals.existingUser = user;
						}
						return next();
					});

			},

			// Create or update user
			function(next) {

				if (locals.existingUser) {

					console.log('[auth.confirm] - Existing user found, updating...');
					console.log('------------------------------------------------------------');

					var userData = {
						state: 'enabled',

						website: req.body.website,

						isVerified: true,

						services: locals.existingUser.services || {}
					};

					_.extend(userData.services[req.session.auth.type], {
						isConfigured: true,

						profileId: req.session.auth.profileId,

						username: req.session.auth.username,
						accessToken: req.session.auth.accessToken,
						refreshToken: req.session.auth.refreshToken
					});

					// console.log('[auth.confirm] - Existing user data:', userData);

					locals.existingUser.set(userData);

					locals.existingUser.save(function(err) {
						if (err) {
							console.log('[auth.confirm] - Error saving existing user.', err);
							console.log('------------------------------------------------------------');
							return next(err);
						}
						console.log('[auth.confirm] - Saved existing user.');
						console.log('------------------------------------------------------------');
						return next();
					});

				} else {

					console.log('[auth.confirm] - Creating new user...');
					console.log('------------------------------------------------------------');

					var userData = {
						name: {
							first: req.body['name.first'],
							last: req.body['name.last']
						},
						email: req.body.email,

						state: 'enabled',

						website: req.body.website,

						isVerified: true,

						services: {}
					};

					userData.services[req.session.auth.type] = {
						isConfigured: true,

						profileId: req.session.auth.profileId,

						username: req.session.auth.username,
						accessToken: req.session.auth.accessToken,
						refreshToken: req.session.auth.refreshToken
					}

					// console.log('[auth.confirm] - New user data:', userData );

					locals.existingUser = new User.model(userData);

					locals.existingUser.save(function(err) {
						if (err) {
							console.log('[auth.confirm] - Error saving new user.', err);
							console.log('------------------------------------------------------------');
							return next(err);
						}
						console.log('[auth.confirm] - Saved new user.');
						console.log('------------------------------------------------------------');
						return next();
					});

				}

			},

			// Session
			function() {
				if (req.user) {
					console.log('[auth.confirm] - Already signed in, skipping sign in.');
					console.log('------------------------------------------------------------');
					return res.redirect('/settings');
					return next();
				}
				return doSignIn();
			}

		], function(err) {
			if (err) {
				req.flash('error', 'Sorry, there was an issue signing you in, please try again.');
				return res.redirect('/login');
			}
		});

	}

	// Retrieve additional data to assist registration (email)
	view.on('render', function(next) {

		if (req.session.auth.type != 'github') return next();

		console.log('[auth.confirm] - Finding GitHub email addresses...');
		console.log('------------------------------------------------------------');

		request({
			url: 'https://api.github.com/user/emails?access_token=' + req.session.auth.accessToken,
			headers: {
				'User-Agent': 'forums.keystonejs.com'
			}
		}, function(err, data) {

			if (err) {
				console.log(err);
				console.log('[auth.confirm] - Error retrieving GitHub email addresses.');
				console.log('------------------------------------------------------------');
				return next();

			} else {

				console.log('[auth.confirm] - Retrieved GitHub email addresses...');
				console.log('------------------------------------------------------------');

				var emails = JSON.parse(data.body);

				if (emails.length) {
					_.each(emails, function(e) {
						if (!e.primary) return;
						req.session.auth.email = e.email;
						return next();
					});
				} else {
					return next();
				}

			}

		});

	});

	view.on('init', function(next) {
		if (req.user) return checkAuth();
		return next();
	});

	view.on('post', { action: 'confirm.details' }, function(next) {
		if (!req.body['name.first'] || !req.body['name.last'] || !req.body.email) {
			req.flash('error', 'Please enter a name & email.');
			return next();
		}
		return checkAuth();
	});

	view.render('auth/confirm');

}
<|endoftext|>


<commit_before>/* eslint-disable react/forbid-prop-types */
/* eslint-disable import/prefer-default-export */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { View } from 'react-native';

import { MODAL_KEYS } from '../utilities';

import { DataTablePageModal } from '../widgets/modals';
import { DataTable, DataTableHeaderRow, DataTableRow } from '../widgets/DataTable';
import { DataTablePageView, PageButton, PageInfo, SearchBar } from '../widgets';

import { getItemLayout } from './dataTableUtilities';

import { usePageReducer, useRecordListener } from '../hooks';

import globalStyles, { newPageStyles } from '../globalStyles';
import { buttonStrings } from '../localization';

/**
 * Renders a mSupply mobile page with a customer requisition loaded for editing
 *
 * State:
 * Uses a reducer to manage state with `backingData` being a realm results
 * of items to display. `data` is a plain JS array of realm objects. data is
 * hydrated from the `backingData` for displaying in the interface.
 * i.e: When filtering, data is populated from filtered items of `backingData`.
 *
 * dataState is a simple map of objects corresponding to a row being displayed,
 * holding the state of a given row. Each object has the shape :
 * { isSelected, isDisabled },
 *
 * @prop {Object} requisition The realm transaction object for this invoice.
 * @prop {Func}   runWithLoadingIndicator Callback for displaying a fullscreen spinner.
 * @prop {String} routeName The current route name for the top of the navigation stack.
 */
export const CustomerRequisitionPage = ({ requisition, runWithLoadingIndicator, routeName }) => {
  const initialState = { page: routeName, pageObject: requisition };
  const [state, dispatch, instantDebouncedDispatch] = usePageReducer(initialState);

  const {
    data,
    dataState,
    sortBy,
    isAscending,
    modalKey,
    pageObject,
    keyExtractor,
    modalValue,
    searchTerm,
    PageActions,
    columns,
    getPageInfoColumns,
  } = state;

  // Listen for changes to this pages requisition. Refreshing data on side effects i.e. finalizing.
  useRecordListener(() => dispatch(PageActions.refreshData()), requisition, 'Requisition');

  const { isFinalised, comment } = pageObject;

  // On click handlers
  const onCloseModal = () => dispatch(PageActions.closeModal());
  const onAddItem = value => dispatch(PageActions.addRequisitionItem(value));
  const onEditComment = value => dispatch(PageActions.editComment(value, 'Requisition'));
  const onFilterData = value => dispatch(PageActions.filterData(value));

  const onSetSuppliedToRequested = () =>
    runWithLoadingIndicator(() => dispatch(PageActions.setSuppliedToRequested()));
  const onSetSuppliedToSuggested = () =>
    runWithLoadingIndicator(() => dispatch(PageActions.setSuppliedToSuggested()));

  const renderPageInfo = useCallback(
    () => (
      <PageInfo
        columns={getPageInfoColumns(pageObject, dispatch, PageActions)}
        isEditingDisabled={isFinalised}
      />
    ),
    [comment, isFinalised]
  );

  const getAction = colKey => {
    switch (colKey) {
      case 'suppliedQuantity':
        return PageActions.editSuppliedQuantity;
      default:
        return null;
    }
  };

  const getModalOnSelect = () => {
    switch (modalKey) {
      case MODAL_KEYS.SELECT_ITEM:
        return onAddItem;
      case MODAL_KEYS.REQUISITION_COMMENT_EDIT:
        return onEditComment;
      default:
        return null;
    }
  };

  const renderRow = useCallback(
    listItem => {
      const { item, index } = listItem;
      const rowKey = keyExtractor(item);
      return (
        <DataTableRow
          rowData={data[index]}
          rowKey={rowKey}
          columns={columns}
          isFinalised={isFinalised}
          dispatch={dispatch}
          getAction={getAction}
          rowIndex={index}
        />
      );
    },
    [data, dataState]
  );

  const renderHeader = useCallback(
    () => (
      <DataTableHeaderRow
        columns={columns}
        dispatch={instantDebouncedDispatch}
        sortAction={PageActions.sortData}
        isAscending={isAscending}
        sortBy={sortBy}
      />
    ),
    [sortBy, isAscending]
  );

  const UseSuggestedQuantitiesButton = () => (
    <View>
      <PageButton
        style={globalStyles.topButton}
        text={buttonStrings.use_suggested_quantities}
        onPress={onSetSuppliedToSuggested}
        isDisabled={isFinalised}
      />
    </View>
  );

  const UseRequestedQuantitiesButton = () => (
    <PageButton
      style={globalStyles.topButton}
      text={buttonStrings.use_requested_quantities}
      onPress={onSetSuppliedToRequested}
      isDisabled={requisition.isFinalised}
    />
  );

  const PageButtons = useCallback(() => {
    const { verticalContainer } = globalStyles;

    return (
      <View style={verticalContainer}>
        <UseRequestedQuantitiesButton />
        <UseSuggestedQuantitiesButton />
      </View>
    );
  }, []);

  const {
    newPageTopSectionContainer,
    newPageTopLeftSectionContainer,
    newPageTopRightSectionContainer,
  } = newPageStyles;
  return (
    <DataTablePageView>
      <View style={newPageTopSectionContainer}>
        <View style={newPageTopLeftSectionContainer}>
          {renderPageInfo()}
          <SearchBar onChangeText={onFilterData} value={searchTerm} />
        </View>
        <View style={newPageTopRightSectionContainer}>
          <PageButtons />
        </View>
      </View>
      <DataTable
        data={data}
        renderRow={renderRow}
        renderHeader={renderHeader}
        keyExtractor={keyExtractor}
        getItemLayout={getItemLayout}
        columns={columns}
      />
      <DataTablePageModal
        fullScreen={false}
        isOpen={!!modalKey}
        modalKey={modalKey}
        onClose={onCloseModal}
        onSelect={getModalOnSelect()}
        dispatch={dispatch}
        currentValue={modalValue}
      />
    </DataTablePageView>
  );
};

CustomerRequisitionPage.propTypes = {
  runWithLoadingIndicator: PropTypes.func.isRequired,
  requisition: PropTypes.object.isRequired,
  routeName: PropTypes.string.isRequired,
};
<commit_msg>ADD update to CustomerRequisitionPage buttons<commit_after>/* eslint-disable react/forbid-prop-types */
/* eslint-disable import/prefer-default-export */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { View } from 'react-native';

import { MODAL_KEYS } from '../utilities';

import { DataTablePageModal } from '../widgets/modals';
import { DataTable, DataTableHeaderRow, DataTableRow } from '../widgets/DataTable';
import { DataTablePageView, PageButton, PageInfo, SearchBar } from '../widgets';

import { getItemLayout } from './dataTableUtilities';

import { usePageReducer, useRecordListener } from '../hooks';

import globalStyles, { newPageStyles } from '../globalStyles';
import { buttonStrings } from '../localization';

/**
 * Renders a mSupply mobile page with a customer requisition loaded for editing
 *
 * State:
 * Uses a reducer to manage state with `backingData` being a realm results
 * of items to display. `data` is a plain JS array of realm objects. data is
 * hydrated from the `backingData` for displaying in the interface.
 * i.e: When filtering, data is populated from filtered items of `backingData`.
 *
 * dataState is a simple map of objects corresponding to a row being displayed,
 * holding the state of a given row. Each object has the shape :
 * { isSelected, isDisabled },
 *
 * @prop {Object} requisition The realm transaction object for this invoice.
 * @prop {Func}   runWithLoadingIndicator Callback for displaying a fullscreen spinner.
 * @prop {String} routeName The current route name for the top of the navigation stack.
 */
export const CustomerRequisitionPage = ({ requisition, runWithLoadingIndicator, routeName }) => {
  const initialState = { page: routeName, pageObject: requisition };
  const [state, dispatch, instantDebouncedDispatch] = usePageReducer(initialState);

  const {
    data,
    dataState,
    sortBy,
    isAscending,
    modalKey,
    pageObject,
    keyExtractor,
    modalValue,
    searchTerm,
    PageActions,
    columns,
    getPageInfoColumns,
  } = state;

  // Listen for changes to this pages requisition. Refreshing data on side effects i.e. finalizing.
  useRecordListener(() => dispatch(PageActions.refreshData()), requisition, 'Requisition');

  const { isFinalised, comment } = pageObject;

  // On click handlers
  const onCloseModal = () => dispatch(PageActions.closeModal());
  const onAddItem = value => dispatch(PageActions.addRequisitionItem(value));
  const onEditComment = value => dispatch(PageActions.editComment(value, 'Requisition'));
  const onFilterData = value => dispatch(PageActions.filterData(value));

  const onSetSuppliedToRequested = () =>
    runWithLoadingIndicator(() => dispatch(PageActions.setSuppliedToRequested()));
  const onSetSuppliedToSuggested = () =>
    runWithLoadingIndicator(() => dispatch(PageActions.setSuppliedToSuggested()));

  const renderPageInfo = useCallback(
    () => (
      <PageInfo
        columns={getPageInfoColumns(pageObject, dispatch, PageActions)}
        isEditingDisabled={isFinalised}
      />
    ),
    [comment, isFinalised]
  );

  const getAction = colKey => {
    switch (colKey) {
      case 'suppliedQuantity':
        return PageActions.editSuppliedQuantity;
      default:
        return null;
    }
  };

  const getModalOnSelect = () => {
    switch (modalKey) {
      case MODAL_KEYS.SELECT_ITEM:
        return onAddItem;
      case MODAL_KEYS.REQUISITION_COMMENT_EDIT:
        return onEditComment;
      default:
        return null;
    }
  };

  const renderRow = useCallback(
    listItem => {
      const { item, index } = listItem;
      const rowKey = keyExtractor(item);
      return (
        <DataTableRow
          rowData={data[index]}
          rowKey={rowKey}
          columns={columns}
          isFinalised={isFinalised}
          dispatch={dispatch}
          getAction={getAction}
          rowIndex={index}
        />
      );
    },
    [data, dataState]
  );

  const renderHeader = useCallback(
    () => (
      <DataTableHeaderRow
        columns={columns}
        dispatch={instantDebouncedDispatch}
        sortAction={PageActions.sortData}
        isAscending={isAscending}
        sortBy={sortBy}
      />
    ),
    [sortBy, isAscending]
  );

  const {
    newPageTopSectionContainer,
    newPageTopLeftSectionContainer,
    newPageTopRightSectionContainer,
  } = newPageStyles;
  return (
    <DataTablePageView>
      <View style={newPageTopSectionContainer}>
        <View style={newPageTopLeftSectionContainer}>
          {renderPageInfo()}
          <SearchBar onChangeText={onFilterData} value={searchTerm} />
        </View>
        <View style={newPageTopRightSectionContainer}>
          <PageButton
            style={globalStyles.topButton}
            text={buttonStrings.use_requested_quantities}
            onPress={onSetSuppliedToRequested}
            isDisabled={requisition.isFinalised}
          />
          <PageButton
            style={globalStyles.topButton}
            text={buttonStrings.use_suggested_quantities}
            onPress={onSetSuppliedToSuggested}
            isDisabled={isFinalised}
          />
        </View>
      </View>
      <DataTable
        data={data}
        renderRow={renderRow}
        renderHeader={renderHeader}
        keyExtractor={keyExtractor}
        getItemLayout={getItemLayout}
        columns={columns}
      />
      <DataTablePageModal
        fullScreen={false}
        isOpen={!!modalKey}
        modalKey={modalKey}
        onClose={onCloseModal}
        onSelect={getModalOnSelect()}
        dispatch={dispatch}
        currentValue={modalValue}
      />
    </DataTablePageView>
  );
};

CustomerRequisitionPage.propTypes = {
  runWithLoadingIndicator: PropTypes.func.isRequired,
  requisition: PropTypes.object.isRequired,
  routeName: PropTypes.string.isRequired,
};
<|endoftext|>


<commit_before>let fields = ['engine', 'booster', 'troll', 'ipBan', 'reportban', 'rankban']
let selector = [];
let projection = {};
for (field of fields) {
  selector[field] = true;
  let sel = {};
  sel[field] = true;
  selector.push(sel);
  projection[field] = true;
}
printjson(selector);
printjson(projection);

db.user4.find({
  $or: selector,
  marks: {$exists: false}
},projection).forEach(u => {
  let marks = [];
  for(field of fields) {
    if (u[field]) marks.push(field.toLowerCase());
  }
  db.user4.update({_id:u._id},{$set:{marks:marks}});
});
<commit_msg>remove one-time migration script<commit_after><|endoftext|>


<commit_before>import React from 'react';

import { Aelexe, Zerotorescue, Sharrq, Matardarix } from 'CONTRIBUTORS';
import SPELLS from 'common/SPELLS';
import SpellLink from 'common/SpellLink';

export default [
  {
    date: new Date('2018-11-06'),
    changes: <>Added cooldown tracker tab</>,
    contributors: [Matardarix],
  },
  {
    date: new Date('2018-11-05'),
    changes: <>New:

Checklist

Talents module

<SpellLink id={SPELLS.EXECUTIONERS_PRECISION_TRAIT.id} /> module

<SpellLink id={SPELLS.SEISMIC_WAVE.id} /> module

<SpellLink id={SPELLS.TEST_OF_MIGHT.id} /> module

rage usage module

suggestions regarding <SpellLink id={SPELLS.DEFENSIVE_STANCE_TALENT.id} />

Fixed:

<SpellLink id={SPELLS.ANGER_MANAGEMENT_TALENT.id} /> cooldown reduction calculation

</>,
    contributors: [Matardarix],
  },
  {
    date: new Date('2018-10-12'),
    changes: <>Fixed some spell IDs and ability information. Updated Config.</>,
    contributors: [Sharrq],
  },
  {
    date: new Date('2018-06-30'),
    changes: <>Update all abilities to new BFA values, removed incompatible modules and added an <SpellLink id={SPELLS.ANGER_MANAGEMENT_TALENT.id} /> statistic.</>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2018-06-16'),
    changes: <>Fixed a rare crash when casting <SpellLink id={SPELLS.EXECUTE.id} /> on a non-boss target.</>,
    contributors: [Aelexe],
  },
];
<commit_msg>Changelog entry<commit_after>import React from 'react';

import { Aelexe, Zerotorescue, Sharrq, Matardarix } from 'CONTRIBUTORS';
import SPELLS from 'common/SPELLS';
import SpellLink from 'common/SpellLink';

export default [
  {
    date: new Data('2018-11-07'),
    changes: <>Added Rage usage tab and suggestions on rage wast, removed the doughnut chart for rage usage</>,
    contributors: [Matardarix],
  },
  {
    date: new Date('2018-11-06'),
    changes: <>Added cooldown tracker tab</>,
    contributors: [Matardarix],
  },
  {
    date: new Date('2018-11-05'),
    changes: <>New:

Checklist

Talents module

<SpellLink id={SPELLS.EXECUTIONERS_PRECISION_TRAIT.id} /> module

<SpellLink id={SPELLS.SEISMIC_WAVE.id} /> module

<SpellLink id={SPELLS.TEST_OF_MIGHT.id} /> module

rage usage module

suggestions regarding <SpellLink id={SPELLS.DEFENSIVE_STANCE_TALENT.id} />

Fixed:

<SpellLink id={SPELLS.ANGER_MANAGEMENT_TALENT.id} /> cooldown reduction calculation

</>,
    contributors: [Matardarix],
  },
  {
    date: new Date('2018-10-12'),
    changes: <>Fixed some spell IDs and ability information. Updated Config.</>,
    contributors: [Sharrq],
  },
  {
    date: new Date('2018-06-30'),
    changes: <>Update all abilities to new BFA values, removed incompatible modules and added an <SpellLink id={SPELLS.ANGER_MANAGEMENT_TALENT.id} /> statistic.</>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2018-06-16'),
    changes: <>Fixed a rare crash when casting <SpellLink id={SPELLS.EXECUTE.id} /> on a non-boss target.</>,
    contributors: [Aelexe],
  },
];
<|endoftext|>


<commit_before>var app = angular.module('routing', []);
var hash_params = L.Hash.parseHash(location.hash);
var mode_mapping = { 'foot' : 'pedestrian', 'car' : 'auto', 'bicycle' : 'bicycle', 'transit' : 'multimodal'};
var date = new Date();
var isoDateTime = date.toISOString();  //"2015-06-12T15:28:46.493Z"
var serviceUrl;
var envToken;
var envServer;

function selectEnv(){
	$( "option:selected" ).each(function() {
	  envServer = $( this ).text();
	  serviceUrl =  document.getElementById(envServer).value;
	  getEnvToken();
	});
}

function getEnvToken(){
  switch (envServer) {
  case "localhost":
	  envToken = accessToken.local;
	  break;
  case "development":
	  envToken = accessToken.dev;
	  break;
  case "production":
	  envToken = accessToken.prod;
	  break;
  }
}

//sets ISO date time to 12:15 of current date on initial transit run
function parseIsoDateTime(dtStr) {
  var dt = dtStr.split("T");
	return dtStr.replace(dt[1],"12:15:00");
}
var dateStr = parseIsoDateTime(isoDateTime.toString());

app.run(function($rootScope) {
	  var hash_loc = hash_params ? hash_params : {'center': {'lat': 40.7486, 'lng': -73.9690}, 'zoom': 13};
	  $rootScope.geobase = {
	    'zoom': hash_loc.zoom,
	    'lat' : hash_loc.center.lat,
	    'lon' : hash_loc.center.lng
	  }
	  $(document).on('new-location', function(e){
	    $rootScope.geobase = {
	      'zoom': e.zoom,
	      'lat' : e.lat,
	      'lon' : e.lon
	    };
	  })
});

app.controller('RouteController', function($scope, $rootScope, $sce, $http) {

  var roadmap = L.tileLayer('http://otile3.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png', {attribution: 'Map data © OpenStreetMap'}),
      cyclemap = L.tileLayer('http://b.tile.thunderforest.com/cycle/{z}/{x}/{y}.png', {attribution: 'Map data © OpenStreetMap'}),
      transitmap = L.tileLayer(' http://{s}.tile.thunderforest.com/transport/{z}/{x}/{y}.png', {attribution: 'Map data © OpenStreetMap'});

  var baseMaps = {
	"RoadMap": roadmap,
	"CycleMap": cyclemap,
	"TransitMap": transitmap
  };

  var map = L.map('map', {
      zoom: $rootScope.geobase.zoom,
      zoomControl: false,
      layers: [cyclemap],
      center: [$rootScope.geobase.lat, $rootScope.geobase.lon]
  });

  L.control.layers(baseMaps, null).addTo(map);

  $scope.route_instructions = '';

  var Locations = [];
  var mode = 'car';

  var icon = L.icon({
	    iconUrl: 'resource/dot.png',

	    iconSize:     [38, 35], // size of the icon
	    shadowSize:   [50, 64], // size of the shadow
	    iconAnchor:   [22, 34], // point of the icon which will correspond to marker's location
	    shadowAnchor: [4, 62],  // the same for the shadow
	    popupAnchor:  [-3, -76] // point from which the popup should open relative to the iconAnchor
		});

	var mode_icons = {
	  'car' : 'js/images/drive.png',
	  'foot': 'js/images/walk.png',
	  'bicycle': 'js/images/bike.png'
	};

	var getStartIcon = function(icon){
	  return L.icon({
	    iconUrl: 'resource/startmarker@2x.png',
	    iconSize:     [44, 56], // size of the icon
	    iconAnchor: [22, 50]
	  });
	};

	var getEndIcon = function(icon){
	  return L.icon({
	    iconUrl: 'resource/destmarker@2x.png',
	    iconSize:   [44, 56], // size of the icon
	    iconAnchor: [22, 50]
	  });
	};

	// Set up the hash
	  var hash = new L.Hash(map);
	  var markers = [];
	  var remove_markers = function(){
	    for (i=0; i<markers.length; i++) {
	      map.removeLayer(markers[i]);
	    }
	    markers = [];
	  };

	  // Number of locations
	  var locations = 0;

	  var reset = function() {
	    $('svg').html('');
	    $('.leaflet-routing-container').remove();
	    $('.leaflet-marker-icon.leaflet-marker-draggable').remove();
	    $scope.$emit( 'resetRouteInstruction' );
	    remove_markers();
	    locations = 0;
	  };

	  $rootScope.$on( 'map.setView', function( ev, geo, zoom ){
	    map.setView( geo, zoom || 8 );
	  });
	  $rootScope.$on( 'map.dropMarker', function( ev, geo, m){

	    if (locations == 0) {
	      var marker = new L.marker(geo, {icon: getStartIcon(m || 'car')});
	    }
	    else {
	      var marker = new L.marker(geo, {icon: getEndIcon(m || 'car')});
	    }
	    map.addLayer(marker);
	    markers.push(marker);
	    //marker.openPopup();
	  });

	  $scope.renderHtml = function(html_code){
	    return $sce.trustAsHtml(html_code);
	  };

	  $scope.$on( 'setRouteInstruction', function( ev, instructions ) {
	    $scope.$apply(function(){
	      $scope.route_instructions = instructions;
	    });
	  });

	  $scope.$on( 'resetRouteInstruction', function( ev ) {
	    $scope.$apply(function(){
	      $scope.route_instructions = '';
	    });
	  });

	  map.on('click', function(e) {
	    var geo = {
	      'lat': e.latlng.lat,
	      'lon': e.latlng.lng
	    };

        if (locations == 0) {
	      Locations.push({lat: geo.lat, lon: geo.lon })
	      $rootScope.$emit( 'map.dropMarker', [geo.lat, geo.lon], mode);
	      locations++;
	      return;
	    } else if (locations > 1) {
	      Locations = [];
	      reset();

	      Locations.push({lat: geo.lat, lon: geo.lon })
	      $rootScope.$emit( 'map.dropMarker', [geo.lat, geo.lon], mode);
	      locations++;
	      return;
	    }

	    var waypoints = [];
	    Locations.forEach(function(gLoc) {
	      waypoints.push(L.latLng(gLoc.lat, gLoc.lon));
	    });

	    waypoints.push(L.latLng(geo.lat, geo.lon));

	    $rootScope.$emit( 'map.dropMarker', [geo.lat, geo.lon], mode);
	    locations++;

	    valhalla_mode = mode_mapping[mode];

	var rr = L.Routing.control({
	  waypoints: waypoints,
	  geocoder: null,
	  transitmode: valhalla_mode,
	  routeWhileDragging: false,
	  router: L.Routing.valhalla(envToken,'bicycle'),
	  summaryTemplate:'
{name}

{distance}, {time}
',

	  createMarker: function(i,wp,n){
      var iconV;
        if(i == 0){
          iconV = L.icon({
          iconUrl: 'resource/dot.png',
          iconSize:[24,24]
          });
        }else{
          iconV = L.icon({
          iconUrl: 'resource/dot.png',
          iconSize:[24,24]
        })
        }
        var options = {
          draggable: true,
          icon: iconV
        }
        return L.marker(wp.latLng,options);
	  },
	  formatter: new L.Routing.Valhalla.Formatter(),
	    pointMarkerStyle: {radius: 6,color: '#25A5FA',fillColor: '#5E6472',opacity: 1,fillOpacity: 1}
		}).addTo(map);

  var driveBtn = document.getElementById("drive_btn");
  var bikeBtn = document.getElementById("bike_btn");
  var walkBtn = document.getElementById("walk_btn");
  var multiBtn = document.getElementById("multi_btn");

  driveBtn.addEventListener('click', function (e) {
	getEnvToken();
    rr.route({transitmode: 'auto'});
  });

  bikeBtn.addEventListener('click', function (e) {
	getEnvToken();
	var bikeoptions = setBikeOptions();
	rr.route({transitmode: 'bicycle', costing_options: bikeoptions});
  });

  walkBtn.addEventListener('click', function (e) {
	getEnvToken();
    rr.route({transitmode: 'pedestrian'});
  });

  multiBtn.addEventListener('click', function (e) {
	getEnvToken();
    rr.route({transitmode: 'multimodal', date_time: dateStr});
  });

  function datetimeUpdate(datetime) {
      var changeDt = datetime;
      var inputDate, splitDate, year, month, day, time, hour, minute;
       if(changeDt != null){
   	     if (changeDt.length >= 11) {
   	    	inputDate = changeDt.split(" ");
   	    	splitDate = inputDate[0].split("-");
     	    day = splitDate[0];
     	    if (day < 10) {
      	      day = '0' + day;
      	    }
     	    month = GetMonthIndex(splitDate[1])+1;
     	   if (month < 10) {
     		  month = '0' + month;
       	    }
     	    year = splitDate[2];

    	  time = inputDate[1].split(":");
     	  hour = time[0];
     	  minute = time[1];

   	      dateStr = year + "-" + month + "-" + day + "T" + hour + ":" + minute;
   	    } else {
   		    dateStr = parseIsoDateTime(isoDateTime.toString());
   	    }
   	    multiBtn.click();
       }
  };

  function setBikeOptions () {
	var btype = document.getElementsByName("btype");
	var bicycle_type = [];
	  for (var i=0;i<btype.length;i++){
	    if ( btype[i].checked ) {
	    	bicycle_type.push(btype[i].value);
	    }
	  }
	var use_roads = document.getElementById("use_roads").value;
	var cycling_speed = document.getElementById("cycle_speed").value;
	var hilliness_factor = document.getElementById("hill_factor").value;

	bikeoptions = {
	  bicycle_type: bicycle_type,
	  use_roads: use_roads,
	  cycling_speed: cycling_speed,
	  hilliness_factor: hilliness_factor
	}
	return bikeoptions;
  };

  $(document).on('mode-alert', function(e, m) {
    mode = m;
    reset();
    Locations = [];
  });

  $(document).on('route:time_distance', function(e, td){
    var instructions = $('.leaflet-routing-container.leaflet-control').html();
    $scope.$emit( 'setRouteInstruction', instructions);
  });

  $("#datepicker").on("click", function() {
	datetimeUpdate(this.value);
  });
});

  $("#showbtn").on("click", function() {
	document.getElementById('options').style.display="block";
  });

  $("#hidebtn").on("click", function() {
	  document.getElementById('options').style.display="none";
  });

})<commit_msg>added link to OSM for all POIs to allow for review/edits<commit_after>var app = angular.module('routing', []);
var hash_params = L.Hash.parseHash(location.hash);
var mode_mapping = { 'foot' : 'pedestrian', 'car' : 'auto', 'bicycle' : 'bicycle', 'transit' : 'multimodal'};
var date = new Date();
var isoDateTime = date.toISOString();  //"2015-06-12T15:28:46.493Z"
var serviceUrl;
var envToken;
var envServer;

function selectEnv(){
	$( "option:selected" ).each(function() {
	  envServer = $( this ).text();
	  serviceUrl =  document.getElementById(envServer).value;
	  getEnvToken();
	});
}

function getEnvToken(){
  switch (envServer) {
  case "localhost":
	  envToken = accessToken.local;
	  break;
  case "development":
	  envToken = accessToken.dev;
	  break;
  case "production":
	  envToken = accessToken.prod;
	  break;
  }
}

//sets ISO date time to 12:15 of current date on initial transit run
function parseIsoDateTime(dtStr) {
  var dt = dtStr.split("T");
	return dtStr.replace(dt[1],"12:15:00");
}
var dateStr = parseIsoDateTime(isoDateTime.toString());

app.run(function($rootScope) {
	  var hash_loc = hash_params ? hash_params : {'center': {'lat': 40.7486, 'lng': -73.9690}, 'zoom': 13};
	  $rootScope.geobase = {
	    'zoom': hash_loc.zoom,
	    'lat' : hash_loc.center.lat,
	    'lon' : hash_loc.center.lng
	  }
	  $(document).on('new-location', function(e){
	    $rootScope.geobase = {
	      'zoom': e.zoom,
	      'lat' : e.lat,
	      'lon' : e.lon
	    };
	  })
});

app.controller('RouteController', function($scope, $rootScope, $sce, $http) {

  var roadmap = L.tileLayer('http://otile3.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png', {attribution: 'Map data © OpenStreetMap'}),
      cyclemap = L.tileLayer('http://b.tile.thunderforest.com/cycle/{z}/{x}/{y}.png', {attribution: 'Map data © OpenStreetMap'}),
      transitmap = L.tileLayer(' http://{s}.tile.thunderforest.com/transport/{z}/{x}/{y}.png', {attribution: 'Map data © OpenStreetMap'});

  var baseMaps = {
	"RoadMap": roadmap,
	"CycleMap": cyclemap,
	"TransitMap": transitmap
  };

  var map = L.map('map', {
      zoom: $rootScope.geobase.zoom,
      zoomControl: false,
      layers: [cyclemap],
      center: [$rootScope.geobase.lat, $rootScope.geobase.lon]
  });

  L.control.layers(baseMaps, null).addTo(map);

  $scope.route_instructions = '';

  var Locations = [];
  var mode = 'car';

  var icon = L.icon({
	    iconUrl: 'resource/dot.png',

	    iconSize:     [38, 35], // size of the icon
	    shadowSize:   [50, 64], // size of the shadow
	    iconAnchor:   [22, 34], // point of the icon which will correspond to marker's location
	    shadowAnchor: [4, 62],  // the same for the shadow
	    popupAnchor:  [-3, -76] // point from which the popup should open relative to the iconAnchor
		});

	var mode_icons = {
	  'car' : 'js/images/drive.png',
	  'foot': 'js/images/walk.png',
	  'bicycle': 'js/images/bike.png'
	};

	var getStartIcon = function(icon){
	  return L.icon({
	    iconUrl: 'resource/startmarker@2x.png',
	    iconSize:     [44, 56], // size of the icon
	    iconAnchor: [22, 50]
	  });
	};

	var getEndIcon = function(icon){
	  return L.icon({
	    iconUrl: 'resource/destmarker@2x.png',
	    iconSize:   [44, 56], // size of the icon
	    iconAnchor: [22, 50]
	  });
	};

	// Set up the hash
	  var hash = new L.Hash(map);
	  var markers = [];
	  var remove_markers = function(){
	    for (i=0; i<markers.length; i++) {
	      map.removeLayer(markers[i]);
	    }
	    markers = [];
	  };

	  // Number of locations
	  var locations = 0;

	  var reset = function() {
	    $('svg').html('');
	    $('.leaflet-routing-container').remove();
	    $('.leaflet-marker-icon.leaflet-marker-draggable').remove();
	    $scope.$emit( 'resetRouteInstruction' );
	    remove_markers();
	    locations = 0;
	  };

	  $rootScope.$on( 'map.setView', function( ev, geo, zoom ){
	    map.setView( geo, zoom || 8 );
	  });
	  $rootScope.$on( 'map.dropMarker', function( ev, geo, m){

	    if (locations == 0) {
	      var marker = new L.marker(geo, {icon: getStartIcon(m || 'car')});
	      marker.bindPopup("Edit POI here");
	    }
	    else {
	      var marker = new L.marker(geo, {icon: getEndIcon(m || 'car')});
	      marker.bindPopup("Edit POI here");
	    }
	    map.addLayer(marker);
	    markers.push(marker);
	  });

	  $scope.renderHtml = function(html_code){
	    return $sce.trustAsHtml(html_code);
	  };

	  $scope.$on( 'setRouteInstruction', function( ev, instructions ) {
	    $scope.$apply(function(){
	      $scope.route_instructions = instructions;
	    });
	  });

	  $scope.$on( 'resetRouteInstruction', function( ev ) {
	    $scope.$apply(function(){
	      $scope.route_instructions = '';
	    });
	  });

	  map.on('click', function(e) {
	    var geo = {
	      'lat': e.latlng.lat,
	      'lon': e.latlng.lng
	    };

        if (locations == 0) {
	      Locations.push({lat: geo.lat, lon: geo.lon })
	      $rootScope.$emit( 'map.dropMarker', [geo.lat, geo.lon], mode);
	      locations++;
	      return;
	    } else if (locations > 1) {
	      Locations = [];
	      reset();

	      Locations.push({lat: geo.lat, lon: geo.lon })
	      $rootScope.$emit( 'map.dropMarker', [geo.lat, geo.lon], mode);
	      locations++;
	      return;
	    }

	    var waypoints = [];
	    Locations.forEach(function(gLoc) {
	      waypoints.push(L.latLng(gLoc.lat, gLoc.lon));
	    });

	    waypoints.push(L.latLng(geo.lat, geo.lon));

	    $rootScope.$emit( 'map.dropMarker', [geo.lat, geo.lon], mode);
	    locations++;

	    valhalla_mode = mode_mapping[mode];

	var rr = L.Routing.control({
	  waypoints: waypoints,
	  geocoder: null,
	  transitmode: valhalla_mode,
	  routeWhileDragging: false,
	  router: L.Routing.valhalla(envToken,'bicycle'),
	  summaryTemplate:'
{name}

{distance}, {time}
',

	  createMarker: function(i,wp,n){
      var iconV;
        if(i == 0){
          iconV = L.icon({
          iconUrl: 'resource/dot.png',
          iconSize:[24,24]
          });
        }else{
          iconV = L.icon({
          iconUrl: 'resource/dot.png',
          iconSize:[24,24]
        })
        }
        var options = {
          draggable: true,
          icon: iconV
        }
        var dot = L.marker(wp.latLng,options);
        return dot.bindPopup("Edit POI here");
	  },
	  formatter: new L.Routing.Valhalla.Formatter(),
	    pointMarkerStyle: {radius: 6,color: '#25A5FA',fillColor: '#5E6472',opacity: 1,fillOpacity: 1}
		}).addTo(map);

  var driveBtn = document.getElementById("drive_btn");
  var bikeBtn = document.getElementById("bike_btn");
  var walkBtn = document.getElementById("walk_btn");
  var multiBtn = document.getElementById("multi_btn");

  driveBtn.addEventListener('click', function (e) {
	getEnvToken();
    rr.route({transitmode: 'auto'});
  });

  bikeBtn.addEventListener('click', function (e) {
	getEnvToken();
	var bikeoptions = setBikeOptions();
	rr.route({transitmode: 'bicycle', costing_options: bikeoptions});
  });

  walkBtn.addEventListener('click', function (e) {
	getEnvToken();
    rr.route({transitmode: 'pedestrian'});
  });

  multiBtn.addEventListener('click', function (e) {
	getEnvToken();
    rr.route({transitmode: 'multimodal', date_time: dateStr});
  });

  function datetimeUpdate(datetime) {
      var changeDt = datetime;
      var inputDate, splitDate, year, month, day, time, hour, minute;
       if(changeDt != null){
   	     if (changeDt.length >= 11) {
   	    	inputDate = changeDt.split(" ");
   	    	splitDate = inputDate[0].split("-");
     	    day = splitDate[0];
     	    if (day < 10) {
      	      day = '0' + day;
      	    }
     	    month = GetMonthIndex(splitDate[1])+1;
     	   if (month < 10) {
     		  month = '0' + month;
       	    }
     	    year = splitDate[2];

    	  time = inputDate[1].split(":");
     	  hour = time[0];
     	  minute = time[1];

   	      dateStr = year + "-" + month + "-" + day + "T" + hour + ":" + minute;
   	    } else {
   		    dateStr = parseIsoDateTime(isoDateTime.toString());
   	    }
   	    multiBtn.click();
       }
  };

  function setBikeOptions () {
	var btype = document.getElementsByName("btype");
	var bicycle_type = [];
	  for (var i=0;i<btype.length;i++){
	    if ( btype[i].checked ) {
	    	bicycle_type.push(btype[i].value);
	    }
	  }
	var use_roads = document.getElementById("use_roads").value;
	var cycling_speed = document.getElementById("cycle_speed").value;
	var hilliness_factor = document.getElementById("hill_factor").value;

	bikeoptions = {
	  bicycle_type: bicycle_type,
	  use_roads: use_roads,
	  cycling_speed: cycling_speed,
	  hilliness_factor: hilliness_factor
	}
	return bikeoptions;
  };

  $(document).on('mode-alert', function(e, m) {
    mode = m;
    reset();
    Locations = [];
  });

  $(document).on('route:time_distance', function(e, td){
    var instructions = $('.leaflet-routing-container.leaflet-control').html();
    $scope.$emit( 'setRouteInstruction', instructions);
  });

  $("#datepicker").on("click", function() {
	datetimeUpdate(this.value);
  });
});

  $("#showbtn").on("click", function() {
	document.getElementById('options').style.display="block";
  });

  $("#hidebtn").on("click", function() {
	  document.getElementById('options').style.display="none";
  });

})<|endoftext|>