<commit_before>"use strict";

// ----------------------------------------------------------------------------
// Usage: node run-tests [--php] [--js] [--python] [exchange] [symbol]

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

const fs = require ('fs')
    , log = require ('ololog').handleNodeErrors ()
    , ansi = require ('ansicolor').nice
    , { spawn, execSync } = require ('child_process')

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

const [,, ...args] = process.argv

const keys = {

    '--js': false,      // run JavaScript tests only
    '--php': false,     // run PHP tests only
    '--python': false,  // run Python tests only
    '--python3': false,  // run Python tests only
}

let exchanges = []
let symbol = 'all'
let maxConcurrency = 5 // Number.MAX_VALUE // no limit

for (const arg of args) {
    if (arg.startsWith ('--'))               { keys[arg] = true }
    else if (arg.includes ('/'))             { symbol = arg }
    else if (Number.isFinite (Number (arg))) { maxConcurrency = Number (arg) }
    else                                     { exchanges.push (arg) }
}

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

if (!exchanges.length) {

    if (!fs.existsSync ('./exchanges.json')) {

        log.bright.red ('\n\tNo', 'exchanges.json'.white, 'found, please run', 'npm run build'.white, 'to generate it!\n')
        process.exit (1)
    }

    exchanges = require ('./exchanges.json').ids
}

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

const sleep = s => new Promise (resolve => setTimeout (resolve, s))
const timeout = (s, promise) => Promise.race ([ promise, sleep (s).then (() => { throw new Error ('Test execution took longer than ' + maxTimeout.toString () + ' milliseconds, testing timed out.') }) ])
const maxTimeout = 300000 // 5 minutes

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

const exec = (bin, ...args) =>

    // a custom version of child_process.exec that captures both stdout and
    // stderr,  not separating them into distinct buffers — so that we can show
    // the same output as if it were running in a terminal.

    timeout (maxTimeout, new Promise (return_ => {

        const ps = spawn (bin, args)

        let output = ''
        let stderr = ''
        let hasWarnings = false

        ps.stdout.on ('data', data => { output += data.toString () })
        ps.stderr.on ('data', data => { output += data.toString (); stderr += data.toString (); hasWarnings = true })

        ps.on ('exit', code => {

            output = ansi.strip (output.trim ())
            stderr = ansi.strip (stderr)

            const regex = /^\[[^\] ]+\]/mg

            let match = undefined
            const warnings = []

            match = regex.exec (output)

            do {
                if (match = regex.exec (output)) {
                    warnings.push (match[0])
                }
            } while (match);

            return_ ({
                failed: code !== 0,
                output,
                hasOutput: output.length > 0,
                hasWarnings: hasWarnings || warnings.length > 0,
                warnings: warnings,
            })
        })

    })).catch (e => ({

        failed: true,
        output: e.message

    })).then (x => Object.assign (x, { hasOutput: x.output.length > 0 }))

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

let numExchangesTested = 0

// tests of different languages for the same exchange should be run
// sequentially to prevent the interleaving nonces problem.

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

const sequentialMap = async (input, fn) => {

    const result = []
    for (const item of input) { result.push (await fn (item)) }
    return result
}

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

const testExchange = async (exchange) => {

    // run tests for all/selected languages (in parallel)

    const args = [exchange, ... (symbol === 'all') ? [] : [ symbol ]]
        , allTests = [
            { language: 'JavaScript', key: '--js',      exec: ['node',      'js/test/test.js',     ...args] },
            { language: 'Python',     key: '--python',  exec: ['python',    'python/test/test.py', ...args] },
            { language: 'Python',     key: '--python3', exec: ['python3',   'python/test/test.py', ...args] },
            { language: 'PHP',        key: '--php',     exec: ['php', '-f', 'php/test/test.php',   ...args] }
        ]
        , selectedTests  = allTests.filter (t => keys[t.key])
        , scheduledTests = selectedTests.length ? selectedTests : allTests
        , completeTests  = await sequentialMap (scheduledTests, async test => Object.assign (test, await exec (...test.exec)))
        , failed         = completeTests.find (test => test.failed)
        , hasWarnings    = completeTests.find (test => test.hasWarnings)
        , warnings       = completeTests.reduce ((total, { warnings }) => total.concat (warnings), [])

    // print interactive log output

    numExchangesTested++

    const percentsDone = ((numExchangesTested / exchanges.length) * 100).toFixed (0) + '%'

    log.bright (('[' + percentsDone + ']').dim, 'Testing', exchange.cyan, (failed      ? 'FAIL'.red :
                                                                          (hasWarnings ? (warnings.length ? warnings.join (' ') : 'WARN').yellow
                                                                                : 'OK'.green)))

    // return collected data to main loop

    return {

        exchange,
        failed,
        hasWarnings,
        explain () {
            for (const { language, failed, output, hasWarnings } of completeTests) {
                if (failed || hasWarnings) {

                    if (!failed && output.indexOf('[Skipped]') >= 0)
                        continue;

                    if (failed) { log.bright ('\nFAILED'.bgBrightRed.white, exchange.red,    '(' + language + '):\n') }
                    else        { log.bright ('\nWARN'.yellow.inverse,      exchange.yellow, '(' + language + '):\n') }

                    log.indent (1) (output)
                }
            }
        }
    }
}

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

function TaskPool (maxConcurrency) {

    const pending = []
        , queue   = []

    let numActive = 0

    return {

        pending,

        run (task) {

            if (numActive >= maxConcurrency) { // queue task

                return new Promise (resolve => queue.push (() => this.run (task).then (resolve)))

            } else { // execute task

                let p = task ().then (x => {
                    numActive--
                    return (queue.length && (numActive < maxConcurrency))
                                ? queue.shift () ().then (() => x)
                                : x
                })
                numActive++
                pending.push (p)
                return p
            }
        }
    }
}

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

async function testAllExchanges () {

    const taskPool = TaskPool (maxConcurrency)
    const results = []

    for (const exchange of exchanges) {
        taskPool.run (() => testExchange (exchange).then (x => results.push (x)))
    }

    await Promise.all (taskPool.pending)

    return results
}

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

(async function () {

    log.bright.magenta.noPretty ('Testing'.white, Object.assign (
                                                            { exchanges, symbol, keys },
                                                            maxConcurrency >= Number.MAX_VALUE ? {} : { maxConcurrency }))

    const tested    = await testAllExchanges ()
        , warnings  = tested.filter (t => !t.failed && t.hasWarnings)
        , failed    = tested.filter (t =>  t.failed)
        , succeeded = tested.filter (t => !t.failed && !t.hasWarnings)

    log.newline ()

    warnings.forEach (t => t.explain ())
    failed.forEach (t => t.explain ())

    log.newline ()

    if (failed.length)   { log.noPretty.bright.red    ('FAIL'.bgBrightRed.white, failed.map (t => t.exchange)) }
    if (warnings.length) { log.noPretty.bright.yellow ('WARN'.inverse,           warnings.map (t => t.exchange)) }

    log.newline ()

    log.bright ('All done,', [failed.length    && (failed.length    + ' failed')   .red,
                              succeeded.length && (succeeded.length + ' succeeded').green,
                              warnings.length  && (warnings.length  + ' warnings') .yellow].filter (s => s).join (', '))

    if (failed.length) {

        await sleep (10000) // to fight TravisCI log truncation issue, see https://github.com/travis-ci/travis-ci/issues/8189
        process.exit (1)

    } else {
        process.exit (0)
    }

}) ();

// ----------------------------------------------------------------------------
<commit_msg>run-tests 10 minutes timeout<commit_after>"use strict";

// ----------------------------------------------------------------------------
// Usage: node run-tests [--php] [--js] [--python] [exchange] [symbol]

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

const fs = require ('fs')
    , log = require ('ololog').handleNodeErrors ()
    , ansi = require ('ansicolor').nice
    , { spawn, execSync } = require ('child_process')

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

const [,, ...args] = process.argv

const keys = {

    '--js': false,      // run JavaScript tests only
    '--php': false,     // run PHP tests only
    '--python': false,  // run Python tests only
    '--python3': false,  // run Python tests only
}

let exchanges = []
let symbol = 'all'
let maxConcurrency = 5 // Number.MAX_VALUE // no limit

for (const arg of args) {
    if (arg.startsWith ('--'))               { keys[arg] = true }
    else if (arg.includes ('/'))             { symbol = arg }
    else if (Number.isFinite (Number (arg))) { maxConcurrency = Number (arg) }
    else                                     { exchanges.push (arg) }
}

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

if (!exchanges.length) {

    if (!fs.existsSync ('./exchanges.json')) {

        log.bright.red ('\n\tNo', 'exchanges.json'.white, 'found, please run', 'npm run build'.white, 'to generate it!\n')
        process.exit (1)
    }

    exchanges = require ('./exchanges.json').ids
}

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

const sleep = s => new Promise (resolve => setTimeout (resolve, s))
const timeout = (s, promise) => Promise.race ([ promise, sleep (s).then (() => { throw new Error ('Test execution took longer than ' + maxTimeout.toString () + ' milliseconds, testing timed out.') }) ])
const maxTimeout = 600000 // 10 minutes

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

const exec = (bin, ...args) =>

    // a custom version of child_process.exec that captures both stdout and
    // stderr,  not separating them into distinct buffers — so that we can show
    // the same output as if it were running in a terminal.

    timeout (maxTimeout, new Promise (return_ => {

        const ps = spawn (bin, args)

        let output = ''
        let stderr = ''
        let hasWarnings = false

        ps.stdout.on ('data', data => { output += data.toString () })
        ps.stderr.on ('data', data => { output += data.toString (); stderr += data.toString (); hasWarnings = true })

        ps.on ('exit', code => {

            output = ansi.strip (output.trim ())
            stderr = ansi.strip (stderr)

            const regex = /^\[[^\] ]+\]/mg

            let match = undefined
            const warnings = []

            match = regex.exec (output)

            do {
                if (match = regex.exec (output)) {
                    warnings.push (match[0])
                }
            } while (match);

            return_ ({
                failed: code !== 0,
                output,
                hasOutput: output.length > 0,
                hasWarnings: hasWarnings || warnings.length > 0,
                warnings: warnings,
            })
        })

    })).catch (e => ({

        failed: true,
        output: e.message

    })).then (x => Object.assign (x, { hasOutput: x.output.length > 0 }))

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

let numExchangesTested = 0

// tests of different languages for the same exchange should be run
// sequentially to prevent the interleaving nonces problem.

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

const sequentialMap = async (input, fn) => {

    const result = []
    for (const item of input) { result.push (await fn (item)) }
    return result
}

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

const testExchange = async (exchange) => {

    // run tests for all/selected languages (in parallel)

    const args = [exchange, ... (symbol === 'all') ? [] : [ symbol ]]
        , allTests = [
            { language: 'JavaScript', key: '--js',      exec: ['node',      'js/test/test.js',     ...args] },
            { language: 'Python',     key: '--python',  exec: ['python',    'python/test/test.py', ...args] },
            { language: 'Python',     key: '--python3', exec: ['python3',   'python/test/test.py', ...args] },
            { language: 'PHP',        key: '--php',     exec: ['php', '-f', 'php/test/test.php',   ...args] }
        ]
        , selectedTests  = allTests.filter (t => keys[t.key])
        , scheduledTests = selectedTests.length ? selectedTests : allTests
        , completeTests  = await sequentialMap (scheduledTests, async test => Object.assign (test, await exec (...test.exec)))
        , failed         = completeTests.find (test => test.failed)
        , hasWarnings    = completeTests.find (test => test.hasWarnings)
        , warnings       = completeTests.reduce ((total, { warnings }) => total.concat (warnings), [])

    // print interactive log output

    numExchangesTested++

    const percentsDone = ((numExchangesTested / exchanges.length) * 100).toFixed (0) + '%'

    log.bright (('[' + percentsDone + ']').dim, 'Testing', exchange.cyan, (failed      ? 'FAIL'.red :
                                                                          (hasWarnings ? (warnings.length ? warnings.join (' ') : 'WARN').yellow
                                                                                : 'OK'.green)))

    // return collected data to main loop

    return {

        exchange,
        failed,
        hasWarnings,
        explain () {
            for (const { language, failed, output, hasWarnings } of completeTests) {
                if (failed || hasWarnings) {

                    if (!failed && output.indexOf('[Skipped]') >= 0)
                        continue;

                    if (failed) { log.bright ('\nFAILED'.bgBrightRed.white, exchange.red,    '(' + language + '):\n') }
                    else        { log.bright ('\nWARN'.yellow.inverse,      exchange.yellow, '(' + language + '):\n') }

                    log.indent (1) (output)
                }
            }
        }
    }
}

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

function TaskPool (maxConcurrency) {

    const pending = []
        , queue   = []

    let numActive = 0

    return {

        pending,

        run (task) {

            if (numActive >= maxConcurrency) { // queue task

                return new Promise (resolve => queue.push (() => this.run (task).then (resolve)))

            } else { // execute task

                let p = task ().then (x => {
                    numActive--
                    return (queue.length && (numActive < maxConcurrency))
                                ? queue.shift () ().then (() => x)
                                : x
                })
                numActive++
                pending.push (p)
                return p
            }
        }
    }
}

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

async function testAllExchanges () {

    const taskPool = TaskPool (maxConcurrency)
    const results = []

    for (const exchange of exchanges) {
        taskPool.run (() => testExchange (exchange).then (x => results.push (x)))
    }

    await Promise.all (taskPool.pending)

    return results
}

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

(async function () {

    log.bright.magenta.noPretty ('Testing'.white, Object.assign (
                                                            { exchanges, symbol, keys },
                                                            maxConcurrency >= Number.MAX_VALUE ? {} : { maxConcurrency }))

    const tested    = await testAllExchanges ()
        , warnings  = tested.filter (t => !t.failed && t.hasWarnings)
        , failed    = tested.filter (t =>  t.failed)
        , succeeded = tested.filter (t => !t.failed && !t.hasWarnings)

    log.newline ()

    warnings.forEach (t => t.explain ())
    failed.forEach (t => t.explain ())

    log.newline ()

    if (failed.length)   { log.noPretty.bright.red    ('FAIL'.bgBrightRed.white, failed.map (t => t.exchange)) }
    if (warnings.length) { log.noPretty.bright.yellow ('WARN'.inverse,           warnings.map (t => t.exchange)) }

    log.newline ()

    log.bright ('All done,', [failed.length    && (failed.length    + ' failed')   .red,
                              succeeded.length && (succeeded.length + ' succeeded').green,
                              warnings.length  && (warnings.length  + ' warnings') .yellow].filter (s => s).join (', '))

    if (failed.length) {

        await sleep (10000) // to fight TravisCI log truncation issue, see https://github.com/travis-ci/travis-ci/issues/8189
        process.exit (1)

    } else {
        process.exit (0)
    }

}) ();

// ----------------------------------------------------------------------------
<|endoftext|>


<commit_before>var $ = require('jquery');
var _ = require('underscore');
var Backbone = require('backbone');

// Initialize jQuery in BackBone
Backbone.$ = $;

var unknown = "unknown", fail = no = false, pass = yes = true, empty = "";

var client = new Object;

client.build = function(){
	var Model = Backbone.Model.extend({
		urlRoot: '/api',
		idAttribute: 'eID',

		// Default attributes
		defaults: function() {
			return {
				eID: undefined,
				from: { name:unknown, email:unknown },
				to: new Array,
				subject: unknown,

				text: empty,
				html: unknown,

				stamp: undefined
			};
		},

		initialize: function() {

		},

		store: function() {
			this.save();
		},

		setRead: function() {
			this.set('read', true);
		}

	});

	var Collection = Backbone.Collection.extend({
		url: '/api',

		// Reference to this collection's model.
		model: Model,

		initialize: function(){
			//this.model.on('sync', this.onSync);
			//this.model.on('error', this.onError);
			//this.model.on('destroy', this.onDestroy);
		},

		// Filter down the list of all unread emails.
		read: function(eID) {
			return this.model.setRead();
		}
	});

	var ItemView = Backbone.View.extend({
		tagName:  "article",

		template: _.template('<%- subject %>'),

		// The DOM events specific to an item.
		events: {
			"click .header"   : "clicked"
		},

		initialize: function() {
			//this.listenTo(this.model, 'change', this.render);
			//this.listenTo(this.model, 'destroy', this.remove);
		},

		render: function() {
			this.$el.html(this.template(this.model.toJSON()));
			return this;
		},

		clicked: function() {
			console.log("Just letting you know someone clicked on something");
		}

	});

	var Mail = new Collection;

	var View = Backbone.View.extend({
		el: "section#list",

		template: _.template('
<%- from.email %>

<%- subject %>
'),

		initialize: function() {
			this.listenTo(Mail, 'all', this.render);

			Mail.fetch();
		},

		addOne: function(mail) {
			var el = $(this.el);
			var view = new ItemView({model: mail});
			console.log(el.get('eID'));
			if(!el.find('#' + el.get('eID')).length){
				var newElement = $(view.render().el);
				el.append(newElement);
				newElement.prop('id',el.get('eID'));
			}
		},

		addAll: function() {
		  Mail.each(this.addOne, this);
		},

		render: function() {
			this.addAll();
		}

	});

	this.Model = Model;
	this.Mail = Mail;
	this.ItemView = ItemView;
	this.View = new View;
}

$(function(){ window.API = new client.build(); })<commit_msg>Fixed a variable override<commit_after>var $ = require('jquery');
var _ = require('underscore');
var Backbone = require('backbone');

// Initialize jQuery in BackBone
Backbone.$ = $;

var unknown = "unknown", fail = no = false, pass = yes = true, empty = "";

var client = new Object;

client.build = function(){
	var Model = Backbone.Model.extend({
		urlRoot: '/api',
		idAttribute: 'eID',

		// Default attributes
		defaults: function() {
			return {
				eID: undefined,
				from: { name:unknown, email:unknown },
				to: new Array,
				subject: unknown,

				text: empty,
				html: unknown,

				stamp: undefined
			};
		},

		initialize: function() {

		},

		store: function() {
			this.save();
		},

		setRead: function() {
			this.set('read', true);
		}

	});

	var Collection = Backbone.Collection.extend({
		url: '/api',

		// Reference to this collection's model.
		model: Model,

		initialize: function(){
			//this.model.on('sync', this.onSync);
			//this.model.on('error', this.onError);
			//this.model.on('destroy', this.onDestroy);
		},

		// Filter down the list of all unread emails.
		read: function(eID) {
			return this.model.setRead();
		}
	});

	var ItemView = Backbone.View.extend({
		tagName:  "article",

		template: _.template('<%- subject %>'),

		// The DOM events specific to an item.
		events: {
			"click .header"   : "clicked"
		},

		initialize: function() {
			//this.listenTo(this.model, 'change', this.render);
			//this.listenTo(this.model, 'destroy', this.remove);
		},

		render: function() {
			this.$el.html(this.template(this.model.toJSON()));
			return this;
		},

		clicked: function() {
			console.log("Just letting you know someone clicked on something");
		}

	});

	var Mail = new Collection;

	var View = Backbone.View.extend({
		el: "section#list",

		template: _.template('
<%- from.email %>

<%- subject %>
'),

		initialize: function() {
			this.listenTo(Mail, 'all', this.render);

			Mail.fetch();
		},

		addOne: function(mail) {
			var el = $(this.el);
			var view = new ItemView({model: mail});
			console.log(mail.get('eID'));
			if(!el.find('#' + mail.get('eID')).length){
				var newElement = $(view.render().el);
				el.append(newElement);
				newElement.prop('id',mail.get('eID'));
			}
		},

		addAll: function() {
		  Mail.each(this.addOne, this);
		},

		render: function() {
			this.addAll();
		}

	});

	this.Model = Model;
	this.Mail = Mail;
	this.ItemView = ItemView;
	this.View = new View;
}

$(function(){ window.API = new client.build(); })<|endoftext|>


<commit_before>describe("Package", function () {

    var request = new XMLHttpRequest(),
        finderjs,
        finderjsmin,
        packagejson;

    // Fetch `src/applait.finder.js` synchronously
    request.open("GET", "src/applait.finder.js", false);  // `false` makes the request synchronous
    request.send(null);

    if (request.status === 200) {
        finderjs = request.responseText;
    } else {
        throw "Unable to fetch src/applait.finder.js";
    }

    // Fetch `applait.finder.min.js` synchronously
    request.open("GET", "applait.finder.min.js", false);  // `false` makes the request synchronous
    request.send(null);

    if (request.status === 200) {
        finderjsmin = request.responseText;
    } else {
        throw "Unable to fetch applait.finder.min.js";
    }

    // Fetch `package.json` synchronously
    request.open("GET", "package.json", false);  // `false` makes the request synchronous
    request.send(null);

    if (request.status === 200) {
        packagejson = JSON.parse(request.responseText);
    } else {
        throw "Unable to fetch package.json";
    }

    // start the real tests
    it ("should have same version string throughout.", function () {
        var re = /@version\s([0-9.]+([a-z0-9-.]+)?)/,
            srcversion,
            minversion;

        try {
            srcversion = finderjs.match(re)[1];
        } catch (e) {
            throw "Unable to find version string in src/applait.finder.js. " + e.message;
        }

        try {
            minversion = finderjsmin.match(re)[1];
        } catch (e) {
            throw "Unable to find version string in applait.finder.min.js. " + e.message;
        }

        expect(srcversion).toBe(packagejson.version.toString());
        expect(srcversion).toBe(minversion);
        expect(minversion).toBe(packagejson.version.toString());
    });

});
<commit_msg>Add entire package.json to test to ensure its integrity<commit_after>describe("Package", function () {

    var request = new XMLHttpRequest(),
        finderjs,
        finderjsmin,
        packagejson;

    // Fetch `src/applait.finder.js` synchronously
    request.open("GET", "src/applait.finder.js", false);  // `false` makes the request synchronous
    request.send(null);

    if (request.status === 200) {
        finderjs = request.responseText;
    } else {
        throw "Unable to fetch src/applait.finder.js";
    }

    // Fetch `applait.finder.min.js` synchronously
    request.open("GET", "applait.finder.min.js", false);  // `false` makes the request synchronous
    request.send(null);

    if (request.status === 200) {
        finderjsmin = request.responseText;
    } else {
        throw "Unable to fetch applait.finder.min.js";
    }

    // Fetch `package.json` synchronously
    request.open("GET", "package.json", false);  // `false` makes the request synchronous
    request.send(null);

    if (request.status === 200) {
        packagejson = JSON.parse(request.responseText);
    } else {
        throw "Unable to fetch package.json";
    }

    // start the real tests
    it ("should have same version string throughout.", function () {
        var re = /@version\s([0-9.]+([a-z0-9-.]+)?)/,
            srcversion,
            minversion;

        try {
            srcversion = finderjs.match(re)[1];
        } catch (e) {
            throw "Unable to find version string in src/applait.finder.js. " + e.message;
        }

        try {
            minversion = finderjsmin.match(re)[1];
        } catch (e) {
            throw "Unable to find version string in applait.finder.min.js. " + e.message;
        }

        expect(srcversion).toBe(packagejson.version.toString());
        expect(srcversion).toBe(minversion);
        expect(minversion).toBe(packagejson.version.toString());
    });

    // Test package.json integrity
    it("definition file, package.json should have proper data.", function () {

        expect(packagejson).toEqual({
            "name": "finderjs",
            "version": "1.1.3",
            "description": "Device Storage search and file picker library for Firefox OS",
            "main": "src/applait.finder.js",
            "directories": {
                "doc": "docs"
            },
            "repository": {
                "type": "git",
                "url": "git://github.com/applait/finderjs.git"
            },
            "keywords": [
                "firefoxos",
                "devicestorage"
            ],
            "author": {
                "name": "Applait Technologies LLP",
                "email": "ask@applait.com",
                "url": "http://applait.com"
            },
            "license": "MIT",
            "readmeFilename": "README.md",
            "bugs": {
                "url": "https://github.com/applait/finderjs/issues"
            },
            "devDependencies": {
                "grunt": "~0.4.5",
                "grunt-contrib-jshint": "~0.10.0",
                "grunt-contrib-uglify": "~0.5.0",
                "grunt-jsdoc": "~0.6.1",
                "grunt-contrib-jasmine": "~0.8.1",
                "grunt-contrib-connect": "~0.9.0"
            },
            "scripts": {
                "test": "grunt test",
                "build": "grunt build"
            }
        });
    });

});
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var passport = require('passport');

module.exports = function (app) {
  // User Routes
  var users = require('../controllers/users.server.controller');

  // Setting up the users password api
  app.route('/api/auth/forgot').post(users.forgot);
  app.route('/api/auth/reset/:token').get(users.validateResetToken);
  app.route('/api/auth/reset/:token').post(users.reset);

  // Setting up the users authentication api
  app.route('/api/auth/signup').post(users.signup);
  app.route('/api/auth/signin').post(users.signin);
  app.route('/api/auth/signout').get(users.signout);
  app.route('/api/auth/resend').post(users.resend);
  app.route('/api/auth/verify/:token').get(users.verify);
  app.route('/api/auth/verifyEmail/:token').post(users.verifyEmail);

  // Setting the facebook oauth routes
  app.route('/api/auth/facebook').get(users.oauthCall('facebook', {
    scope: ['email']
  }));
  app.route('/api/auth/facebook/callback').get(users.oauthCallback('facebook'));

  // Setting the twitter oauth routes
  app.route('/api/auth/twitter').get(users.oauthCall('twitter'));
  app.route('/api/auth/twitter/callback').get(users.oauthCallback('twitter'));

  // Setting the google oauth routes
  app.route('/api/auth/google').get(users.oauthCall('google', {
    scope: [
      'https://www.googleapis.com/auth/userinfo.profile',
      'https://www.googleapis.com/auth/userinfo.email'
    ]
  }));
  app.route('/api/auth/google/callback').get(users.oauthCallback('google'));

  // Setting the linkedin oauth routes
  app.route('/api/auth/linkedin').get(users.oauthCall('linkedin', {
    scope: [
      'r_basicprofile',
      'r_emailaddress'
    ]
  }));
  app.route('/api/auth/linkedin/callback').get(users.oauthCallback('linkedin'));

  // Setting the github oauth routes
  app.route('/api/auth/github').get(users.oauthCall('github'));
  app.route('/api/auth/github/callback').get(users.oauthCallback('github'));

  // Setting the paypal oauth routes
  app.route('/api/auth/paypal').get(users.oauthCall('paypal'));
  app.route('/api/auth/paypal/callback').get(users.oauthCallback('paypal'));
};
<commit_msg>Commited from HaNhatLenh at Sat, Sep 09, 2017 10:41:51 AM<commit_after>'use strict';

/**
 * Module dependencies.
 */
var passport = require('passport');

module.exports = function (app) {
  // User Routes
  var users = require('../controllers/users.server.controller');

  // Setting up the users password api
  app.route('/api/auth/forgot').post(users.forgot);
  app.route('/api/auth/reset/:token').get(users.validateResetToken);
  app.route('/api/auth/reset/:token').post(users.reset);

  // Setting up the users authentication api
  app.route('/api/auth/signup').post(users.signup);
  app.route('/api/auth/signin').post(users.signin);
  app.route('/api/auth/signout').get(users.signout);
  app.route('/api/auth/resend').post(users.resend);
  app.route('/api/auth/verify/:token').get(users.verify);
  app.route('/api/auth/verifyEmail/:token').get(users.verifyEmail);

  // Setting the facebook oauth routes
  app.route('/api/auth/facebook').get(users.oauthCall('facebook', {
    scope: ['email']
  }));
  app.route('/api/auth/facebook/callback').get(users.oauthCallback('facebook'));

  // Setting the twitter oauth routes
  app.route('/api/auth/twitter').get(users.oauthCall('twitter'));
  app.route('/api/auth/twitter/callback').get(users.oauthCallback('twitter'));

  // Setting the google oauth routes
  app.route('/api/auth/google').get(users.oauthCall('google', {
    scope: [
      'https://www.googleapis.com/auth/userinfo.profile',
      'https://www.googleapis.com/auth/userinfo.email'
    ]
  }));
  app.route('/api/auth/google/callback').get(users.oauthCallback('google'));

  // Setting the linkedin oauth routes
  app.route('/api/auth/linkedin').get(users.oauthCall('linkedin', {
    scope: [
      'r_basicprofile',
      'r_emailaddress'
    ]
  }));
  app.route('/api/auth/linkedin/callback').get(users.oauthCallback('linkedin'));

  // Setting the github oauth routes
  app.route('/api/auth/github').get(users.oauthCall('github'));
  app.route('/api/auth/github/callback').get(users.oauthCallback('github'));

  // Setting the paypal oauth routes
  app.route('/api/auth/paypal').get(users.oauthCall('paypal'));
  app.route('/api/auth/paypal/callback').get(users.oauthCallback('paypal'));
};
<|endoftext|>


<commit_before>var alexa = require('alexa-app');
var request = require('request-promise');
var express = require('express');
var nodecache = require('node-cache');

// Create instance of express
var express_app = express();
// Create a 1 hour cache for storing user devices
var cache = new nodecache({ stdTTL: 3600, checkperiod: 120 });
// Create instance of alexa-app
var app = new alexa.app('connect');
// Bind alexa-app to express instance
app.express({ expressApp: express_app });

// Run every time the skill is accessed
app.pre = function (req, res, type) {
    // Error if the application ID of the request is not for this skill
    if (req.applicationId != "amzn1.ask.skill.33d79728-0f5a-44e7-ae22-ccf0b0c0e9e0" &&
        req.getSession().details.application.applicationId != "amzn1.ask.skill.33d79728-0f5a-44e7-ae22-ccf0b0c0e9e0") {
        throw "Invalid applicationId";
    }
    // Check that the user has an access token, if they have linked their account
    if (!req.getSession().details.user.accessToken) {
        res.say("You have not linked your Spotify account, check your Alexa app to link the account");
        res.linkAccount();
    }
};

// Function for when skill is invoked without intent
app.launch(function (req, res) {
    res.say("I can control your Spotify Connect devices, to start, ask me to list your devices");
    res.reprompt("To start, ask me to list your devices");
    // Keep session open
    res.shouldEndSession(false);
});

// Handle default Amazon help intent
// No slots or utterances required
app.intent("AMAZON.HelpIntent", {
    "slots": {},
    "utterances": []
}, function (req, res) {
    res.say("You can ask me to list your connect devices and then control them. ")
    res.say("For example, tell me to play on a device number after listing devices");
    res.reprompt("What would you like to do?");
    // Keep session open
    res.shouldEndSession(false);
    return;
});

// Handle default Amazon stop intent
// No slots or utterances required
app.intent("AMAZON.StopIntent", {
    "slots": {},
    "utterances": []
}, function (req, res) {
    return;
});

// Handle default Amazon cancel intent
// No slots or utterances required
app.intent("AMAZON.CancelIntent", {
    "slots": {},
    "utterances": []
}, function (req, res) {
    return;
});

// Handle play intent
// No slots required
app.intent('PlayIntent', {
    "utterances": [
        "play",
        "resume",
        "continue"
    ]
},
    function (req, res) {
        // PUT to Spotify REST API
        var statusCode;
        return request.put("https://api.spotify.com/v1/me/player/play", {
            resolveWithFullResponse: true
        }).auth(null, null, true, req.getSession().details.user.accessToken)
            .then((r) => {
                req.getSession().set("statusCode", r.statusCode);
            });
    }
);

// Handle pause intent
// No slots required
app.intent('PauseIntent', {
    "utterances": [
        "pause"
    ]
},
    function (req, res) {
        // PUT to Spotify REST API
        return request.put("https://api.spotify.com/v1/me/player/pause", {
            resolveWithFullResponse: true
        }).auth(null, null, true, req.getSession().details.user.accessToken)
            .then((r) => {
                req.getSession().set("statusCode", r.statusCode);
            });
    }
);

// Handle skip next intent
// No slots required
app.intent('SkipNextIntent', {
    "utterances": [
        "skip",
        "next",
        "forwards"
    ]
},
    function (req, res) {
        // POST to Spotify REST API
        return request.post("https://api.spotify.com/v1/me/player/next", {
            resolveWithFullResponse: true
        }).auth(null, null, true, req.getSession().details.user.accessToken)
            .then((r) => {
                req.getSession().set("statusCode", r.statusCode);
            });
    }
);

// Handle skip previous intent
// No slots required
app.intent('SkipPreviousIntent', {
    "utterances": [
        "previous",
        "last",
        "back",
        "backwards"
    ]
},
    function (req, res) {
        // POST to Spotify REST API
        return request.post("https://api.spotify.com/v1/me/player/previous", {
            resolveWithFullResponse: true
        }).auth(null, null, true, req.getSession().details.user.accessToken)
            .then((r) => {
                req.getSession().set("statusCode", r.statusCode);
            });
    }
);

// Handle volume level intent
// Slot for new volume
app.intent('VolumeLevelIntent', {
    "slots": {
        "VOLUMELEVEL": "AMAZON.NUMBER"
    },
    "utterances": [
        "{set the|set|} volume {level|} {to|} {-|VOLUMELEVEL}"
    ]
},
    function (req, res) {
        // Check that request contains session
        if (req.hasSession()) {
            // Check that the slot has a value
            if (req.slot("VOLUMELEVEL")) {
                // Check if the slot is a number
                if (!isNaN(req.slot("VOLUMELEVEL"))) {
                    var volumeLevel = req.slot("VOLUMELEVEL");
                    // Check that the volume is valid
                    if (volumeLevel >= 0 && volumeLevel <= 10) {
                        // PUT to Spotify REST API
                        request.put({
                            // Send new volume * 10 (convert to percentage)
                            url: "https://api.spotify.com/v1/me/player/volume?volume_percent=" + 10 * volumeLevel,
                            // Send access token as bearer auth
                            auth: {
                                "bearer": req.getSession().details.user.accessToken
                            },
                            // Handle sending as JSON
                            json: true
                        });
                    }
                    else {
                        // If not valid volume
                        res.say("You can only set the volume between 0 and 10");
                        // Keep session open
                        res.shouldEndSession(false);
                    }
                }
                else {
                    // Not a number
                    res.say("Try setting a volume between 1 and 10");
                    res.reprompt("What would you like to do?");
                    // Keep session open
                    res.shouldEndSession(false);
                }
            }
            else {
                // No slot value
                res.say("I could't work out the volume to use.")
                res.say("Try setting a volume between 1 and 10");
                res.reprompt("What would you like to do?");
                // Keep session open
                res.shouldEndSession(false);
            }
        }
    }
);

// Handle get devices intent
// No slots required
app.intent('GetDevicesIntent', {
    "utterances": [
        "devices",
        "list",
        "search",
        "find"
    ]
},
    function (req, res) {
        // GET from Spotify REST API
        return request.get({
            url: "https://api.spotify.com/v1/me/player/devices",
            // Send access token as bearer auth
            auth: {
                "bearer": req.getSession().details.user.accessToken
            },
            // Parse results as JSON
            json: true
        })
            .then(function (body) {
                var devices = body.devices || [];
                var deviceNames = [];
                for (var i = 0; i < devices.length; i++) {
                    // Number each device
                    deviceNames.push((i + 1) + ". " + devices[i].name);
                    // Add the device number to JSON
                    devices[i].number = (i + 1);
                }
                req.getSession().set("devices", devices);
                cache.set(req.getSession().details.user.userId + ":devices", devices);
                // Check if user has devices
                if (devices.length > 0) {
                    // Comma separated list of device names
                    res.say("I found these connect devices: ");
                    res.say([deviceNames.slice(0, -1).join(', '), deviceNames.slice(-1)[0]].join(deviceNames.length < 2 ? '' : ', and ') + ". ");
                    res.say("What would you like to do with these devices?").reprompt("What would you like to do?");
                    // Keep session open
                    res.shouldEndSession(false);
                }
                else {
                    // No devices found
                    res.say("I couldn't find any connect devices, check your Alexa app for information on connecting a device");
                    res.card({
                        type: "Simple",
                        title: "Connecting to a device using Spotify Connect",
                        content: "To add a device to Spotify Connect,"
                            + " log in to your Spotify account on a supported device"
                            + " such as an Echo, phone, or computer"
                            + "\nhttps://support.spotify.com/uk/article/spotify-connect/"
                    });
                }
            })
            // Handle errors
            .catch(function (err) {
                console.error('error:', err.message);
            });
    }
);

// Handle device play intent
// Slot for device number
app.intent('DevicePlayIntent', {
    "slots": {
        "DEVICENUMBER": "AMAZON.NUMBER"
    },
    "utterances": [
        "play on {number|device|device number|} {-|DEVICENUMBER}"
    ]
},
    function (req, res) {
        // Check that request contains session
        if (req.hasSession()) {
            // Check that the slot has a value
            if (req.slot("DEVICENUMBER")) {
                // Check if the slot is a number
                if (!isNaN(req.slot("DEVICENUMBER"))) {
                    var deviceNumber = req.slot("DEVICENUMBER");
                    // Check if session is new
                    if (req.getSession().isNew()) {
                        // If new session try to use cache
                        var devices = cache.get(req.getSession().details.user.userId + ":devices") || [];
                    }
                    else {
                        // If existing session use session data
                        var devices = req.getSession().get("devices") || [];
                    }
                    var deviceId, deviceName;
                    // Iterate through devices to find ID and name by number
                    for (var i = 0; i < devices.length; i++) {
                        if (devices[i].number == deviceNumber) {
                            deviceId = devices[i].id;
                            deviceName = devices[i].name;
                        }
                    }
                    // Check that the device for the number was found
                    if (deviceId) {
                        // PUT to Spotify REST API
                        request.put({
                            url: "https://api.spotify.com/v1/me/player",
                            // Send access token as bearer auth
                            auth: {
                                "bearer": req.getSession().details.user.accessToken
                            },
                            body: {
                                // Send device ID
                                "device_ids": [
                                    deviceId
                                ],
                                // Make sure that music plays
                                "play": true
                            },
                            // Handle sending as JSON
                            json: true
                        });
                        res.say("Playing on device " + deviceNumber + ": " + deviceName);
                    }
                    else {
                        // If device for number not found
                        res.say("I couldn't find device " + deviceNumber + ". ");
                        res.say("Try asking me to list devices first");
                        // Keep session open
                        res.shouldEndSession(false);
                    }
                }
                else {
                    // Not a number
                    res.say("I couldn't work out which device to play on, make sure you refer to the device by number.");
                    res.say("Try asking me to play on a device number");
                    res.reprompt("What would you like to do?");
                    // Keep session open
                    res.shouldEndSession(false);
                }
            }
            else {
                // No slot value
                res.say("I couldn't work out which device number to play on.");
                res.say("Try asking me to play on a device number");
                res.reprompt("What would you like to do?");
                // Keep session open
                res.shouldEndSession(false);
            }
        }
    }
);

// Handle device transfer intent
// Slot for device number
app.intent('DeviceTransferIntent', {
    "slots": {
        "DEVICENUMBER": "AMAZON.NUMBER"
    },
    "utterances": [
        "transfer to {number|device|device number|} {-|DEVICENUMBER}"
    ]
},
    function (req, res) {
        // Check that request contains session
        if (req.hasSession()) {
            // Check that the slot has a value
            if (req.slot("DEVICENUMBER")) {
                // Check if the slot is a number
                if (!isNaN(req.slot("DEVICENUMBER"))) {
                    var deviceNumber = req.slot("DEVICENUMBER");
                    // Check if session is new
                    if (req.getSession().isNew()) {
                        // If new session try to use cache
                        var devices = cache.get(req.getSession().details.user.userId + ":devices") || [];
                    }
                    else {
                        // If existing session use session data
                        var devices = req.getSession().get("devices") || [];
                    }
                    var deviceId, deviceName;
                    // Iterate through devices to find ID and name by number
                    for (var i = 0; i < devices.length; i++) {
                        if (devices[i].number == deviceNumber) {
                            deviceId = devices[i].id;
                            deviceName = devices[i].name;
                        }
                    }
                    // Check that the device for the number was found
                    if (deviceId) {
                        // PUT to Spotify REST API
                        request.put({
                            url: "https://api.spotify.com/v1/me/player",
                            // Send access token as bearer auth
                            auth: {
                                "bearer": req.getSession().details.user.accessToken
                            },
                            body: {
                                // Send device ID
                                "device_ids": [
                                    deviceId
                                ]
                            },
                            // Handle sending as JSON
                            json: true
                        });
                        res.say("Transferring to device " + deviceNumber + ": " + deviceName);
                    }
                    else {
                        // If device for number not found
                        res.say("I couldn't find device " + deviceNumber + ". ");
                        res.say("Try asking me to list devices first");
                        // Keep session open
                        res.shouldEndSession(false);
                    }
                }
                else {
                    // Not a number
                    res.say("I couldn't work out which device to transfer to, make sure you refer to the device by number.");
                    res.say("Try asking me to to transfer a device number");
                    res.reprompt("What would you like to do?");
                    // Keep session open
                    res.shouldEndSession(false);
                }
            }
            else {
                // No slot value
                res.say("I couldn't work out which device number to transfer to.");
                res.say("Try asking me to transfer to a device number");
                res.reprompt("What would you like to do?");
                // Keep session open
                res.shouldEndSession(false);
            }
        }
    }
);

// Handle get track intent
// No slots required
app.intent('GetTrackIntent', {
    "utterances": [
        "{what is|what's} {playing|this song}",
        "what {song|track|} is this"
    ]
},
    function (req, res) {
        // GET from Spotify REST API
        return request.get({
            url: "https://api.spotify.com/v1/me/player/currently-playing",
            // Send access token as bearer auth
            auth: {
                "bearer": req.getSession().details.user.accessToken
            },
            // Parse results as JSON
            json: true
        })
            .then(function (body) {
                if (body.is_playing) {
                    res.say("This is " + body.item.name + " by " + body.item.artists[0].name);
                }
                else {
                    if (body.item.name) {
                        // If not playing but last track known
                        res.say("That was " + body.item.name + " by " + body.item.artists[0].name);
                    }
                    else {
                        // If unknown
                        res.say("Nothing is playing");
                    }
                }
            })
            // Handle errors
            .catch(function (err) {
                console.error('error:', err.message);
            });
    }
);

// Set up redirect to project page
express_app.use(express.static(__dirname));
express_app.get('/', function (req, res) {
    res.redirect('https://github.com/thorpelawrence/alexa-spotify-connect');
});

// Only listen if run directly, not if required as a module
if (require.main === module) {
    var port = process.env.PORT || 8888;
    console.log("Listening on port " + port);
    express_app.listen(port);
}

// Export alexa-app instance for skill.js
module.exports = app;
<commit_msg>Add post handling<commit_after>var alexa = require('alexa-app');
var request = require('request-promise');
var express = require('express');
var nodecache = require('node-cache');

// Create instance of express
var express_app = express();
// Create a 1 hour cache for storing user devices
var cache = new nodecache({ stdTTL: 3600, checkperiod: 120 });
// Create instance of alexa-app
var app = new alexa.app('connect');
// Bind alexa-app to express instance
app.express({ expressApp: express_app });

// Run every time the skill is accessed
app.pre = function (req, res, type) {
    // Error if the application ID of the request is not for this skill
    if (req.applicationId != "amzn1.ask.skill.33d79728-0f5a-44e7-ae22-ccf0b0c0e9e0" &&
        req.getSession().details.application.applicationId != "amzn1.ask.skill.33d79728-0f5a-44e7-ae22-ccf0b0c0e9e0") {
        throw "Invalid applicationId";
    }
    // Check that the user has an access token, if they have linked their account
    if (!req.getSession().details.user.accessToken) {
        res.say("You have not linked your Spotify account, check your Alexa app to link the account");
        res.linkAccount();
    }
};

// Run after every request
app.post = function (req, res, type, exception) {
    if (exception) {
        return res.clear().say("An error occured: " + exception).send();
    }
};

// Function for when skill is invoked without intent
app.launch(function (req, res) {
    res.say("I can control your Spotify Connect devices, to start, ask me to list your devices");
    res.reprompt("To start, ask me to list your devices");
    // Keep session open
    res.shouldEndSession(false);
});

// Handle default Amazon help intent
// No slots or utterances required
app.intent("AMAZON.HelpIntent", {
    "slots": {},
    "utterances": []
}, function (req, res) {
    res.say("You can ask me to list your connect devices and then control them. ")
    res.say("For example, tell me to play on a device number after listing devices");
    res.reprompt("What would you like to do?");
    // Keep session open
    res.shouldEndSession(false);
    return;
});

// Handle default Amazon stop intent
// No slots or utterances required
app.intent("AMAZON.StopIntent", {
    "slots": {},
    "utterances": []
}, function (req, res) {
    return;
});

// Handle default Amazon cancel intent
// No slots or utterances required
app.intent("AMAZON.CancelIntent", {
    "slots": {},
    "utterances": []
}, function (req, res) {
    return;
});

// Handle play intent
// No slots required
app.intent('PlayIntent', {
    "utterances": [
        "play",
        "resume",
        "continue"
    ]
},
    function (req, res) {
        // PUT to Spotify REST API
        var statusCode;
        return request.put("https://api.spotify.com/v1/me/player/play", {
            resolveWithFullResponse: true
        }).auth(null, null, true, req.getSession().details.user.accessToken)
            .then((r) => {
                req.getSession().set("statusCode", r.statusCode);
            });
    }
);

// Handle pause intent
// No slots required
app.intent('PauseIntent', {
    "utterances": [
        "pause"
    ]
},
    function (req, res) {
        // PUT to Spotify REST API
        return request.put("https://api.spotify.com/v1/me/player/pause", {
            resolveWithFullResponse: true
        }).auth(null, null, true, req.getSession().details.user.accessToken)
            .then((r) => {
                req.getSession().set("statusCode", r.statusCode);
            });
    }
);

// Handle skip next intent
// No slots required
app.intent('SkipNextIntent', {
    "utterances": [
        "skip",
        "next",
        "forwards"
    ]
},
    function (req, res) {
        // POST to Spotify REST API
        return request.post("https://api.spotify.com/v1/me/player/next", {
            resolveWithFullResponse: true
        }).auth(null, null, true, req.getSession().details.user.accessToken)
            .then((r) => {
                req.getSession().set("statusCode", r.statusCode);
            });
    }
);

// Handle skip previous intent
// No slots required
app.intent('SkipPreviousIntent', {
    "utterances": [
        "previous",
        "last",
        "back",
        "backwards"
    ]
},
    function (req, res) {
        // POST to Spotify REST API
        return request.post("https://api.spotify.com/v1/me/player/previous", {
            resolveWithFullResponse: true
        }).auth(null, null, true, req.getSession().details.user.accessToken)
            .then((r) => {
                req.getSession().set("statusCode", r.statusCode);
            });
    }
);

// Handle volume level intent
// Slot for new volume
app.intent('VolumeLevelIntent', {
    "slots": {
        "VOLUMELEVEL": "AMAZON.NUMBER"
    },
    "utterances": [
        "{set the|set|} volume {level|} {to|} {-|VOLUMELEVEL}"
    ]
},
    function (req, res) {
        // Check that request contains session
        if (req.hasSession()) {
            // Check that the slot has a value
            if (req.slot("VOLUMELEVEL")) {
                // Check if the slot is a number
                if (!isNaN(req.slot("VOLUMELEVEL"))) {
                    var volumeLevel = req.slot("VOLUMELEVEL");
                    // Check that the volume is valid
                    if (volumeLevel >= 0 && volumeLevel <= 10) {
                        // PUT to Spotify REST API
                        request.put({
                            // Send new volume * 10 (convert to percentage)
                            url: "https://api.spotify.com/v1/me/player/volume?volume_percent=" + 10 * volumeLevel,
                            // Send access token as bearer auth
                            auth: {
                                "bearer": req.getSession().details.user.accessToken
                            },
                            // Handle sending as JSON
                            json: true
                        });
                    }
                    else {
                        // If not valid volume
                        res.say("You can only set the volume between 0 and 10");
                        // Keep session open
                        res.shouldEndSession(false);
                    }
                }
                else {
                    // Not a number
                    res.say("Try setting a volume between 1 and 10");
                    res.reprompt("What would you like to do?");
                    // Keep session open
                    res.shouldEndSession(false);
                }
            }
            else {
                // No slot value
                res.say("I could't work out the volume to use.")
                res.say("Try setting a volume between 1 and 10");
                res.reprompt("What would you like to do?");
                // Keep session open
                res.shouldEndSession(false);
            }
        }
    }
);

// Handle get devices intent
// No slots required
app.intent('GetDevicesIntent', {
    "utterances": [
        "devices",
        "list",
        "search",
        "find"
    ]
},
    function (req, res) {
        // GET from Spotify REST API
        return request.get({
            url: "https://api.spotify.com/v1/me/player/devices",
            // Send access token as bearer auth
            auth: {
                "bearer": req.getSession().details.user.accessToken
            },
            // Parse results as JSON
            json: true
        })
            .then(function (body) {
                var devices = body.devices || [];
                var deviceNames = [];
                for (var i = 0; i < devices.length; i++) {
                    // Number each device
                    deviceNames.push((i + 1) + ". " + devices[i].name);
                    // Add the device number to JSON
                    devices[i].number = (i + 1);
                }
                req.getSession().set("devices", devices);
                cache.set(req.getSession().details.user.userId + ":devices", devices);
                // Check if user has devices
                if (devices.length > 0) {
                    // Comma separated list of device names
                    res.say("I found these connect devices: ");
                    res.say([deviceNames.slice(0, -1).join(', '), deviceNames.slice(-1)[0]].join(deviceNames.length < 2 ? '' : ', and ') + ". ");
                    res.say("What would you like to do with these devices?").reprompt("What would you like to do?");
                    // Keep session open
                    res.shouldEndSession(false);
                }
                else {
                    // No devices found
                    res.say("I couldn't find any connect devices, check your Alexa app for information on connecting a device");
                    res.card({
                        type: "Simple",
                        title: "Connecting to a device using Spotify Connect",
                        content: "To add a device to Spotify Connect,"
                            + " log in to your Spotify account on a supported device"
                            + " such as an Echo, phone, or computer"
                            + "\nhttps://support.spotify.com/uk/article/spotify-connect/"
                    });
                }
            })
            // Handle errors
            .catch(function (err) {
                console.error('error:', err.message);
            });
    }
);

// Handle device play intent
// Slot for device number
app.intent('DevicePlayIntent', {
    "slots": {
        "DEVICENUMBER": "AMAZON.NUMBER"
    },
    "utterances": [
        "play on {number|device|device number|} {-|DEVICENUMBER}"
    ]
},
    function (req, res) {
        // Check that request contains session
        if (req.hasSession()) {
            // Check that the slot has a value
            if (req.slot("DEVICENUMBER")) {
                // Check if the slot is a number
                if (!isNaN(req.slot("DEVICENUMBER"))) {
                    var deviceNumber = req.slot("DEVICENUMBER");
                    // Check if session is new
                    if (req.getSession().isNew()) {
                        // If new session try to use cache
                        var devices = cache.get(req.getSession().details.user.userId + ":devices") || [];
                    }
                    else {
                        // If existing session use session data
                        var devices = req.getSession().get("devices") || [];
                    }
                    var deviceId, deviceName;
                    // Iterate through devices to find ID and name by number
                    for (var i = 0; i < devices.length; i++) {
                        if (devices[i].number == deviceNumber) {
                            deviceId = devices[i].id;
                            deviceName = devices[i].name;
                        }
                    }
                    // Check that the device for the number was found
                    if (deviceId) {
                        // PUT to Spotify REST API
                        request.put({
                            url: "https://api.spotify.com/v1/me/player",
                            // Send access token as bearer auth
                            auth: {
                                "bearer": req.getSession().details.user.accessToken
                            },
                            body: {
                                // Send device ID
                                "device_ids": [
                                    deviceId
                                ],
                                // Make sure that music plays
                                "play": true
                            },
                            // Handle sending as JSON
                            json: true
                        });
                        res.say("Playing on device " + deviceNumber + ": " + deviceName);
                    }
                    else {
                        // If device for number not found
                        res.say("I couldn't find device " + deviceNumber + ". ");
                        res.say("Try asking me to list devices first");
                        // Keep session open
                        res.shouldEndSession(false);
                    }
                }
                else {
                    // Not a number
                    res.say("I couldn't work out which device to play on, make sure you refer to the device by number.");
                    res.say("Try asking me to play on a device number");
                    res.reprompt("What would you like to do?");
                    // Keep session open
                    res.shouldEndSession(false);
                }
            }
            else {
                // No slot value
                res.say("I couldn't work out which device number to play on.");
                res.say("Try asking me to play on a device number");
                res.reprompt("What would you like to do?");
                // Keep session open
                res.shouldEndSession(false);
            }
        }
    }
);

// Handle device transfer intent
// Slot for device number
app.intent('DeviceTransferIntent', {
    "slots": {
        "DEVICENUMBER": "AMAZON.NUMBER"
    },
    "utterances": [
        "transfer to {number|device|device number|} {-|DEVICENUMBER}"
    ]
},
    function (req, res) {
        // Check that request contains session
        if (req.hasSession()) {
            // Check that the slot has a value
            if (req.slot("DEVICENUMBER")) {
                // Check if the slot is a number
                if (!isNaN(req.slot("DEVICENUMBER"))) {
                    var deviceNumber = req.slot("DEVICENUMBER");
                    // Check if session is new
                    if (req.getSession().isNew()) {
                        // If new session try to use cache
                        var devices = cache.get(req.getSession().details.user.userId + ":devices") || [];
                    }
                    else {
                        // If existing session use session data
                        var devices = req.getSession().get("devices") || [];
                    }
                    var deviceId, deviceName;
                    // Iterate through devices to find ID and name by number
                    for (var i = 0; i < devices.length; i++) {
                        if (devices[i].number == deviceNumber) {
                            deviceId = devices[i].id;
                            deviceName = devices[i].name;
                        }
                    }
                    // Check that the device for the number was found
                    if (deviceId) {
                        // PUT to Spotify REST API
                        request.put({
                            url: "https://api.spotify.com/v1/me/player",
                            // Send access token as bearer auth
                            auth: {
                                "bearer": req.getSession().details.user.accessToken
                            },
                            body: {
                                // Send device ID
                                "device_ids": [
                                    deviceId
                                ]
                            },
                            // Handle sending as JSON
                            json: true
                        });
                        res.say("Transferring to device " + deviceNumber + ": " + deviceName);
                    }
                    else {
                        // If device for number not found
                        res.say("I couldn't find device " + deviceNumber + ". ");
                        res.say("Try asking me to list devices first");
                        // Keep session open
                        res.shouldEndSession(false);
                    }
                }
                else {
                    // Not a number
                    res.say("I couldn't work out which device to transfer to, make sure you refer to the device by number.");
                    res.say("Try asking me to to transfer a device number");
                    res.reprompt("What would you like to do?");
                    // Keep session open
                    res.shouldEndSession(false);
                }
            }
            else {
                // No slot value
                res.say("I couldn't work out which device number to transfer to.");
                res.say("Try asking me to transfer to a device number");
                res.reprompt("What would you like to do?");
                // Keep session open
                res.shouldEndSession(false);
            }
        }
    }
);

// Handle get track intent
// No slots required
app.intent('GetTrackIntent', {
    "utterances": [
        "{what is|what's} {playing|this song}",
        "what {song|track|} is this"
    ]
},
    function (req, res) {
        // GET from Spotify REST API
        return request.get({
            url: "https://api.spotify.com/v1/me/player/currently-playing",
            // Send access token as bearer auth
            auth: {
                "bearer": req.getSession().details.user.accessToken
            },
            // Parse results as JSON
            json: true
        })
            .then(function (body) {
                if (body.is_playing) {
                    res.say("This is " + body.item.name + " by " + body.item.artists[0].name);
                }
                else {
                    if (body.item.name) {
                        // If not playing but last track known
                        res.say("That was " + body.item.name + " by " + body.item.artists[0].name);
                    }
                    else {
                        // If unknown
                        res.say("Nothing is playing");
                    }
                }
            })
            // Handle errors
            .catch(function (err) {
                console.error('error:', err.message);
            });
    }
);

// Set up redirect to project page
express_app.use(express.static(__dirname));
express_app.get('/', function (req, res) {
    res.redirect('https://github.com/thorpelawrence/alexa-spotify-connect');
});

// Only listen if run directly, not if required as a module
if (require.main === module) {
    var port = process.env.PORT || 8888;
    console.log("Listening on port " + port);
    express_app.listen(port);
}

// Export alexa-app instance for skill.js
module.exports = app;
<|endoftext|>


<commit_before>window.requestAnimationFrame = window.requestAnimationFrame || (
function() {
	return  window.webkitRequestAnimationFrame ||
			window.mozRequestAnimationFrame ||
			window.oRequestAnimationFrame ||
			window.msRequestAnimationFrame ||
			function( callback, element) {
				window.setTimeout(callback, 1000 / 60);
			};
})();

var canvas,
		gl,
		width, height, numFrames, full_game,
		turn_number, counter,
		vertex_buffer, color_buffer,
		blankColor, player1Color, player2Color, dimFactor,
		vertex_locations, player1Color, player2Color,
		vertex_shader, fragment_shader,
		currentProgram,
		vertex_position, color_position,
		parameters = {  start_time  : new Date().getTime(),
						time        : 0,
						screenWidth : 0,
						screenHeight: 0 };

function begin(data) {
	var meta = data.split("\n")[0].split(" ")
	var restOfFile = data.substring(data.indexOf("\n") + 1).replace(/[^0-9]/g, "");
	var w = meta[0]
	var h = meta[1]
	var nf = meta[2]
	var fc = restOfFile

	vertex_shader = document.getElementById('vs').textContent;
	fragment_shader = document.getElementById('fs').textContent;

	canvas = document.querySelector('canvas');

	//Get width, height, and number of frames from file. Note that those are already declared as global variables.
	width = w;
	height = h;
	numFrames = nf;

	//Parse 1D array:
	full_game = [];
	var loc = 0;
	for(var a = 0; a < numFrames; a++) {
		var frame = [];
		for(var b = 0; b < height * width; b++) {
			frame.push(fc[loc]);
			loc++;
		}
		full_game.push(frame);
	}

	//Set turn number to be 0;
	turn_number = 0;
	counter = 0;

	//Initialise WebGL
	try {
		gl = canvas.getContext('experimental-webgl');
	} catch(error) { }

	if (!gl) {
		throw "cannot create webgl context";
	}

	vl = [];
	var dx = 2.0 / width, dy = 2.0 / height, xSize = 0.4 * dx, ySize = 0.4 * dy;
	for(var yPos = -1 + (dy / 2); yPos < 1; yPos += dy) {
		for(var xPos = -1 + (dx / 2); xPos < 1; xPos += dx) {
			vl.push(xPos - xSize);
			vl.push(yPos - ySize);
			vl.push(xPos + xSize);
			vl.push(yPos + ySize);
			vl.push(xPos + xSize);
			vl.push(yPos - ySize);
			vl.push(xPos - xSize);
			vl.push(yPos - ySize);
			vl.push(xPos + xSize);
			vl.push(yPos + ySize);
			vl.push(xPos - xSize);
			vl.push(yPos + ySize);
		}
	}
	var vertex_locations = new Float32Array(vl);

	//Get player colors from file.
	player1Color = new Float32Array([ 1.0, 0.0, 0.0 ]);
	player2Color = new Float32Array([ 0.0, 0.0, 1.0 ]);
	blankColor = new Float32Array([ 0.5, 0.5, 0.5 ]);
	dimFactor = 0.3;

	//Create Vertex buffer
	vertex_buffer = gl.createBuffer();
	gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
	gl.bufferData(gl.ARRAY_BUFFER, vertex_locations, gl.STATIC_DRAW);
	//Create Color buffer
	color_buffer = gl.createBuffer();

	//Create Program
	currentProgram = createProgram(vertex_shader, fragment_shader);

	//Find vertex and color positions.
	vertex_position = gl.getAttribLocation(currentProgram, "position");
	color_position = gl.getAttribLocation(currentProgram, "vcolor");

	onWindowResize();
	window.addEventListener('resize', onWindowResize, false);

	animate();
}

function createProgram(vertex, fragment) {

	var program = gl.createProgram();

	var vs = createShader(vertex, gl.VERTEX_SHADER);
	var fs = createShader('#ifdef GL_ES\nprecision highp float;\n#endif\n\n' + fragment, gl.FRAGMENT_SHADER);

	if (vs == null || fs == null) return null;

	gl.attachShader(program, vs);
	gl.attachShader(program, fs);

	gl.deleteShader(vs);
	gl.deleteShader(fs);

	gl.linkProgram(program);

	if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {

		alert("ERROR:\n" +
		"VALIDATE_STATUS: " + gl.getProgramParameter(program, gl.VALIDATE_STATUS) + "\n" +
		"ERROR: " + gl.getError() + "\n\n" +
		"- Vertex Shader -\n" + vertex + "\n\n" +
		"- Fragment Shader -\n" + fragment);

		return null;

	}

	return program;

}

function createShader(src, type) {

	var shader = gl.createShader(type);

	gl.shaderSource(shader, src);
	gl.compileShader(shader);

	if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {

		alert((type == gl.VERTEX_SHADER ? "VERTEX" : "FRAGMENT") + " SHADER:\n" + gl.getShaderInfoLog(shader));
		return null;

	}

	return shader;
}

function onWindowResize(event) {
	canvas.width = 400;
	canvas.height = 400;

	parameters.screenWidth = canvas.width;
	parameters.screenHeight = canvas.height;

	gl.viewport(0, 0, canvas.width, canvas.height);
}

function animate() {
	if(turn_number < numFrames - 1) requestAnimationFrame(animate);
	counter++;
	if(turn_number < numFrames - 1 && counter % 10 == 0) turn_number++;
	nextFrame();
	render();
}

function nextFrame() {
	var map = full_game[turn_number];

	var colors = [];
	for(var a = 0; a < map.length; a++) {
		for(var b = 0; b < 6; b++) { //Fill in for each vertex:
			if(map[a] == 1) {
				colors.push(player1Color[0]);
				colors.push(player1Color[1]);
				colors.push(player1Color[2]);
			}
			else if(map[a] == 3) {
				colors.push(player1Color[0] * dimFactor);
				colors.push(player1Color[1] * dimFactor);
				colors.push(player1Color[2] * dimFactor);
			}
			else if(map[a] == 2) {
				colors.push(player2Color[0]);
				colors.push(player2Color[1]);
				colors.push(player2Color[2]);
			}
			else if(map[a] == 4) {
				colors.push(player2Color[0] * dimFactor);
				colors.push(player2Color[1] * dimFactor);
				colors.push(player2Color[2] * dimFactor);
			}
			else {
				colors.push(blankColor[0]);
				colors.push(blankColor[1]);
				colors.push(blankColor[2]);
			}
		}
	}

	gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.DYNAMIC_DRAW);
}

function render() {

	if (!currentProgram) return;

	parameters.time = new Date().getTime() - parameters.start_time;

	gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

	//Load program into GPU

	gl.useProgram(currentProgram);

	//Set values to program variables

	//Render geometry
	gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
	gl.vertexAttribPointer(vertex_position, 2, gl.FLOAT, false, 0, 0);
	gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
	gl.vertexAttribPointer(color_position, 3, gl.FLOAT, false, 0, 0);
	gl.enableVertexAttribArray(vertex_position);
	gl.enableVertexAttribArray(color_position);
	gl.drawArrays(gl.TRIANGLES, 0, 1536); //256 * 6
	gl.disableVertexAttribArray(vertex_position);
	gl.disableVertexAttribArray(color_position);
}<commit_msg>Better WebGL Functionality<commit_after>window.requestAnimationFrame = window.requestAnimationFrame ||(
function() {
	return  window.webkitRequestAnimationFrame ||
			window.mozRequestAnimationFrame ||
			window.oRequestAnimationFrame ||
			window.msRequestAnimationFrame ||
			function( callback, element) {
				window.setTimeout(callback, 1000 / 60);
			};
})();

var canvas,
		gl,
		width, height, numFrames, full_game,
		turn_number, counter, play,
		vertex_buffer, color_buffer,
		blankColor, player1Color, player2Color, dimFactor,
		vertex_locations, player1Color, player2Color,
		vertex_shader, fragment_shader,
		currentProgram,
		vertex_position, color_position,
		parameters = {  start_time  : new Date().getTime(),
						time        : 0,
						screenWidth : 0,
						screenHeight: 0 };

function begin(data) {
	var meta = data.split("\n")[0].split(" ")
	var restOfFile = data.substring(data.indexOf("\n") + 1).replace(/[^0-9]/g, "");
	var w = meta[0]
	var h = meta[1]
	var nf = meta[2]
	var fc = restOfFile

	vertex_shader = document.getElementById('vs').textContent;
	fragment_shader = document.getElementById('fs').textContent;

	canvas = document.querySelector('canvas');

	//Get width, height, and number of frames from file. Note that those are already declared as global variables.
	width = w;
	height = h;
	numFrames = nf;

	//Parse 1D array:
	full_game = [];
	var loc = 0;
	for(var a = 0; a < numFrames; a++) {
		var frame = [];
		for(var b = 0; b < height * width; b++) {
			frame.push(fc[loc]);
			loc++;
		}
		full_game.push(frame);
	}

	//Add canvas listener:
	play = true;
	canvas.addEventListener("keypress", keyFunc, false)

	//Set turn number to be 0;
	turn_number = 0;
	counter = 0;

	//Initialise WebGL
	try {
		gl = canvas.getContext('experimental-webgl');
	} catch(error) { }

	if(!gl) {
		throw "cannot create webgl context";
	}

	vl = [];
	var dx = 2.0 / width, dy = 2.0 / height, xSize = 0.4 * dx, ySize = 0.4 * dy;
	for(var yPos = -1 +(dy / 2); yPos < 1; yPos += dy) {
		for(var xPos = -1 +(dx / 2); xPos < 1; xPos += dx) {
			vl.push(xPos - xSize);
			vl.push(yPos - ySize);
			vl.push(xPos + xSize);
			vl.push(yPos + ySize);
			vl.push(xPos + xSize);
			vl.push(yPos - ySize);
			vl.push(xPos - xSize);
			vl.push(yPos - ySize);
			vl.push(xPos + xSize);
			vl.push(yPos + ySize);
			vl.push(xPos - xSize);
			vl.push(yPos + ySize);
		}
	}
	var vertex_locations = new Float32Array(vl);

	//Get player colors from file.
	player1Color = new Float32Array([ 1.0, 0.0, 0.0 ]);
	player2Color = new Float32Array([ 0.0, 0.0, 1.0 ]);
	blankColor = new Float32Array([ 0.5, 0.5, 0.5 ]);
	dimFactor = 0.3;

	//Create Vertex buffer
	vertex_buffer = gl.createBuffer();
	gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
	gl.bufferData(gl.ARRAY_BUFFER, vertex_locations, gl.STATIC_DRAW);
	//Create Color buffer
	color_buffer = gl.createBuffer();

	//Create Program
	currentProgram = createProgram(vertex_shader, fragment_shader);

	//Find vertex and color positions.
	vertex_position = gl.getAttribLocation(currentProgram, "position");
	color_position = gl.getAttribLocation(currentProgram, "vcolor");

	onWindowResize();
	window.addEventListener('resize', onWindowResize, false);

	animate();
}

function keyFunc(e) {
	if(e.keyCode == 37) {
		turn_number--;
	}
	else if(e.keyCode == 39) {
		turn_number++;
	}
	else if(e.keyCode == 32) {
		play = !play;
	}
}

function createProgram(vertex, fragment) {

	var program = gl.createProgram();

	var vs = createShader(vertex, gl.VERTEX_SHADER);
	var fs = createShader('#ifdef GL_ES\nprecision highp float;\n#endif\n\n' + fragment, gl.FRAGMENT_SHADER);

	if(vs == null || fs == null) return null;

	gl.attachShader(program, vs);
	gl.attachShader(program, fs);

	gl.deleteShader(vs);
	gl.deleteShader(fs);

	gl.linkProgram(program);

	if(!gl.getProgramParameter(program, gl.LINK_STATUS)) {

		alert("ERROR:\n" +
		"VALIDATE_STATUS: " + gl.getProgramParameter(program, gl.VALIDATE_STATUS) + "\n" +
		"ERROR: " + gl.getError() + "\n\n" +
		"- Vertex Shader -\n" + vertex + "\n\n" +
		"- Fragment Shader -\n" + fragment);

		return null;

	}

	return program;

}

function createShader(src, type) {

	var shader = gl.createShader(type);

	gl.shaderSource(shader, src);
	gl.compileShader(shader);

	if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {

		alert((type == gl.VERTEX_SHADER ? "VERTEX" : "FRAGMENT") + " SHADER:\n" + gl.getShaderInfoLog(shader));
		return null;

	}

	return shader;
}

function onWindowResize(event) {
	canvas.width = 400;
	canvas.height = 400;

	parameters.screenWidth = canvas.width;
	parameters.screenHeight = canvas.height;

	gl.viewport(0, 0, canvas.width, canvas.height);
}

function animate() {
	if(turn_number < numFrames - 1) requestAnimationFrame(animate);
	counter++;
	if(play && turn_number < numFrames - 1 && counter % 12 == 0) turn_number++;
	nextFrame();
	render();
}

function nextFrame() {
	var map = full_game[turn_number];

	var colors = [];
	for(var a = 0; a < map.length; a++) {
		for(var b = 0; b < 6; b++) { //Fill in for each vertex:
			if(map[a] == 1) {
				colors.push(player1Color[0]);
				colors.push(player1Color[1]);
				colors.push(player1Color[2]);
			}
			else if(map[a] == 3) {
				colors.push(player1Color[0] * dimFactor);
				colors.push(player1Color[1] * dimFactor);
				colors.push(player1Color[2] * dimFactor);
			}
			else if(map[a] == 2) {
				colors.push(player2Color[0]);
				colors.push(player2Color[1]);
				colors.push(player2Color[2]);
			}
			else if(map[a] == 4) {
				colors.push(player2Color[0] * dimFactor);
				colors.push(player2Color[1] * dimFactor);
				colors.push(player2Color[2] * dimFactor);
			}
			else {
				colors.push(blankColor[0]);
				colors.push(blankColor[1]);
				colors.push(blankColor[2]);
			}
		}
	}

	gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.DYNAMIC_DRAW);
}

function render() {

	if(!currentProgram) return;

	parameters.time = new Date().getTime() - parameters.start_time;

	gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

	//Load program into GPU

	gl.useProgram(currentProgram);

	//Set values to program variables

	//Render geometry
	gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
	gl.vertexAttribPointer(vertex_position, 2, gl.FLOAT, false, 0, 0);
	gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
	gl.vertexAttribPointer(color_position, 3, gl.FLOAT, false, 0, 0);
	gl.enableVertexAttribArray(vertex_position);
	gl.enableVertexAttribArray(color_position);
	gl.drawArrays(gl.TRIANGLES, 0, 1536); //256 * 6
	gl.disableVertexAttribArray(vertex_position);
	gl.disableVertexAttribArray(color_position);
}<|endoftext|>


<commit_before>"use strict";

const parseEnvironment = require('../lib/helpers/environment-parser');

const AWS = require('aws-sdk');
const chalk = require('chalk');
const path = require('path');
const program = require('commander');
const Promise = require('bluebird');
const prompt = require('readline-sync');
const _ = require('lodash');

const config = require('../lib/helpers/config');
const hype = require('../lib/helpers/lambdahype');
const logger = require('../lib/helpers/logger').shared;

const setup = require('../lib/deploy/setup-step');
const processing = require('../lib/deploy/bundle-lambdas-step');
const deriveAPI = require('../lib/deploy/derive-api-step');
const deriveStack = require('../lib/deploy/derive-stack-step');
const fetchStack = require('../lib/deploy/fetch-stack-step');
const deployStack = require('../lib/deploy/update-stack-step');

//
//  Program specification
//

program
    .option('-n, --project-name <name>', 'Project name')
    .option('-s, --stage <stage>', 'Stage name')
    .option('-r, --region <region>', 'Region')
    .option('-e, --environment <env>', 'Environment Variables to embed as key-value pairs', parseEnvironment, {})
    .option('--dry-run', 'Simply generate files that would be used to update the stack and API')
    .option('--exclude <list>', 'Packages to exclude from bundling', function(val) { return val.split(','); })
    .option('-o, --optimization <level>', 'Optimization level to use, valid values are 0-1', parseInt, 1)
    .option('--clean', 'Force a clean build where cached bundles are not used')
    .option('--no-color', 'Turn off ANSI coloring in output');

program.on('--help', function() {
    console.log('  Examples:');
    console.log();
    console.log('    Generate deployment files (in staging directory), but don\'t actually deploy');
    console.log('    $ lambda deploy --dry-run');
    console.log();
    console.log('    Deploy to \'prod\' stage with NODE_ENV set to \'production\' and FOO set to \'bar\'')
    console.log('    $ lambda deploy -s prod -e NODE_ENV=production,FOO=bar');
    console.log();
    console.log('    Deploy to default (dev) stage excluding \'example\' package from the bundle (included in the ZIP separately)');
    console.log('    $ lambda deploy --exclude example');
    console.log();
    console.log('    Deploy to default stage, ignoring cached bundles and disabling minification');
    console.log('    $ lambda deploy --clean --optimization 0');
    console.log();
});

program.parse(process.argv);

//
// Configure program
//

// Enable/Disable colors
chalk.enabled = program.color;

// Finalise configuration
program.region = program.region || config.aws.region;
program.stage = program.stage || config.aws.stage;
program.projectName = program.projectName || config.project.name || prompt.question('Please enter project name: ');

// Make region global for AWS
AWS.config.region = program.region;

// Carry over some stuff to environment
program.environment["AWS_REGION"] = program.region;
program.environment["AWS_STAGE"] = program.stage;
program.environment["AWS_PROJECT_NAME"] = program.projectName;

//
// Main logic
//
const context = {
    directories: {
        cwd: process.cwd(),
        root: path.join(path.resolve(__dirname), '../lib/deploy')
    },

    project: {
        name: program.projectName,
        stage: program.stage,
        region: program.region,
        timestamp: Math.floor(Date.now() / 1000)
    },

    program: _.pick(program, ['environment', 'stage', 'region', 'lambda', 'optimization', 'exclude', 'clean']),
    logger: logger
};

// Setup step
let promise = new Promise(function(resolve) {
    const dryRunString = program.dryRun ? ' (dry run)' : '';

    logger.log(`Deploying ${chalk.underline(program.projectName)} ${chalk.underline(program.stage)} to ${chalk.underline(program.region)}${dryRunString}`);

    resolve(context);
}).then(setup);

// Process Lambdas
promise = promise.then(processing);

// Derive API configuration and then stack one
promise = promise.then(deriveAPI).then(deriveStack);

// Deploying the stack (OPTIONAL)
if (!program.dryRun) {
    promise = promise.then(fetchStack).then(deployStack);
}

promise.then(function() {
    logger.log(chalk.bold('Deployment complete'));
    logger.log(hype);
}).catch(function(error) {
    logger.error(chalk.bold.red('Deployment failed'), error.message, error.stack);
    process.exit(1);
});
<commit_msg>Fix linter<commit_after>"use strict";

const parseEnvironment = require('../lib/helpers/environment-parser');

const AWS = require('aws-sdk');
const chalk = require('chalk');
const path = require('path');
const program = require('commander');
const Promise = require('bluebird');
const prompt = require('readline-sync');
const _ = require('lodash');

const config = require('../lib/helpers/config');
const hype = require('../lib/helpers/lambdahype');
const logger = require('../lib/helpers/logger').shared;

const setup = require('../lib/deploy/setup-step');
const processing = require('../lib/deploy/bundle-lambdas-step');
const deriveAPI = require('../lib/deploy/derive-api-step');
const deriveStack = require('../lib/deploy/derive-stack-step');
const fetchStack = require('../lib/deploy/fetch-stack-step');
const deployStack = require('../lib/deploy/update-stack-step');

//
//  Program specification
//

program
    .option('-n, --project-name <name>', 'Project name')
    .option('-s, --stage <stage>', 'Stage name')
    .option('-r, --region <region>', 'Region')
    .option('-e, --environment <env>', 'Environment Variables to embed as key-value pairs', parseEnvironment, {})
    .option('--dry-run', 'Simply generate files that would be used to update the stack and API')
    .option('--exclude <list>', 'Packages to exclude from bundling', function(val) { return val.split(','); })
    .option('-o, --optimization <level>', 'Optimization level to use, valid values are 0-1', parseInt, 1)
    .option('--clean', 'Force a clean build where cached bundles are not used')
    .option('--no-color', 'Turn off ANSI coloring in output');

program.on('--help', function() {
    console.log('  Examples:');
    console.log();
    console.log('    Generate deployment files (in staging directory), but don\'t actually deploy');
    console.log('    $ lambda deploy --dry-run');
    console.log();
    console.log('    Deploy to \'prod\' stage with NODE_ENV set to \'production\' and FOO set to \'bar\'');
    console.log('    $ lambda deploy -s prod -e NODE_ENV=production,FOO=bar');
    console.log();
    console.log('    Deploy to default (dev) stage excluding \'example\' package from the bundle (included in the ZIP separately)');
    console.log('    $ lambda deploy --exclude example');
    console.log();
    console.log('    Deploy to default stage, ignoring cached bundles and disabling minification');
    console.log('    $ lambda deploy --clean --optimization 0');
    console.log();
});

program.parse(process.argv);

//
// Configure program
//

// Enable/Disable colors
chalk.enabled = program.color;

// Finalise configuration
program.region = program.region || config.aws.region;
program.stage = program.stage || config.aws.stage;
program.projectName = program.projectName || config.project.name || prompt.question('Please enter project name: ');

// Make region global for AWS
AWS.config.region = program.region;

// Carry over some stuff to environment
program.environment["AWS_REGION"] = program.region;
program.environment["AWS_STAGE"] = program.stage;
program.environment["AWS_PROJECT_NAME"] = program.projectName;

//
// Main logic
//
const context = {
    directories: {
        cwd: process.cwd(),
        root: path.join(path.resolve(__dirname), '../lib/deploy')
    },

    project: {
        name: program.projectName,
        stage: program.stage,
        region: program.region,
        timestamp: Math.floor(Date.now() / 1000)
    },

    program: _.pick(program, ['environment', 'stage', 'region', 'lambda', 'optimization', 'exclude', 'clean']),
    logger: logger
};

// Setup step
let promise = new Promise(function(resolve) {
    const dryRunString = program.dryRun ? ' (dry run)' : '';

    logger.log(`Deploying ${chalk.underline(program.projectName)} ${chalk.underline(program.stage)} to ${chalk.underline(program.region)}${dryRunString}`);

    resolve(context);
}).then(setup);

// Process Lambdas
promise = promise.then(processing);

// Derive API configuration and then stack one
promise = promise.then(deriveAPI).then(deriveStack);

// Deploying the stack (OPTIONAL)
if (!program.dryRun) {
    promise = promise.then(fetchStack).then(deployStack);
}

promise.then(function() {
    logger.log(chalk.bold('Deployment complete'));
    logger.log(hype);
}).catch(function(error) {
    logger.error(chalk.bold.red('Deployment failed'), error.message, error.stack);
    process.exit(1);
});
<|endoftext|>