<commit_before>mini.Module(
		"engine/game"
)
.requires(
		"engine/loop",
		"engine/config",
		"basic/canvas",
		"basic/audio/audio",
		"scenes/titlescene"
)
.defines(function(Loop, Config, Canvas, Audio, TitleScene) {
	var Game = function(configuration) {
		this.config = configuration || Config.getDefault();
		this.id = Game.nextGameID++;
		console.log("NEW GAME: " + this.id);
		this.init();
	};

	Game.nextGameID = 0;

	Game.prototype.init = function() {
		// prepare datGui
		this.datGui = new dat.GUI();

		// prepare stats
		this.stats = new Stats();
		$("body").prepend(this.stats.domElement);

		// Configure and start the game loop.
		this.loop = new Loop()
			.clear()
			.add(this.stats, this.stats.update)
			.start();

		this.canvas = new Canvas({canvasId: "bloobCanvas" + this.id});
		this.audio = new Audio(this);

		var titleScene = new TitleScene(this, this.loop).run();
	};

	return Game;
});
<commit_msg>game as subclass of mini.Class<commit_after>mini.Module(
		"engine/game"
)
.requires(
		"engine/loop",
		"engine/config",
		"basic/canvas",
		"basic/audio/audio",
		"scenes/titlescene"
)
.defines(function(Loop, Config, Canvas, Audio, TitleScene) {
	var Game = mini.Class.subclass({
		initialize: function(configuration) {
			this.config = configuration || Config.getDefault();
			this.id = Game.nextGameID++;
			console.log("NEW GAME: " + this.id);
			this.init();
		},
		init: function() {
			// prepare datGui
			this.datGui = new dat.GUI();

			// prepare stats
			this.stats = new Stats();
			$("body").prepend(this.stats.domElement);

			// Configure and start the game loop.
			this.loop = new Loop()
				.clear()
				.add(this.stats, this.stats.update)
				.start();

			this.canvas = new Canvas({canvasId: "bloobCanvas" + this.id});
			this.audio = new Audio(this);

			var titleScene = new TitleScene(this, this.loop).run();
		}
	});

	Game.nextGameID = 0;

	return Game;
});
<|endoftext|>


<commit_before>'use strict';

const async = require('async');
const _ = require('lodash');
const request = require('request');
const jsonpath = require('JSONPath');
const debug = require('debug')('http');
const VERSION = require('../package.json').version;
const USER_AGENT = 'artillery ' + VERSION + ' (https://artillery.io)';
const engineUtil = require('./engine_util');
const template = engineUtil.template;
const http = require('http');
const https = require('https');

module.exports = HttpEngine;

function HttpEngine(config) {
  this.config = config;
}

HttpEngine.prototype.step = function step(requestSpec, ee) {
  let config = this.config;

  var f = function(context, callback) {
    let method = _.keys(requestSpec)[0].toUpperCase();
    let params = requestSpec[method.toLowerCase()];
    let uri = maybePrependBase(template(params.url, context), config);
    let tls = config.tls || {};
    let timeout = config.timeout || 10;

    var requestParams = _.extend(tls, {
      uri: uri,
      method: method,
      headers: {
      },
      timeout: timeout * 1000,
      jar: context._jar
    });

    if (params.json) {
      requestParams.json = template(params.json, context);
    } else if (params.body) {
      requestParams.body = template(params.body, context);
    }

    // Assign default headers then overwrite as needed
    let defaultHeaders = lowcaseKeys(
      (config.defaults && config.defaults.headers) ?
        config.defaults.headers : {'user-agent': USER_AGENT});
    requestParams.headers = _.extend(defaultHeaders,
                                     lowcaseKeys(params.headers));
    let headers = _.foldl(requestParams.headers,
                          function(acc, v, k) {
                            acc[k] = template(v, context);
                            return acc;
                          }, {});

    requestParams.headers = headers;
    if (params.cookie) {
      _.each(params.cookie, function(v, k) {
        context._jar.setCookie(k + '=' + template(v, context), uri);
      });
    }

    if (config.http2) {
      requestParams.http2 = true;
    } else {
      requestParams.agent = context._agent;
    }

    debug('request: %j', requestParams);

    request(requestParams, function requestCallback(err, res, body) {
      if (err) {
        let errCode = err.code || err.message;
        ee.emit('error', errCode);
        debug(err);
        // this aborts the scenario
        return callback(err, context);
      }

      if (res.headers['content-type'] &&
          res.headers['content-type'].match(/^application\/json/)) {
        try {
          let r;
          if (typeof res.body === 'string') {
            r = JSON.parse(res.body);
          } else {
            r = res.body;
          }

          if (params.match) {
            let result = jsonpath.eval(r, params.match.json)[0];
            let value = template(params.match.value, context);

            if (result !== value) {
              // abort the scenario
              // return callback(err, context);
            } else {
              debug('match: %s matched %s in response', params.match.json, value);
              ee.emit('match');
            }
          }

          if (params.capture) {
            let capturedVal = jsonpath.eval(r, params.capture.json)[0];
            context.vars[params.capture.as] = capturedVal;

            debug('capture: %s = %s', params.capture.as, capturedVal);

            if (params.capture.transform) {
              let transformedVal = engineUtil.evil(
                context.vars,
                params.capture.transform);
              context.vars[params.capture.as] = transformedVal;
              debug('transform: %s = %s', params.capture.as, context.vars[params.capture.as]);
            }
          }
          context.vars.$ = r;
        } catch (e) {
        }
      }

      context._successCount++;
      context._pendingRequests--;
      return callback(null, context);
    })
    .on('request', function(req) {
      ee.emit('request');

      const startedAt = process.hrtime();

      req.on('response', function updateLatency(res) {
        let code = res.statusCode;
        const endedAt = process.hrtime(startedAt);
        let delta = (endedAt[0] * 1e9) + endedAt[1];
        ee.emit('response', delta, code);
      });
    }).on('end', function() {
    });
  };

  return f;
};

HttpEngine.prototype.compile = function compile(tasks, scenarioSpec, ee) {
  let config = this.config;

  return function scenario(initialContext, callback) {
    //
    // Calculate the number of steps we expect to take.
    //
    initialContext._successCount = 0;
    initialContext._pendingRequests = _.size(
      _.reject(scenarioSpec, function(rs) {
        return (typeof rs.think === 'number');
      }));

    initialContext._jar = request.jar();

    if (!config.http2) {
      let agentOpts = {
        keepAlive: true,
        keepAliveMsecs: 1000,
        maxSockets: 1,
        maxFreeSockets: 1
      };

      if ((/^https/i).test(config.target)) {
        initialContext._agent = new https.Agent(agentOpts);
      } else {
        initialContext._agent = new http.Agent(agentOpts);
      }
    }

    let steps = _.flatten([
      function zero(cb) {
        ee.emit('started');
        return cb(null, initialContext);
      },
      tasks
    ]);

    async.waterfall(
      steps,
      function scenarioWaterfallCb(err, context) {
        return callback(err, context);
      });
  };
};

function maybePrependBase(uri, config) {

  if (_.startsWith(uri, '/')) {
    return config.target + uri;
  } else {
    return uri;
  }
}

/*
 * Given a dictionary, return a dictionary with all keys lowercased.
 */
function lowcaseKeys(h) {
  return _.transform(h, function(result, v, k) {
    result[k.toLowerCase()] = v;
  });
}
<commit_msg>Allow passing in of https pfx certificate<commit_after>'use strict';

const async = require('async');
const _ = require('lodash');
const request = require('request');
const jsonpath = require('JSONPath');
const debug = require('debug')('http');
const VERSION = require('../package.json').version;
const USER_AGENT = 'artillery ' + VERSION + ' (https://artillery.io)';
const engineUtil = require('./engine_util');
const template = engineUtil.template;
const http = require('http');
const https = require('https');
const fs = require('fs');

module.exports = HttpEngine;

function HttpEngine(config) {
  this.config = config;
}

HttpEngine.prototype.step = function step(requestSpec, ee) {
  let config = this.config;

  var f = function(context, callback) {
    let method = _.keys(requestSpec)[0].toUpperCase();
    let params = requestSpec[method.toLowerCase()];
    let uri = maybePrependBase(template(params.url, context), config);
    let tls = config.tls || {};
    let timeout = config.timeout || 10;

    var requestParams = _.extend(tls, {
      uri: uri,
      method: method,
      headers: {
      },
      timeout: timeout * 1000,
      jar: context._jar
    });

    if (params.json) {
      requestParams.json = template(params.json, context);
    } else if (params.body) {
      requestParams.body = template(params.body, context);
    }

    // Assign default headers then overwrite as needed
    let defaultHeaders = lowcaseKeys(
      (config.defaults && config.defaults.headers) ?
        config.defaults.headers : {'user-agent': USER_AGENT});
    requestParams.headers = _.extend(defaultHeaders,
                                     lowcaseKeys(params.headers));
    let headers = _.foldl(requestParams.headers,
                          function(acc, v, k) {
                            acc[k] = template(v, context);
                            return acc;
                          }, {});

    requestParams.headers = headers;
    if (params.cookie) {
      _.each(params.cookie, function(v, k) {
        context._jar.setCookie(k + '=' + template(v, context), uri);
      });
    }

    if (config.http2) {
      requestParams.http2 = true;
    } else {
      requestParams.agent = context._agent;
    }

    debug('request: %j', requestParams);

    request(requestParams, function requestCallback(err, res, body) {
      if (err) {
        let errCode = err.code || err.message;
        ee.emit('error', errCode);
        debug(err);
        // this aborts the scenario
        return callback(err, context);
      }

      if (res.headers['content-type'] &&
          res.headers['content-type'].match(/^application\/json/)) {
        try {
          let r;
          if (typeof res.body === 'string') {
            r = JSON.parse(res.body);
          } else {
            r = res.body;
          }

          if (params.match) {
            let result = jsonpath.eval(r, params.match.json)[0];
            let value = template(params.match.value, context);

            if (result !== value) {
              // abort the scenario
              // return callback(err, context);
            } else {
              debug('match: %s matched %s in response', params.match.json, value);
              ee.emit('match');
            }
          }

          if (params.capture) {
            let capturedVal = jsonpath.eval(r, params.capture.json)[0];
            context.vars[params.capture.as] = capturedVal;

            debug('capture: %s = %s', params.capture.as, capturedVal);

            if (params.capture.transform) {
              let transformedVal = engineUtil.evil(
                context.vars,
                params.capture.transform);
              context.vars[params.capture.as] = transformedVal;
              debug('transform: %s = %s', params.capture.as, context.vars[params.capture.as]);
            }
          }
          context.vars.$ = r;
        } catch (e) {
        }
      }

      context._successCount++;
      context._pendingRequests--;
      return callback(null, context);
    })
    .on('request', function(req) {
      ee.emit('request');

      const startedAt = process.hrtime();

      req.on('response', function updateLatency(res) {
        let code = res.statusCode;
        const endedAt = process.hrtime(startedAt);
        let delta = (endedAt[0] * 1e9) + endedAt[1];
        ee.emit('response', delta, code);
      });
    }).on('end', function() {
    });
  };

  return f;
};

HttpEngine.prototype.compile = function compile(tasks, scenarioSpec, ee) {
  let config = this.config;
  let tls = config.tls || {};

  return function scenario(initialContext, callback) {
    //
    // Calculate the number of steps we expect to take.
    //
    initialContext._successCount = 0;
    initialContext._pendingRequests = _.size(
      _.reject(scenarioSpec, function(rs) {
        return (typeof rs.think === 'number');
      }));

    initialContext._jar = request.jar();

    if (!config.http2) {
      let agentOpts = {
        keepAlive: true,
        keepAliveMsecs: 1000,
        maxSockets: 1,
        maxFreeSockets: 1
      };

      if ((/^https/i).test(config.target)) {
        if (tls.pfx) {
          agentOpts.pfx = fs.readFileSync(tls.pfx);
        }
        initialContext._agent = new https.Agent(agentOpts);
      } else {
        initialContext._agent = new http.Agent(agentOpts);
      }
    }

    let steps = _.flatten([
      function zero(cb) {
        ee.emit('started');
        return cb(null, initialContext);
      },
      tasks
    ]);

    async.waterfall(
      steps,
      function scenarioWaterfallCb(err, context) {
        return callback(err, context);
      });
  };
};

function maybePrependBase(uri, config) {

  if (_.startsWith(uri, '/')) {
    return config.target + uri;
  } else {
    return uri;
  }
}

/*
 * Given a dictionary, return a dictionary with all keys lowercased.
 */
function lowcaseKeys(h) {
  return _.transform(h, function(result, v, k) {
    result[k.toLowerCase()] = v;
  });
}
<|endoftext|>


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

'use strict';

const async = require('async');
const _ = require('lodash');
const request = require('request');

const debug = require('debug')('http');
const debugResponse = require('debug')('http:response');
const debugFullBody = require('debug')('http:full_body');
const VERSION = require('../package.json').version;
const USER_AGENT = 'artillery ' + VERSION + ' (https://artillery.io)';
const engineUtil = require('./engine_util');
const template = engineUtil.template;
const http = require('http');
const https = require('https');
const fs = require('fs');
const filtrex = require('filtrex');

module.exports = HttpEngine;

function HttpEngine(script) {
  this.config = script.config;

  if (script.config.http && script.config.http.pool) {
    this.pool = {
      maxSockets: Number(script.config.http.pool)
    };
  }
}

HttpEngine.prototype.createScenario = function(scenarioSpec, ee) {
  var self = this;

  // Helper function to wrap an object's property in a list if it's
  // defined, or set it to an empty list if not.
  function ensurePropertyIsAList(obj, prop) {
    obj[prop] = [].concat(
      typeof obj[prop] === 'undefined' ?
        [] : obj[prop]);
    return obj;
  }

  ensurePropertyIsAList(scenarioSpec, 'beforeRequest');
  ensurePropertyIsAList(scenarioSpec, 'afterResponse');
  ensurePropertyIsAList(scenarioSpec, 'beforeScenario');
  ensurePropertyIsAList(scenarioSpec, 'afterScenario');

  // Add scenario-level hooks if needed:
  // For now, just turn them into function steps and insert them
  // directly into the flow array.
  // TODO: Scenario-level hooks will probably want access to the
  // entire scenario spec rather than just the userContext.
  const beforeScenarioFns = _.map(
    scenarioSpec.beforeScenario,
    function(hookFunctionName) {
      return {'function': hookFunctionName};
    });
  const afterScenarioFns = _.map(
    scenarioSpec.afterScenario,
    function(hookFunctionName) {
      return {'function': hookFunctionName};
    });

  const newFlow = beforeScenarioFns.concat(
    scenarioSpec.flow.concat(afterScenarioFns));

  scenarioSpec.flow = newFlow;

  let tasks = _.map(scenarioSpec.flow, function(rs) {
    return self.step(rs, ee, {
      beforeRequest: scenarioSpec.beforeRequest,
      afterResponse: scenarioSpec.afterResponse
    });
  });

  return self.compile(tasks, scenarioSpec.flow, ee);
};

HttpEngine.prototype.step = function step(requestSpec, ee, opts) {

  opts = opts || {};
  let self = this;
  let config = this.config;

  if (requestSpec.loop) {
    let steps = _.map(requestSpec.loop, function(rs) {
      return self.step(rs, ee, opts);
    });

    return engineUtil.createLoopWithCount(
      requestSpec.count || -1,
      steps,
      { loopValue: requestSpec.loopValue || '$loopCount' });
  }

  if (requestSpec.think) {
    return engineUtil.createThink(requestSpec, _.get(self.config, 'defaults.think', {}));
  }

  if (requestSpec.log) {
    return function(context, callback) {
      console.log(template(requestSpec.log, context));
      return process.nextTick(function() { callback(null, context); });
    };
  }

  if (requestSpec.function) {
    return function(context, callback) {
      let processFunc = self.config.processor[requestSpec.function];
      if (processFunc) {
        return processFunc(context, ee, function() {
          return callback(null, context);
        });
      } else {
        return process.nextTick(function () { callback(null, context); });
      }
    };
  }

  let f = function(context, callback) {
    let method = _.keys(requestSpec)[0].toUpperCase();
    let params = requestSpec[method.toLowerCase()];

    // A special case for when "url" attribute is missing. We need to check for
    // it manually as request.js won't emit an 'error' event when the argument
    // is missing.
    // This will be obsoleted by better script validation.
    if (!params.url && !params.uri) {
      let err = new Error('an URL must be specified');
      ee.emit('error', err.message);
      return callback(err, context);
    }

    let tls = config.tls || {};
    let timeout = config.timeout || _.get(config, 'http.timeout') || 120;

    if (!engineUtil.isProbableEnough(params)) {
      return process.nextTick(function() {
        callback(null, context);
      });
    }

    if (!_.isUndefined(params.ifTrue)) {
      let cond;
      let result;
      try {
        cond = filtrex(params.ifTrue);
        result = cond(context.vars);
      } catch (e) {
        result = 1; // if the expression is incorrect, just proceed // TODO: debug message
      }
      if (typeof result === 'undefined' || result === 0) {
        return process.nextTick(function () {
          callback(null, context);
        });
      }
    }

    // Run beforeRequest processors (scenario-level ones too)
    let requestParams = _.cloneDeep(params);
    requestParams = _.extend(requestParams, {
      url: maybePrependBase(params.url || params.uri, config), // *NOT* templating here
      method: method,
      headers: {
      },
      timeout: timeout * 1000,
      jar: context._jar
    });
    requestParams = _.extend(requestParams, tls);

    let functionNames = _.concat(opts.beforeRequest || [], params.beforeRequest || []);

    async.eachSeries(
      functionNames,
      function iteratee(functionName, next) {

        let processFunc = config.processor[functionName];
        processFunc(requestParams, context, ee, function(err) {
          if (err) {
            return next(err);
          }
          return next(null);
        });
      },
      function done(err) {
        if (err) {
          debug(err);
          let errCode = err.code || err.message;
          // FIXME: Should not need to have to emit manually here
          ee.emit('error', errCode);
          return callback(err, context);
        }

        // Assemble the new request params
        if (params.json) {
          requestParams.json = template(params.json, context);
        }

        if (params.body) {
          requestParams.body = template(params.body, context);
        }

        if (params.form) {
          requestParams.form = _.reduce(
            requestParams.form,
            function (acc, v, k) {
              acc[k] = template(v, context);
              return acc;
            },
            {});
        }

        // Assign default headers then overwrite as needed
        let defaultHeaders = lowcaseKeys(
          (config.defaults && config.defaults.headers) ?
            config.defaults.headers : {'user-agent': USER_AGENT});
        requestParams.headers = _.extend(defaultHeaders,
                                         lowcaseKeys(params.headers));
        let headers = _.reduce(requestParams.headers,
                               function(acc, v, k) {
                                 acc[k] = template(v, context);
                                 return acc;
                               }, {});
        requestParams.headers = headers;

        let defaultCookie = config.defaults ? config.defaults.cookie || {} : {};

        let cookie = _.reduce(
          params.cookie,
          function(acc, v, k) {
            acc[k] = v;
            return acc;
          },
          defaultCookie);

        if (cookie) {
          _.each(cookie, function(v, k) {
            context._jar.setCookie(k + '=' + template(v, context), requestParams.url);
          });
        }

        if (typeof requestParams.auth === 'object') {
          requestParams.auth.user = template(requestParams.auth.user, context);
          requestParams.auth.pass = template(requestParams.auth.pass, context);
        }

        if (config.http2) {
          requestParams.http2 = true;
        } else {
          if (!self.pool) {
            requestParams.agent = context._agent;
          } else {

            requestParams.pool = self.pool;
          }
        }

        let url = maybePrependBase(template(requestParams.uri || requestParams.url, context), config);

        if (requestParams.uri) {
          // If a hook function sets requestParams.uri to something, request.js
          // will pick that over .url, so we need to delete it.
          delete requestParams.uri;
        }

        requestParams.url = url;

        function requestCallback(err, res, body) {
          if (err) {
            return;
          }

          if (process.env.DEBUG) {
            let requestInfo = {
              url: requestParams.url,
              method: requestParams.method,
              headers: requestParams.headers
            };
            if (requestParams.json && typeof requestParams.json !== 'boolean') {
              requestInfo.json = requestParams.json;
            }

            // If "json" is set to an object, it will be serialised and sent as body and the value of the "body" attribute will be ignored.
            if (requestParams.body && typeof requestParams.json !== 'object') {
              if (process.env.DEBUG.indexOf('http:full_body') > -1) {
                // Show the entire body
                requestInfo.body = requestParams.body;
              } else {
                // Only show the beginning of long bodies
                requestInfo.body = requestParams.body.substring(0, 512);
                if (requestParams.body.length > 512) {
                  requestInfo.body += ' ...';
                }
              }
            }
            debug('request: %s', JSON.stringify(requestInfo, null, 2));
          }

          debugResponse(JSON.stringify(res.headers, null, 2));
          debugResponse(JSON.stringify(body, null, 2));

          engineUtil.captureOrMatch(
            params,
            res,
            context,
            function captured(err, result) {
              if (err) {
                // end the scenario
                ee.emit('error', err.message);
                return callback(err, context);
              }

              debug('captures and matches:');
              debug(result.matches);
              debug(result.captures);

              // match and capture are strict by default:
              let haveFailedMatches = _.some(result.matches, function(v, k) {
                return !v.success && v.strict !== false;
              });

              let haveFailedCaptures = _.some(result.captures, function(v, k) {
                return v === '';
              });

              if (haveFailedMatches || haveFailedCaptures) {
                // TODO: Emit the details of each failed capture/match
              } else {
                _.each(result.matches, function(v, k) {
                  ee.emit('match', v.success, {
                    expected: v.expected,
                    got: v.got,
                    expression: v.expression,
                    strict: v.strict
                  });
                });

                _.each(result.captures, function(v, k) {
                  context.vars[k] = v;
                });
              }

              // Now run afterResponse processors
              let functionNames = _.concat(opts.afterResponse || [], params.afterResponse || []);
              async.eachSeries(
                functionNames,
                function iteratee(functionName, next) {
                  let processFunc = config.processor[functionName];
                  processFunc(requestParams, res, context, ee, function(err) {
                    if (err) {
                      return next(err);
                    }
                    return next(null);
                  });
                }, function(err) {
                  if (err) {
                    debug(err);
                    ee.emit('error', err.code || err.message);
                    return callback(err, context);
                  }

                  if (haveFailedMatches || haveFailedCaptures) {
                    // FIXME: This means only one error in the report even if multiple captures failed for the same request.
                    return callback(new Error('Failed capture or match'), context);
                  }

                  return callback(null, context);
                });
            });
        }

        // If we aren't processing the full response, we don't need the
        // callback:
        let maybeCallback;
        if (typeof requestParams.capture === 'object' ||
            typeof requestParams.match === 'object' ||
            requestParams.afterResponse ||
            opts.afterResponse ||
            process.env.DEBUG) {
          maybeCallback = requestCallback;
        }

        if(!requestParams.url) {
          let err = new Error('an URL must be specified');
          ee.emit('error', err.message);
          return callback(err, context);
        }

        request(requestParams, maybeCallback)
          .on('request', function(req) {
            ee.emit('request');

            const startedAt = process.hrtime();

            req.on('response', function updateLatency(res) {
              let code = res.statusCode;
              const endedAt = process.hrtime(startedAt);
              let delta = (endedAt[0] * 1e9) + endedAt[1];
              ee.emit('response', delta, code, context._uid);
            });
          }).on('end', function() {
            context._successCount++;

            if (!maybeCallback) {
              callback(null, context);
            } // otherwise called from requestCallback
          }).on('error', function(err) {
            let errCode = err.code || err.message;
            ee.emit('error', errCode);
            debug(err);
            // this aborts the scenario
            return callback(err, context);
          });
      }); // eachSeries
  };

  return f;
};

HttpEngine.prototype.compile = function compile(tasks, scenarioSpec, ee) {
  let self = this;
  let config = this.config;
  let tls = config.tls || {};

  return function scenario(initialContext, callback) {
    initialContext._successCount = 0;

    initialContext._jar = request.jar();

    if (!config.http2) {
      if (!self.pool) {
        let agentOpts = {
          keepAlive: true,
          keepAliveMsecs: 1000,
          maxSockets: 1,
          maxFreeSockets: 1
        };

        // FIXME: This won't work if we have a pool - needs to be set in agentOptions
        // in request params
        if ((/^https/i).test(config.target)) {
          if (tls.pfx) {
            // FIXME: Path needs to be resolved relative to Artillery's cwd
            agentOpts.pfx = fs.readFileSync(tls.pfx);
          }
          initialContext._agent = new https.Agent(agentOpts);
        } else {
          initialContext._agent = new http.Agent(agentOpts);
        }
      }
    }

    let steps = _.flatten([
      function zero(cb) {
        ee.emit('started');
        return cb(null, initialContext);
      },
      tasks
    ]);

    async.waterfall(
      steps,
      function scenarioWaterfallCb(err, context) {
        // If the connection was refused we might not have a context
        if (context && context._agent) {
          context._agent.destroy();
        }

        if (err) {
          ee.emit('error', err.message);
          return callback(err, context);
        } else {
          return callback(null, context);
        }
      });
  };
};

function maybePrependBase(uri, config) {
  if (_.startsWith(uri, '/')) {
    return config.target + uri;
  } else {
    return uri;
  }
}

/*
 * Given a dictionary, return a dictionary with all keys lowercased.
 */
function lowcaseKeys(h) {
  return _.transform(h, function(result, v, k) {
    result[k.toLowerCase()] = v;
  });
}
<commit_msg>fix(http): Include headers set in beforeRequest hooks<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

const async = require('async');
const _ = require('lodash');
const request = require('request');

const debug = require('debug')('http');
const debugResponse = require('debug')('http:response');
const debugFullBody = require('debug')('http:full_body');
const VERSION = require('../package.json').version;
const USER_AGENT = 'artillery ' + VERSION + ' (https://artillery.io)';
const engineUtil = require('./engine_util');
const template = engineUtil.template;
const http = require('http');
const https = require('https');
const fs = require('fs');
const filtrex = require('filtrex');

module.exports = HttpEngine;

function HttpEngine(script) {
  this.config = script.config;

  if (script.config.http && script.config.http.pool) {
    this.pool = {
      maxSockets: Number(script.config.http.pool)
    };
  }
}

HttpEngine.prototype.createScenario = function(scenarioSpec, ee) {
  var self = this;

  // Helper function to wrap an object's property in a list if it's
  // defined, or set it to an empty list if not.
  function ensurePropertyIsAList(obj, prop) {
    obj[prop] = [].concat(
      typeof obj[prop] === 'undefined' ?
        [] : obj[prop]);
    return obj;
  }

  ensurePropertyIsAList(scenarioSpec, 'beforeRequest');
  ensurePropertyIsAList(scenarioSpec, 'afterResponse');
  ensurePropertyIsAList(scenarioSpec, 'beforeScenario');
  ensurePropertyIsAList(scenarioSpec, 'afterScenario');

  // Add scenario-level hooks if needed:
  // For now, just turn them into function steps and insert them
  // directly into the flow array.
  // TODO: Scenario-level hooks will probably want access to the
  // entire scenario spec rather than just the userContext.
  const beforeScenarioFns = _.map(
    scenarioSpec.beforeScenario,
    function(hookFunctionName) {
      return {'function': hookFunctionName};
    });
  const afterScenarioFns = _.map(
    scenarioSpec.afterScenario,
    function(hookFunctionName) {
      return {'function': hookFunctionName};
    });

  const newFlow = beforeScenarioFns.concat(
    scenarioSpec.flow.concat(afterScenarioFns));

  scenarioSpec.flow = newFlow;

  let tasks = _.map(scenarioSpec.flow, function(rs) {
    return self.step(rs, ee, {
      beforeRequest: scenarioSpec.beforeRequest,
      afterResponse: scenarioSpec.afterResponse
    });
  });

  return self.compile(tasks, scenarioSpec.flow, ee);
};

HttpEngine.prototype.step = function step(requestSpec, ee, opts) {

  opts = opts || {};
  let self = this;
  let config = this.config;

  if (requestSpec.loop) {
    let steps = _.map(requestSpec.loop, function(rs) {
      return self.step(rs, ee, opts);
    });

    return engineUtil.createLoopWithCount(
      requestSpec.count || -1,
      steps,
      { loopValue: requestSpec.loopValue || '$loopCount' });
  }

  if (requestSpec.think) {
    return engineUtil.createThink(requestSpec, _.get(self.config, 'defaults.think', {}));
  }

  if (requestSpec.log) {
    return function(context, callback) {
      console.log(template(requestSpec.log, context));
      return process.nextTick(function() { callback(null, context); });
    };
  }

  if (requestSpec.function) {
    return function(context, callback) {
      let processFunc = self.config.processor[requestSpec.function];
      if (processFunc) {
        return processFunc(context, ee, function() {
          return callback(null, context);
        });
      } else {
        return process.nextTick(function () { callback(null, context); });
      }
    };
  }

  let f = function(context, callback) {
    let method = _.keys(requestSpec)[0].toUpperCase();
    let params = requestSpec[method.toLowerCase()];

    // A special case for when "url" attribute is missing. We need to check for
    // it manually as request.js won't emit an 'error' event when the argument
    // is missing.
    // This will be obsoleted by better script validation.
    if (!params.url && !params.uri) {
      let err = new Error('an URL must be specified');
      ee.emit('error', err.message);
      return callback(err, context);
    }

    let tls = config.tls || {};
    let timeout = config.timeout || _.get(config, 'http.timeout') || 120;

    if (!engineUtil.isProbableEnough(params)) {
      return process.nextTick(function() {
        callback(null, context);
      });
    }

    if (!_.isUndefined(params.ifTrue)) {
      let cond;
      let result;
      try {
        cond = filtrex(params.ifTrue);
        result = cond(context.vars);
      } catch (e) {
        result = 1; // if the expression is incorrect, just proceed // TODO: debug message
      }
      if (typeof result === 'undefined' || result === 0) {
        return process.nextTick(function () {
          callback(null, context);
        });
      }
    }

    // Run beforeRequest processors (scenario-level ones too)
    let requestParams = _.cloneDeep(params);
    requestParams = _.extend(requestParams, {
      url: maybePrependBase(params.url || params.uri, config), // *NOT* templating here
      method: method,
      headers: {
      },
      timeout: timeout * 1000,
      jar: context._jar
    });
    requestParams = _.extend(requestParams, tls);

    let functionNames = _.concat(opts.beforeRequest || [], params.beforeRequest || []);

    async.eachSeries(
      functionNames,
      function iteratee(functionName, next) {

        let processFunc = config.processor[functionName];
        processFunc(requestParams, context, ee, function(err) {
          if (err) {
            return next(err);
          }
          return next(null);
        });
      },
      function done(err) {
        if (err) {
          debug(err);
          let errCode = err.code || err.message;
          // FIXME: Should not need to have to emit manually here
          ee.emit('error', errCode);
          return callback(err, context);
        }

        // Assemble the new request params
        if (params.json) {
          requestParams.json = template(params.json, context);
        }

        if (params.body) {
          requestParams.body = template(params.body, context);
        }

        if (params.form) {
          requestParams.form = _.reduce(
            requestParams.form,
            function (acc, v, k) {
              acc[k] = template(v, context);
              return acc;
            },
            {});
        }

        // Assign default headers then overwrite as needed
        let defaultHeaders = lowcaseKeys(
          (config.defaults && config.defaults.headers) ?
            config.defaults.headers : {'user-agent': USER_AGENT});
        requestParams.headers = _.extend(defaultHeaders,
                                         lowcaseKeys(params.headers), lowcaseKeys(requestParams.headers));
        let headers = _.reduce(requestParams.headers,
                               function(acc, v, k) {
                                 acc[k] = template(v, context);
                                 return acc;
                               }, {});
        requestParams.headers = headers;

        let defaultCookie = config.defaults ? config.defaults.cookie || {} : {};

        let cookie = _.reduce(
          params.cookie,
          function(acc, v, k) {
            acc[k] = v;
            return acc;
          },
          defaultCookie);

        if (cookie) {
          _.each(cookie, function(v, k) {
            context._jar.setCookie(k + '=' + template(v, context), requestParams.url);
          });
        }

        if (typeof requestParams.auth === 'object') {
          requestParams.auth.user = template(requestParams.auth.user, context);
          requestParams.auth.pass = template(requestParams.auth.pass, context);
        }

        if (config.http2) {
          requestParams.http2 = true;
        } else {
          if (!self.pool) {
            requestParams.agent = context._agent;
          } else {

            requestParams.pool = self.pool;
          }
        }

        let url = maybePrependBase(template(requestParams.uri || requestParams.url, context), config);

        if (requestParams.uri) {
          // If a hook function sets requestParams.uri to something, request.js
          // will pick that over .url, so we need to delete it.
          delete requestParams.uri;
        }

        requestParams.url = url;

        function requestCallback(err, res, body) {
          if (err) {
            return;
          }

          if (process.env.DEBUG) {
            let requestInfo = {
              url: requestParams.url,
              method: requestParams.method,
              headers: requestParams.headers
            };
            if (requestParams.json && typeof requestParams.json !== 'boolean') {
              requestInfo.json = requestParams.json;
            }

            // If "json" is set to an object, it will be serialised and sent as body and the value of the "body" attribute will be ignored.
            if (requestParams.body && typeof requestParams.json !== 'object') {
              if (process.env.DEBUG.indexOf('http:full_body') > -1) {
                // Show the entire body
                requestInfo.body = requestParams.body;
              } else {
                // Only show the beginning of long bodies
                requestInfo.body = requestParams.body.substring(0, 512);
                if (requestParams.body.length > 512) {
                  requestInfo.body += ' ...';
                }
              }
            }
            debug('request: %s', JSON.stringify(requestInfo, null, 2));
          }

          debugResponse(JSON.stringify(res.headers, null, 2));
          debugResponse(JSON.stringify(body, null, 2));

          engineUtil.captureOrMatch(
            params,
            res,
            context,
            function captured(err, result) {
              if (err) {
                // end the scenario
                ee.emit('error', err.message);
                return callback(err, context);
              }

              debug('captures and matches:');
              debug(result.matches);
              debug(result.captures);

              // match and capture are strict by default:
              let haveFailedMatches = _.some(result.matches, function(v, k) {
                return !v.success && v.strict !== false;
              });

              let haveFailedCaptures = _.some(result.captures, function(v, k) {
                return v === '';
              });

              if (haveFailedMatches || haveFailedCaptures) {
                // TODO: Emit the details of each failed capture/match
              } else {
                _.each(result.matches, function(v, k) {
                  ee.emit('match', v.success, {
                    expected: v.expected,
                    got: v.got,
                    expression: v.expression,
                    strict: v.strict
                  });
                });

                _.each(result.captures, function(v, k) {
                  context.vars[k] = v;
                });
              }

              // Now run afterResponse processors
              let functionNames = _.concat(opts.afterResponse || [], params.afterResponse || []);
              async.eachSeries(
                functionNames,
                function iteratee(functionName, next) {
                  let processFunc = config.processor[functionName];
                  processFunc(requestParams, res, context, ee, function(err) {
                    if (err) {
                      return next(err);
                    }
                    return next(null);
                  });
                }, function(err) {
                  if (err) {
                    debug(err);
                    ee.emit('error', err.code || err.message);
                    return callback(err, context);
                  }

                  if (haveFailedMatches || haveFailedCaptures) {
                    // FIXME: This means only one error in the report even if multiple captures failed for the same request.
                    return callback(new Error('Failed capture or match'), context);
                  }

                  return callback(null, context);
                });
            });
        }

        // If we aren't processing the full response, we don't need the
        // callback:
        let maybeCallback;
        if (typeof requestParams.capture === 'object' ||
            typeof requestParams.match === 'object' ||
            requestParams.afterResponse ||
            opts.afterResponse ||
            process.env.DEBUG) {
          maybeCallback = requestCallback;
        }

        if(!requestParams.url) {
          let err = new Error('an URL must be specified');
          ee.emit('error', err.message);
          return callback(err, context);
        }

        request(requestParams, maybeCallback)
          .on('request', function(req) {
            ee.emit('request');

            const startedAt = process.hrtime();

            req.on('response', function updateLatency(res) {
              let code = res.statusCode;
              const endedAt = process.hrtime(startedAt);
              let delta = (endedAt[0] * 1e9) + endedAt[1];
              ee.emit('response', delta, code, context._uid);
            });
          }).on('end', function() {
            context._successCount++;

            if (!maybeCallback) {
              callback(null, context);
            } // otherwise called from requestCallback
          }).on('error', function(err) {
            let errCode = err.code || err.message;
            ee.emit('error', errCode);
            debug(err);
            // this aborts the scenario
            return callback(err, context);
          });
      }); // eachSeries
  };

  return f;
};

HttpEngine.prototype.compile = function compile(tasks, scenarioSpec, ee) {
  let self = this;
  let config = this.config;
  let tls = config.tls || {};

  return function scenario(initialContext, callback) {
    initialContext._successCount = 0;

    initialContext._jar = request.jar();

    if (!config.http2) {
      if (!self.pool) {
        let agentOpts = {
          keepAlive: true,
          keepAliveMsecs: 1000,
          maxSockets: 1,
          maxFreeSockets: 1
        };

        // FIXME: This won't work if we have a pool - needs to be set in agentOptions
        // in request params
        if ((/^https/i).test(config.target)) {
          if (tls.pfx) {
            // FIXME: Path needs to be resolved relative to Artillery's cwd
            agentOpts.pfx = fs.readFileSync(tls.pfx);
          }
          initialContext._agent = new https.Agent(agentOpts);
        } else {
          initialContext._agent = new http.Agent(agentOpts);
        }
      }
    }

    let steps = _.flatten([
      function zero(cb) {
        ee.emit('started');
        return cb(null, initialContext);
      },
      tasks
    ]);

    async.waterfall(
      steps,
      function scenarioWaterfallCb(err, context) {
        // If the connection was refused we might not have a context
        if (context && context._agent) {
          context._agent.destroy();
        }

        if (err) {
          ee.emit('error', err.message);
          return callback(err, context);
        } else {
          return callback(null, context);
        }
      });
  };
};

function maybePrependBase(uri, config) {
  if (_.startsWith(uri, '/')) {
    return config.target + uri;
  } else {
    return uri;
  }
}

/*
 * Given a dictionary, return a dictionary with all keys lowercased.
 */
function lowcaseKeys(h) {
  return _.transform(h, function(result, v, k) {
    result[k.toLowerCase()] = v;
  });
}
<|endoftext|>


<commit_before>(function($){

  $(".layout__slot:nth-child(1)").attr("id","calendar-list");
  $(".layout__slot:nth-child(2)").attr("id","app-list");
  $(".layout__slot:nth-child(3)").attr("id","activity-list");
  $(".layout__slot:nth-child(4)").attr("id","tweets-list");
  $(".layout__slot:nth-child(4)").attr("id","tweets-list");

  var appList = $("#app-list")

  appList.prependTo(appList.parent())
  appList.removeClass();

  var activityList = $("#activity-list")
  activityList.find(".notification li").on("click",function(e){
    $(this).find("a").click();
  });

})(jQuery)
<commit_msg>Update page-manipulation.js<commit_after>(function($){

  $(".layout__slot:nth-child(1)").attr("id","calendar-list");
  $(".layout__slot:nth-child(2)").attr("id","app-list");
  $(".layout__slot:nth-child(3)").attr("id","activity-list");
  $(".layout__slot:nth-child(4)").attr("id","tweets-list");
  $(".layout__slot:nth-child(4)").attr("id","tweets-list");

  var appList = $("#app-list")

  appList.prependTo(appList.parent())
  appList.removeClass();

  var activityList = $("#activity-list")
  activityList.find(".notification li").on("click",function(e){
    $(this).find(".vignette_deco2 .resume strong a").click();
  });

})(jQuery)
<|endoftext|>


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

function createResult(status, formData) {
    var data = { status: status };
    if(status !== 'logged-in') {
        data.data = formData;
    }

    return data;
}

function isLoggedIn($) {
    return $('#uname').length != 0;
}

var parser = {};
parser.extractFormData = function(page) {
    return Promise.resolve(page)
        .then(function(page) {
            var form = page('#login-form');
            return _.reduce(form.serializeArray(), function(result, info) {
                result[info.name] = info.value;
                return result;
            }, {});
        });
};

parser.parseLogin = function(page) {
    var self = this;
    return Promise.resolve(page).then(cheerio.load)
        .then(function(page) {
            if(isLoggedIn(page)) {
                return createResult('logged-in');
            } else {
                return parser.extractFormData(page).then(function(data) {
                    if(data == null || Object.keys(data).length == 0) {
                        return createResult('error', 'No form found');
                    } else {
                        return createResult('logged-out', data);
                    }
                });
            }
        }).catch(function(e) {
            return createResult('error', e);
        });
};

module.exports = parser;
<commit_msg>Woops missed something in previous commits<commit_after>var cheerio = require('cheerio');
var Promise = require('bluebird');
var _ = require('lodash');

function createResult(status, formData) {
    var data = { status: status };
    if(status !== 'logged-in') {
        data.data = formData;
    }

    return data;
}

function isLoggedIn($) {
    return $('#uname').length != 0;
}

var parser = {};
parser.extractFormData = function(page) {
    return Promise.resolve(page)
        .then(function(page) {
            var form = page('#login-form');
            return _.reduce(form.serializeArray(), function(result, info) {
                result[info.name] = info.value;
                return result;
            }, {});
        });
};

parser.parseLogin = function(page) {
    var self = this;
    return Promise.resolve(page).then(cheerio.load)
        .then(function(page) {
            if(isLoggedIn(page)) {
                return createResult('logged-in');
            } else {
                return parser.extractFormData(page).then(function(data) {
                    if(data == null || Object.keys(data).length == 0) {
                        return createResult('error', 'No form found');
                    } else {
                        return createResult('logged-out', data);
                    }
                });
            }
        }).catch(function(e) {
            return createResult('error', e);
        });
};

parser.checkLogin = function(page) {
    var self = this;
    return Promise.resolve(page).then(cheerio.load)
        .then(function(page) {
            if(isLoggedIn(page)) {
                return true;
            } else {
                return false;
            }
        });
};

module.exports = parser;
<|endoftext|>


<commit_before>var express = require('express');
var cors = require('cors');
var bodyParser = require('body-parser');
var todo = require('./lib/todo');

var app = express();
app.use(cors());
app.use(bodyParser.json());

app.get('/', function(req, res, next) {
  console.log('GET / called');
  todo.getAll(function(err, result) {
    if (err) {
      res.status = 500;
      res.json({error: 'could not get todos from database'});
    } else {
      res.json(result);
    }
  });
});

app.post('/', function(req, res, next) {
  console.log('POST / called');
  todo.create(req.body, function(err, result) {
    if (err) {
      res.status = 500;
      res.json({error: 'Could not create the todo'});
    } else {
      res.json(result);
    }
  });
});

app.delete('/', function(req, res, next) {
  console.log('DELETE / called');
  todo.deleteAll(function(err, result) {
    if (err) {
      res.status = 500;
      res.json({error: 'Could not delete all todo\'s'});
    } else {
      res.json(result);
    }
  });
});

app.get('/:url', function(req, res, next) {
  console.log('GET /' + req.params.url);
  todo.get(req.params.url, function(err, result) {
    if (err) {
      res.status = 500;
      res.json({error: 'Cannot get todo'});
    } else {
      res.json(result);
    }
  });
});

app.patch('/:url', function(req, res, next) {
  console.log('PATCH /' + req.params.url);
  todo.update(req.params.url, req.body, function(err, result) {
    if (err) {
      res.status = 500;
      res.json({error: 'Cannot update the todo'});
    } else {
      res.json(result);
    }
  });
});

app.delete('/:url', function(req, res, next) {
  console.log('DELETE /' + req.params.url);
  todo.delete(req.params.url, function(err, result) {
    if (err) {
      res.status = 500;
      res.json({error: 'cannot delete task'});
    } else {
      res.json(result);
    }
  });
});

app.post('/setup/db', function(req, res, next) {
  console.log('POST /setup/db');
  todo.setupDB(function(err, result) {
    if (err) {
      res.status = 500;
      res.json({error: 'could not create db schema \n' + err});
    } else {
      res.json(result);
    }
  });
});

app.listen(3000, function() {
  console.log('app listening on port 3000');
});
<commit_msg>Create a abstracted genResponse function.<commit_after>var express = require('express');
var cors = require('cors');
var bodyParser = require('body-parser');
var todo = require('./lib/todo');

var app = express();
app.use(cors());
app.use(bodyParser.json());

var genResponse = function(errorMessage, res) {
  return function(err, result) {
    if (err) {
      res.status = 500;
      res.json({error: errorMessage});
    } else {
      res.json(result);
    }
  };
};

app.get('/', function(req, res, next) {
  console.log('GET / called');
  todo.getAll(function(err, result) {
    if (err) {
      res.status = 500;
      res.json({error: 'could not get todos from database'});
    } else {
      res.json(result);
    }
  });
});

app.post('/', function(req, res, next) {
  console.log('POST / called');
  todo.create(req.body, function(err, result) {
    if (err) {
      res.status = 500;
      res.json({error: 'Could not create the todo'});
    } else {
      res.json(result);
    }
  });
});

app.delete('/', function(req, res, next) {
  console.log('DELETE / called');
  todo.deleteAll(function(err, result) {
    if (err) {
      res.status = 500;
      res.json({error: 'Could not delete all todo\'s'});
    } else {
      res.json(result);
    }
  });
});

app.get('/:url', function(req, res, next) {
  console.log('GET /' + req.params.url);
  todo.get(req.params.url, function(err, result) {
    if (err) {
      res.status = 500;
      res.json({error: 'Cannot get todo'});
    } else {
      res.json(result);
    }
  });
});

app.patch('/:url', function(req, res, next) {
  console.log('PATCH /' + req.params.url);
  todo.update(req.params.url, req.body, function(err, result) {
    if (err) {
      res.status = 500;
      res.json({error: 'Cannot update the todo'});
    } else {
      res.json(result);
    }
  });
});

app.delete('/:url', function(req, res, next) {
  console.log('DELETE /' + req.params.url);
  todo.delete(req.params.url, function(err, result) {
    if (err) {
      res.status = 500;
      res.json({error: 'cannot delete task'});
    } else {
      res.json(result);
    }
  });
});

app.post('/setup/db', function(req, res, next) {
  console.log('POST /setup/db');
  todo.setupDB(function(err, result) {
    if (err) {
      res.status = 500;
      res.json({error: 'could not create db schema \n' + err});
    } else {
      res.json(result);
    }
  });
});

app.listen(3000, function() {
  console.log('app listening on port 3000');
});
<|endoftext|>


<commit_before>import React, { Component, Fragment } from 'react'
import Helmet from 'react-helmet'
import api from 'api'
import storage from 'storage'
import {
  ThemeProvider,
  Box,
  Button,
  Card,
  Container,
  Section,
  Flex,
  Heading,
  LargeButton,
  Link as DSLink,
  Text,
  cx
} from '@hackclub/design-system'
import { clubApplicationSchema } from 'components/apply/ClubApplicationForm'
import ApplyNav from 'components/apply/ApplyNav'
import LeaderInviteForm from 'components/apply/LeaderInviteForm'
import Login from 'components/apply/Login'
import SubmitButton from 'components/apply/SubmitButton'
import LoadingAnimation from 'components/LoadingAnimation'
import fetch from 'unfetch'
import Link from 'gatsby-link'

LargeButton.link = LargeButton.withComponent(Link)

const A = DSLink.extend`
  cursor: pointer;
  :hover {
    text-decoration: underline;
  }
`

const timeSince = time => {
  const seconds = Math.floor((new Date() - new Date(time)) / 1000)
  const intervals = [
    [Math.floor(seconds / (60 * 60 * 24 * 7)), 'weeks'],
    [Math.floor(seconds / (60 * 60 * 24)), 'days'],
    [Math.floor(seconds / (60 * 60)), 'hours'],
    [Math.floor(seconds / 60), 'minutes']
  ]
  for (var i = 0; i < intervals.length; i++) {
    let interval = intervals[i]
    if (interval[0] > 1) {
      return interval.join(' ')
    }
  }
  return 'less than a minute'
}

const Neg = () => <Text.span color="error" bold children="NOT" />

const CustomCard = Card.extend.attrs({
  p: [3, 4],
  color: 'black',
  bg: 'snow',
  w: 1,
  boxShadowSize: 'md'
})`
  ul {
    padding-left: 0;
  }
`

const ApplicationCard = props => {
  const {
    id,
    leader_profiles,
    updated_at,
    created_at,
    submitted_at
  } = props.app
  const { authToken, callback, app, resetCallback } = props

  const leaderProfile = leader_profiles.find(
    profile => profile.user.id == props.userId
  )
  const coLeaderProfiles = leader_profiles.filter(
    profile => profile.user.id != props.userId
  )

  const completeProfiles = leader_profiles.every(
    profile => profile.completed_at
  )
  const completeApplication = clubApplicationSchema.isValidSync(app)
  let submitButtonStatus
  if (app.submitted_at) {
    submitButtonStatus = 'submitted'
  } else if (completeApplication && completeProfiles) {
    submitButtonStatus = 'complete'
  } else {
    submitButtonStatus = 'incomplete'
  }

  return (
    <Container maxWidth={36} mt={3} p={3}>
      {app.rejected_at ? (
        <Flex mb={4}>
          <CustomCard>
            <Heading.h3>Unfortunatly, you’ve been rejected</Heading.h3>

            <Text>
              You can start a new application by clicking{' '}
              here.
            </Text>
          </CustomCard>
        </Flex>
      ) : null}
      <Flex
        align="center"
        justify="center"
        flexDirection={['column', 'row']}
        mx={[null, -2]}
      >
        <LargeButton.link
          w={1}
          m={2}
          inverted
          to={`/apply/club?id=${id}`}
          children="Edit Application"
        />
        <LargeButton.link
          w={1}
          m={2}
          inverted
          to={`/apply/leader?id=${leaderProfile.id}`}
          children="Edit Leader Profile"
        />
      </Flex>
      <Flex mt={2} mb={4}>
        <SubmitButton
          status={submitButtonStatus}
          applicationId={app.id}
          callback={callback}
        />
      </Flex>
      <CustomCard>
        <Text>You only need a team to apply. Invite them:</Text>
        <LeaderInviteForm id={id} authToken={authToken} callback={callback} />
        <Text>After you submit your application:</Text>

We’ll get back to you with our decision in 7 days

            If you’re accepted, we’ll schedule a call to train you to lead your
            club

            We’ll give you access to our curriculum, a template for your
            meetings, and our online community of club leaders

            Once you start holding meetings, we’ll check in with you each week
            to make sure everything is going well

          Contact us at team@hackclub.com{' '}
          if you have any questions while applying.

            This application was{' '}
            {submitted_at !== null
              ? 'submitted'
              : updated_at === created_at ? 'created' : 'updated'}{' '}
            {timeSince(updated_at)} ago

            You have {leaderProfile.completed_at ? null : <Neg />} finished your
            leader profile

          {coLeaderProfiles.map((profile, index) => (

              {profile.user.email}
              {profile.completed_at ? null : (

                  has <Neg />{' '}

              )}
              finished their leader profile

          ))}

        <Text color="slate" f={1}>

            * We also accept applications from clubs that have already held
            meetings.

        </Text>
      </CustomCard>
    </Container>
  )
}

export default class extends Component {
  constructor(props) {
    super(props)

    this.state = {
      status: 'loading',
      app: undefined,
      authToken: undefined,
      userId: undefined
    }

    this.populateApplications = this.populateApplications.bind(this)
    this.createNewApplication = this.createNewApplication.bind(this)
  }

  createNewApplication(userId) {
    const msg =
      'If you start a new application you won’t be able to access this one. Continue?'
    if (!confirm(msg)) {
      return null
    }
    return api
      .post(`v1/users/${userId}/new_club_applications`, {
        authToken: storage.get('authToken')
      })
      .then(app => {
        this.setState({
          status: 'finished',
          app: app
        })
      })
  }

  populateApplications(
    application,
    userId = storage.get('userId'),
    authToken = storage.get('authToken')
  ) {
    if (application) {
      this.setState({
        status: 'finished',
        app: application
      })
    } else {
      api
        .get(`v1/users/${userId}/new_club_applications`, { authToken })
        .then(json => {
          if (json.length === 0) {
            return this.createNewApplication(userId)
          }
          return json.sort((a, b) => {
            return new Date(b.created_at) - new Date(a.created_at)
          })[0]
        })
        .then(app => {
          this.setState({
            status: 'finished',
            app: app
          })
        })
        .catch(e => {
          if (e.status === 401) {
            this.setState({ status: 'needsToAuth' })
          }
        })
    }
  }

  componentDidMount() {
    const authToken = storage.get('authToken')
    const userId = storage.get('userId')
    this.setState({ authToken, userId })
    const needsToAuth = authToken === null || userId === null

    if (needsToAuth) {
      this.setState({ status: 'needsToAuth' })
    } else {
      this.populateApplications()
    }
  }

  content() {
    const { app, status, authToken, userId } = this.state

    switch (status) {
      case 'needsToAuth':
        return <Login />
      case 'loading':
        return <LoadingAnimation />
      case 'finished':
        return (
          <Fragment>
            <ApplyNav breadcrumb={0} />
            <Heading.h1
              bg="primary"
              color="white"
              f={[5, 6]}
              mt={-1}
              py={4}
              px={3}
              align="center"
            >
              Apply to Hack Club
            </Heading.h1>
            <ApplicationCard
              app={app}
              userId={userId}
              authToken={authToken}
              callback={this.populateApplications}
              resetCallback={this.createNewApplication}
            />
          </Fragment>
        )
      default:
        return <Text>Something terrible has happened.</Text>
    }
  }

  render() {
    return (
      <ThemeProvider>
        <Helmet title="Apply – Hack Club" />
        {this.content()}
      </ThemeProvider>
    )
  }
}
<commit_msg>Fix "Clarify how to successfully apply"<commit_after>import React, { Component, Fragment } from 'react'
import Helmet from 'react-helmet'
import api from 'api'
import storage from 'storage'
import {
  ThemeProvider,
  Box,
  Button,
  Card,
  Container,
  Section,
  Flex,
  Heading,
  LargeButton,
  Link as DSLink,
  Text,
  cx
} from '@hackclub/design-system'
import { clubApplicationSchema } from 'components/apply/ClubApplicationForm'
import ApplyNav from 'components/apply/ApplyNav'
import LeaderInviteForm from 'components/apply/LeaderInviteForm'
import Login from 'components/apply/Login'
import SubmitButton from 'components/apply/SubmitButton'
import LoadingAnimation from 'components/LoadingAnimation'
import fetch from 'unfetch'
import Link from 'gatsby-link'

LargeButton.link = LargeButton.withComponent(Link)

const A = DSLink.extend`
  cursor: pointer;
  :hover {
    text-decoration: underline;
  }
`

const timeSince = time => {
  const seconds = Math.floor((new Date() - new Date(time)) / 1000)
  const intervals = [
    [Math.floor(seconds / (60 * 60 * 24 * 7)), 'weeks'],
    [Math.floor(seconds / (60 * 60 * 24)), 'days'],
    [Math.floor(seconds / (60 * 60)), 'hours'],
    [Math.floor(seconds / 60), 'minutes']
  ]
  for (var i = 0; i < intervals.length; i++) {
    let interval = intervals[i]
    if (interval[0] > 1) {
      return interval.join(' ')
    }
  }
  return 'less than a minute'
}

const Neg = () => <Text.span color="error" bold children="NOT" />

const CustomCard = Card.extend.attrs({
  p: [3, 4],
  color: 'black',
  bg: 'snow',
  w: 1,
  boxShadowSize: 'md'
})`
  ul {
    padding-left: 0;
  }
`

const ApplicationCard = props => {
  const {
    id,
    leader_profiles,
    updated_at,
    created_at,
    submitted_at
  } = props.app
  const { authToken, callback, app, resetCallback } = props

  const leaderProfile = leader_profiles.find(
    profile => profile.user.id == props.userId
  )
  const coLeaderProfiles = leader_profiles.filter(
    profile => profile.user.id != props.userId
  )

  const completeProfiles = leader_profiles.every(
    profile => profile.completed_at
  )
  const completeApplication = clubApplicationSchema.isValidSync(app)
  let submitButtonStatus
  if (app.submitted_at) {
    submitButtonStatus = 'submitted'
  } else if (completeApplication && completeProfiles) {
    submitButtonStatus = 'complete'
  } else {
    submitButtonStatus = 'incomplete'
  }

  return (
    <Container maxWidth={36} my={3} p={3}>
      {app.rejected_at ? (
        <Flex mb={4}>
          <CustomCard>
            <Heading.h3>Unfortunatly, you’ve been rejected</Heading.h3>

            <Text>
              You can start a new application by clicking{' '}
              here.
            </Text>
          </CustomCard>
        </Flex>
      ) : null}
      <CustomCard>
        <Heading.h3 mb={2}>How to get into Hack Club</Heading.h3>
        <Text>
          When reviewing applications, we look for a strong leadership team that
          has obtained a teacher sponsor to host their club and the support of
          their school administration. Teams should have 2 to 3 co-leads — we
          rarely accept solo leaders.
        </Text>

        <Text>Invite your co-leads:</Text>
        <LeaderInviteForm id={id} authToken={authToken} callback={callback} />
        <Text>After you submit your application:</Text>

We’ll get back to you with our decision in 7 days

            If you’re accepted, we’ll schedule a call to train you to lead your
            club

            We’ll give you access to our curriculum, a template for your
            meetings, and our online community of club leaders

            Once you start holding meetings, we’ll check in with you each week
            to make sure everything is going well

          Contact us at team@hackclub.com{' '}
          if you have any questions while applying.

            This application was{' '}
            {submitted_at !== null
              ? 'submitted'
              : updated_at === created_at ? 'created' : 'updated'}{' '}
            {timeSince(updated_at)} ago

            You have {leaderProfile.completed_at ? null : <Neg />} finished your
            leader profile

          {coLeaderProfiles.map((profile, index) => (

              {profile.user.email}
              {profile.completed_at ? null : (

                  has <Neg />{' '}

              )}
              finished their leader profile

          ))}

        <Text color="slate" f={1}>

            * We also accept applications from clubs that have already held
            meetings.

        </Text>
      </CustomCard>
      <Flex
        align="center"
        justify="center"
        flexDirection={['column', 'row']}
        mt={4}
        mx={[null, -2]}
      >
        <LargeButton.link
          w={1}
          m={2}
          inverted
          to={`/apply/club?id=${id}`}
          children="Edit Application"
        />
        <LargeButton.link
          w={1}
          m={2}
          inverted
          to={`/apply/leader?id=${leaderProfile.id}`}
          children="Edit Leader Profile"
        />
      </Flex>
      <Flex mt={2} mb={4}>
        <SubmitButton authToken={authToken} application={app} />
      </Flex>
    </Container>
  )
}

export default class extends Component {
  constructor(props) {
    super(props)

    this.state = {
      status: 'loading',
      app: undefined,
      authToken: undefined,
      userId: undefined
    }

    this.populateApplications = this.populateApplications.bind(this)
    this.createNewApplication = this.createNewApplication.bind(this)
  }

  createNewApplication(userId) {
    const msg =
      'If you start a new application you won’t be able to access this one. Continue?'
    if (!confirm(msg)) {
      return null
    }
    return api
      .post(`v1/users/${userId}/new_club_applications`, {
        authToken: storage.get('authToken')
      })
      .then(app => {
        this.setState({
          status: 'finished',
          app: app
        })
      })
  }

  populateApplications(
    application,
    userId = storage.get('userId'),
    authToken = storage.get('authToken')
  ) {
    if (application) {
      this.setState({
        status: 'finished',
        app: application
      })
    } else {
      api
        .get(`v1/users/${userId}/new_club_applications`, { authToken })
        .then(json => {
          if (json.length === 0) {
            return this.createNewApplication(userId)
          }
          return json.sort((a, b) => {
            return new Date(b.created_at) - new Date(a.created_at)
          })[0]
        })
        .then(app => {
          this.setState({
            status: 'finished',
            app: app
          })
        })
        .catch(e => {
          if (e.status === 401) {
            this.setState({ status: 'needsToAuth' })
          }
        })
    }
  }

  componentDidMount() {
    const authToken = storage.get('authToken')
    const userId = storage.get('userId')
    this.setState({ authToken, userId })
    const needsToAuth = authToken === null || userId === null

    if (needsToAuth) {
      this.setState({ status: 'needsToAuth' })
    } else {
      this.populateApplications()
    }
  }

  content() {
    const { app, status, authToken, userId } = this.state

    switch (status) {
      case 'needsToAuth':
        return <Login />
      case 'loading':
        return <LoadingAnimation />
      case 'finished':
        return (
          <Fragment>
            <ApplyNav breadcrumb={0} />
            <Heading.h1
              bg="primary"
              color="white"
              f={[5, 6]}
              mt={-1}
              py={4}
              px={3}
              align="center"
            >
              Apply to Hack Club
            </Heading.h1>
            <ApplicationCard
              app={app}
              userId={userId}
              authToken={authToken}
              callback={this.populateApplications}
              resetCallback={this.createNewApplication}
            />
          </Fragment>
        )
      default:
        return <Text>Something terrible has happened.</Text>
    }
  }

  render() {
    return (
      <ThemeProvider>
        <Helmet title="Apply – Hack Club" />
        {this.content()}
      </ThemeProvider>
    )
  }
}
<|endoftext|>


<commit_before>import React from 'react'
import { datasets } from '../../lib/datasets'
import PlaceIntro from './place-intro'
import getWikiEntry from '../../lib/getWikiEntry'
import getPlaceData from '../../lib/getPlaceData'
import Map from '../home/map'
import Navbar from '../home/navbar'
import DataHighlights from './data-highlights'
import lngLatFromQuery from '../../lib/lngLatFromQuery'

export default class extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      lngLat: lngLatFromQuery(props.location.query),
      placeData: {},
      wikiEntry: '',
      datasets: datasets,
      selectedLayers: [],
      features: []
    }
  }

  componentWillReceiveProps (nextProps) {
    const lngLat = lngLatFromQuery(nextProps.location.query)
    this.lookupPlaceInfo(lngLat)
    this.queryRenderedFeatures(lngLat)
    this.setState({lngLat})
  }

  componentDidMount () {
    const { lngLat } = this.state
    this.lookupPlaceInfo(lngLat)
  }

  lookupPlaceInfo (lngLat) {
    getPlaceData(lngLat)
      .then((placeData) => {
        this.setState({placeData})
        const wikiQuery = placeData.place || placeData.address || placeData.postcode
        return getWikiEntry(wikiQuery)
      })
      .then((wikiEntry) => this.setState({wikiEntry}))
      .catch(function (err) { return console.error(err) })
  }

  queryRenderedFeatures (lngLat) {
    const { map } = this
    if (!map) return
    const features = map.queryRenderedFeatures(map.project(lngLat))
    this.setState({features})
  }

  onMapReady = (map) => {
    this.map = map
    const { lngLat } = this.state
    this.queryRenderedFeatures(lngLat)
  }

  render () {
    const { onMapReady } = this
    const { wikiEntry, placeData, lngLat, datasets, features, selectedLayers } = this.state
    return (

        <Navbar />

          <PlaceIntro lngLat={lngLat} wikiEntry={wikiEntry} placeData={placeData} features={features} />
          <DataHighlights selectedLayers={selectedLayers} datasets={datasets} lngLat={lngLat} features={features} />

          <Map lngLat={lngLat} zoom={13} minZoom={8} datasets={datasets} selectedLayers={this.state.selectedLayers} onMapReady={onMapReady} />

    )
  }
}
<commit_msg>Workaround map flicker by setting zoom to 11<commit_after>import React from 'react'
import { datasets } from '../../lib/datasets'
import PlaceIntro from './place-intro'
import getWikiEntry from '../../lib/getWikiEntry'
import getPlaceData from '../../lib/getPlaceData'
import Map from '../home/map'
import Navbar from '../home/navbar'
import DataHighlights from './data-highlights'
import lngLatFromQuery from '../../lib/lngLatFromQuery'

export default class extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      lngLat: lngLatFromQuery(props.location.query),
      placeData: {},
      wikiEntry: '',
      datasets: datasets,
      selectedLayers: [],
      features: []
    }
  }

  componentWillReceiveProps (nextProps) {
    const lngLat = lngLatFromQuery(nextProps.location.query)
    this.lookupPlaceInfo(lngLat)
    this.queryRenderedFeatures(lngLat)
    this.setState({lngLat})
  }

  componentDidMount () {
    const { lngLat } = this.state
    this.lookupPlaceInfo(lngLat)
  }

  lookupPlaceInfo (lngLat) {
    getPlaceData(lngLat)
      .then((placeData) => {
        this.setState({placeData})
        const wikiQuery = placeData.place || placeData.address || placeData.postcode
        return getWikiEntry(wikiQuery)
      })
      .then((wikiEntry) => this.setState({wikiEntry}))
      .catch(function (err) { return console.error(err) })
  }

  queryRenderedFeatures (lngLat) {
    const { map } = this
    if (!map) return
    const features = map.queryRenderedFeatures(map.project(lngLat))
    this.setState({features})
  }

  onMapReady = (map) => {
    this.map = map
    const { lngLat } = this.state
    this.queryRenderedFeatures(lngLat)
  }

  render () {
    const { onMapReady } = this
    const { wikiEntry, placeData, lngLat, datasets, features, selectedLayers } = this.state
    return (

        <Navbar />

          <PlaceIntro lngLat={lngLat} wikiEntry={wikiEntry} placeData={placeData} features={features} />
          <DataHighlights selectedLayers={selectedLayers} datasets={datasets} lngLat={lngLat} features={features} />

          <Map lngLat={lngLat} zoom={11} minZoom={8} datasets={datasets} selectedLayers={this.state.selectedLayers} onMapReady={onMapReady} />

    )
  }
}
<|endoftext|>


<commit_before>// javascript for the httpclient
url = "/http/proxy/";

$(document).ready(function(){
	$('#form').submit(function(){ sendMsg(); return false; });
	setInterval("checkMsgs()", 500);
});

function sendMsg() {
	if ($('#phone').val().length > 0 && $('#message').val().length > 0) {
		req = url + $('#phone').val() + "/" + $('#message').val();
		$.getJSON(
			req,
			function (response) { if (response) {
				snippet = '
' + response.phone + '	«	' + decode(response.message) + '	' + decode(response.message).length + ' characters
';
				$('#log').append(snippet);
				fixClasses();
				$('div.tester').scrollTo('#log tr:last', 800);
				$('#message').val("");
			}}
		);
	}
}

function fixClasses(){
	$('#log tr').removeClass('first');
	$('#log tr').removeClass('last');
	$('#log tr:first').addClass('first');
	$('#log tr:last').addClass('last');
}

function decode(str) {
	str = str.replace(/%23/gi, "#");
	str = str.replace(/%24/gi, "$");
	str = str.replace(/%26/gi, "&");
	str = str.replace(/%3D/gi, "=");
	str = str.replace(/%3B/gi, ";");
	str = str.replace(/%2C/gi, ",");
	str = str.replace(/%3A/gi, ":");
	str = str.replace(/%3F/gi, "?");
	str = decodeURI(str);
	return str;
}

function checkMsgs() {
	if ($('#phone').val().length > 0) {
		req = url + $('#phone').val() + "/json_resp";
		$.getJSON(
			req,
			function (response) { if (response) {
				snippet = '
' + response.phone + '	»	' + decode(response.message) + '	' + decode(response.message).length + ' characters
';
				$('#log').append(snippet);
				fixClasses();
				$('div.tester').scrollTo('#log tr:last', 800);
			}}

		);
	}
}
<commit_msg>slowed down the js calls<commit_after>// javascript for the httpclient
url = "/http/proxy/";

$(document).ready(function(){
	$('#form').submit(function(){ sendMsg(); return false; });
	setInterval("checkMsgs()", 5000);
});

function sendMsg() {
	if ($('#phone').val().length > 0 && $('#message').val().length > 0) {
		req = url + $('#phone').val() + "/" + $('#message').val();
		$.getJSON(
			req,
			function (response) { if (response) {
				snippet = '
' + response.phone + '	«	' + decode(response.message) + '	' + decode(response.message).length + ' characters
';
				$('#log').append(snippet);
				fixClasses();
				$('div.tester').scrollTo('#log tr:last', 800);
				$('#message').val("");
			}}
		);
	}
}

function fixClasses(){
	$('#log tr').removeClass('first');
	$('#log tr').removeClass('last');
	$('#log tr:first').addClass('first');
	$('#log tr:last').addClass('last');
}

function decode(str) {
	str = str.replace(/%23/gi, "#");
	str = str.replace(/%24/gi, "$");
	str = str.replace(/%26/gi, "&");
	str = str.replace(/%3D/gi, "=");
	str = str.replace(/%3B/gi, ";");
	str = str.replace(/%2C/gi, ",");
	str = str.replace(/%3A/gi, ":");
	str = str.replace(/%3F/gi, "?");
	str = decodeURI(str);
	return str;
}

function checkMsgs() {
	if ($('#phone').val().length > 0) {
		req = url + $('#phone').val() + "/json_resp";
		$.getJSON(
			req,
			function (response) { if (response) {
				snippet = '
' + response.phone + '	»	' + decode(response.message) + '	' + decode(response.message).length + ' characters
';
				$('#log').append(snippet);
				fixClasses();
				$('div.tester').scrollTo('#log tr:last', 800);
			}}

		);
	}
}
<|endoftext|>


<commit_before>var fs = require('fs');
var AWS = require('aws-sdk');
var async = require('async');
var mime = require('mime');
var ogger = require('./ogger');
var path = require('path');

AWS.config.update({
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
});

// TODO: Separate middleware from other logic.
// TODO: Is this needed?
module.exports = function(app) {

  // Testing
  var resourcesDir = '~';

  // Create folders if they don't exist.
  if(!(fs.existsSync(resourcesDir)))            {fs.mkdirSync(resourcesDir);}
  if(!(fs.existsSync(resourcesDir +'/sound')))  {fs.mkdirSync(resourcesDir + '/sound');}
    if(!(fs.existsSync(resourcesDir +'/image')))  {fs.mkdirSync(resourcesDir + '/image');}

  var amazonBaseAddress = "https://storypalette.s3.eu-central-1.amazonaws.com/sound%2F";

  var middleware = {

    // GET resized image: /image/64x64/myimage.jpg
    getImage: function(req, res) {

      // get image dimensions
      var params = req.params[0].split('/');
      var dimensions = params[0].split('x');
      var url;
      // console.log(params);
      if (dimensions[1]) {
        url = 'http://storypalette.imgix.net/' + params[1] + "?fit=clip&h=" + dimensions[0] + "&w=" + dimensions[1];
      } else{
        url = 'http://storypalette.imgix.net/' + params[0];
      }

      console.log('GET image ' + url);

      res.redirect(url);
    },
    // GET sound /sound/mysound.ogg
    // params = {id: 12345, ext: 'mp3'}
    getSound: function(req, res) {
      var url = amazonBaseAddress + req.params.id + '.ogg';
      // console.log('GET ' + url);
      res.redirect(url);
    },
    // Handle file uploads.
    postFile: function(req, res) {
      console.log('postfile');
      if(req.xhr) {
        var basename = require('path').basename;
        var fName = basename(req.header('x-file-name'));

        var ext2type = {
          aif: 'sound',
          aiff: 'sound',
          wav: 'sound',
          mp3: 'sound',
          ogg: 'sound',
          jpg: 'image',
          gif: 'image',
          jpeg: 'image',
          png: 'image'
        };

        var extension = fName.substring(fName.lastIndexOf('.')+1);
        var fileId = fName.substr(0, fName.indexOf("."));
        var localPathRaw = resourcesDir + '/' + ext2type[extension]+'/' + fName;
        var localPathOgg = resourcesDir + '/' + ext2type[extension]+'/' + fileId + '.ogg';
        var localPath = localPathRaw;
        var s3PathImage = 'image/' + fName;
        var s3PathSound = 'sound/' + fileId + '.ogg';
        var s3Path;

        if (ext2type[extension] == "image") {
          localPath = localPathRaw;
          s3Path    = s3PathImage;
        } else if (ext2type[extension] == "sound") {
          localPath = localPathOgg;
          s3Path    = s3PathSound;
        }

        // all sounds are uploaded to AWS S3
        async.waterfall([

          // upload temporarily to web server
          function(callback) {
            var ws = fs.createWriteStream(localPathRaw);
            req.on('data', function(data) {ws.write(data);});
            req.on('end', function(data) {ws.end();
              callback();
            });
          },
          function(callback) {
            if (ext2type[extension] == "sound") {
              // convert to ogg format
              ogger.convert(localPathRaw, localPathOgg, function() {
                callback();
              });
            } else {
              callback();
            }
          },
          // upload to amazon
          function(callback) {
            var body = fs.createReadStream(localPath);
            var s3obj = new AWS.S3({
              signatureVersion: 'v4',
              region: 'eu-central-1',
              params: {
                Bucket: 'storypalette',
                Key: s3Path
              }
            });

            s3obj.upload({Body: body}).
            on('httpUploadProgress', function (progress) {
              console.log(progress.loaded + " of " + progress.total + " bytes");

              if (progress.loaded == progress.total) {
                res.end('finished');
              }
            }).
            send(function(err, data) {
              console.log(err, data); });

            callback();
          },
          // remove temp file from web server
          function(callback) {
            fs.unlink(localPathRaw);

            // TODO improve?
            if (ext2type[extension] == "sound") {
              fs.unlink(localPathOgg);
            }

            callback();
          }
        ], function(err) {
          if (err) {
            res.status(500).send('upload failure');
          }
        });
      }
    }
  }; // middleware

  return middleware;
};
<commit_msg>added better error handling to file upload<commit_after>var fs = require('fs');
var AWS = require('aws-sdk');
var async = require('async');
var mime = require('mime');
var ogger = require('./ogger');
var path = require('path');

AWS.config.update({
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
});

// TODO: Separate middleware from other logic.
// TODO: Is this needed?
module.exports = function(app) {

  // Testing
  var resourcesDir = '~';

  // Create folders if they don't exist.
  if(!(fs.existsSync(resourcesDir)))            {fs.mkdirSync(resourcesDir);}
  if(!(fs.existsSync(resourcesDir +'/sound')))  {fs.mkdirSync(resourcesDir + '/sound');}
    if(!(fs.existsSync(resourcesDir +'/image')))  {fs.mkdirSync(resourcesDir + '/image');}

  var amazonBaseAddress = "https://storypalette.s3.eu-central-1.amazonaws.com/sound%2F";

  var middleware = {

    // GET resized image: /image/64x64/myimage.jpg
    getImage: function(req, res) {

      // get image dimensions
      var params = req.params[0].split('/');
      var dimensions = params[0].split('x');
      var url;
      // console.log(params);
      if (dimensions[1]) {
        url = 'http://storypalette.imgix.net/' + params[1] + "?fit=clip&h=" + dimensions[0] + "&w=" + dimensions[1];
      } else{
        url = 'http://storypalette.imgix.net/' + params[0];
      }

      console.log('GET image ' + url);

      res.redirect(url);
    },
    // GET sound /sound/mysound.ogg
    // params = {id: 12345, ext: 'mp3'}
    getSound: function(req, res) {
      var url = amazonBaseAddress + req.params.id + '.ogg';
      // console.log('GET ' + url);
      res.redirect(url);
    },
    // Handle file uploads.
    postFile: function(req, res) {
      console.log('postfile');
      if(req.xhr) {
        var basename = require('path').basename;
        var fName = basename(req.header('x-file-name'));

        var ext2type = {
          aif: 'sound',
          aiff: 'sound',
          wav: 'sound',
          mp3: 'sound',
          ogg: 'sound',
          jpg: 'image',
          gif: 'image',
          jpeg: 'image',
          png: 'image'
        };

        var extension = fName.substring(fName.lastIndexOf('.')+1);
        var fileId = fName.substr(0, fName.indexOf("."));
        var localPathRaw = resourcesDir + '/' + ext2type[extension]+'/' + fName;
        var localPathOgg = resourcesDir + '/' + ext2type[extension]+'/' + fileId + '.ogg';
        var localPath = localPathRaw;
        var s3PathImage = 'image/' + fName;
        var s3PathSound = 'sound/' + fileId + '.ogg';
        var s3Path;

        if (ext2type[extension] == "image") {
          localPath = localPathRaw;
          s3Path    = s3PathImage;
        } else if (ext2type[extension] == "sound") {
          localPath = localPathOgg;
          s3Path    = s3PathSound;
        }

        // all sounds are uploaded to AWS S3
        async.waterfall([

          // upload temporarily to web server
          function (callback) {
              try {
                  var ws = fs.createWriteStream(localPathRaw);
                  req.on('data', function(data) {ws.write(data);});
                  req.on('end', function(data) {ws.end();
                      callback();
                  });
              }
              catch (err)
              {
                  console.log("unable to transfer file to web server")
                  console.log(err.message);
              }
          },
          function (callback) {

            if (ext2type[extension] == "sound") {
                // convert to ogg format
                try {
                    ogger.convert(localPathRaw, localPathOgg, function() {
                        callback();
                    });
                }
                catch (err) {
                    console.log("unable to convert sound to ogg");
                    console.log(err.message);
                }
            } else {
              callback();
            }
          },
          // upload to amazon
          function(callback) {
            var body = fs.createReadStream(localPath);
            var s3obj = new AWS.S3({
              signatureVersion: 'v4',
              region: 'eu-central-1',
              params: {
                Bucket: 'storypalette',
                Key: s3Path
              }
            });

            try {
                s3obj.upload({Body: body}).
                on('httpUploadProgress', function (progress) {
                    console.log(progress.loaded + " of " + progress.total + " bytes");

                    if (progress.loaded == progress.total) {
                        res.end('finished');
                    }
                }).
                send(function(err, data) {
                    console.log(err, data); });
            }
            catch(err)
            {
                console.log("unable to transfer file to amazon s3");
                console.log(err.message);
            }

            callback();
          },
          // remove temp file from web server
          function(callback) {
            fs.unlink(localPathRaw);

            // TODO improve?
            if (ext2type[extension] == "sound") {
              fs.unlink(localPathOgg);
            }

            callback();
          }
        ], function(err) {
          if (err) {
            res.status(500).send('upload failure');
          }
        });
      }
    }
  }; // middleware

  return middleware;
};
<|endoftext|>


<commit_before>var fs = require('fs');
var AWS = require('aws-sdk');
var async = require('async');
var mime = require('mime');
var ogger = require('./ogger');
var path = require('path');
var config = require('config');

AWS.config.update({
  accessKeyId: config.server.aws.accessKeyId,
  secretAccessKey: config.server.aws.secretAccessKey,
});

// TODO: Separate middleware from other logic.
module.exports = function(app, config) {

  var resourcesDir = config.server.resources;

  // Create folders if they don't exist.
  if(!(fs.existsSync(resourcesDir)))            {fs.mkdirSync(resourcesDir);}
  if(!(fs.existsSync(resourcesDir +'/sound')))  {fs.mkdirSync(resourcesDir + '/sound');}
    if(!(fs.existsSync(resourcesDir +'/image')))  {fs.mkdirSync(resourcesDir + '/image');}

  var amazonBaseAddress = "https://storypalette.s3.eu-central-1.amazonaws.com/sound%2F";

  var middleware = {

    // GET resized image: /image/64x64/myimage.jpg
    getImage: function(req, res) {

      // get image dimensions
      var params = req.params[0].split('/');
      var dimensions = params[0].split('x');
      var url;
      console.log(params);
      if (dimensions[1])
        url = 'http://storypalette.imgix.net/' + params[1] + "?fit=clip&h=" + dimensions[0] + "&w=" + dimensions[1];
      else
        url = 'http://storypalette.imgix.net/' + params[0];

      console.log('GET ' + url);

      res.redirect(url);
    },
    // GET sound /sound/mysound.ogg
    // params = {id: 12345, ext: 'mp3'}
    getSound: function(req, res)
    {
      var url = amazonBaseAddress + req.params.id + '.ogg';
      console.log('GET ' + url);
      res.redirect(url);
    },
    // Handle file uploads.
    postFile: function(req, res) {
      console.log('postfile');
      if(req.xhr) {
        var basename = require('path').basename,
            fName = basename(req.header('x-file-name'));

        var ext2type = {
          mp3: 'sound',
          ogg: 'sound',
          jpg: 'image',
          gif: 'image',
          jpeg: 'image',
          png: 'image'
        };

        var extension = fName.substring(fName.lastIndexOf('.')+1);
        var fileId = fName.substr(0, fName.indexOf("."));
        var localPathRaw = resourcesDir + '/' + ext2type[extension]+'/' + fName;
        var localPathOgg = resourcesDir + '/' + ext2type[extension]+'/' + fileId + '.ogg';
        var localPath = localPathRaw;
        var s3PathImage = 'image/' + fName;
        var s3PathSound = 'sound/' + fileId + '.ogg';
        var s3Path;

        if (ext2type[extension] == "image") {
          localPath = localPathRaw;
          s3Path    = s3PathImage;
        } else if (ext2type[extension] == "sound") {
          localPath = localPathOgg;
          s3Path    = s3PathSound;
        }

        // all sounds are uploaded to AWS S3
        async.waterfall([

          // upload temporarily to web server
          function(callback) {
            var ws = fs.createWriteStream(localPathRaw);
            req.on('data', function(data) {ws.write(data);});
            req.on('end', function(data) {ws.end();
              callback();
            });
          },
          function(callback) {
            if (ext2type[extension] == "sound") {
              // convert to ogg format
              ogger.convert(localPathRaw, localPathOgg, function() {
                callback();
              });
            } else {
              callback();
            }
          },
          // upload to amazon
          function(callback) {
            var body = fs.createReadStream(localPath);
            var s3obj = new AWS.S3({
              signatureVersion: 'v4',
              region: 'eu-central-1',
              params: {
                Bucket: 'storypalette',
                Key: s3Path
              }
            });

            s3obj.upload({Body: body}).
            on('httpUploadProgress', function (progress) {
              console.log(progress.loaded + " of " + progress.total + " bytes");

              if (progress.loaded == progress.total) {
                res.end('finished');
              }
            }).
            send(function(err, data) { console.log(err, data); });

            callback();
          },
          // remove temp file from web server
          function(callback) {
            fs.unlink(localPathRaw);

            // TODO improve?
            if (ext2type[extension] == "sound") {
              fs.unlink(localPathOgg);
            }

            callback();
          }
        ], function(err) {
          if (err) {
            res.status(500).send('upload failure');
          }
        });
      }
    }
  }; // middleware

  return middleware;
};
<commit_msg>Commented out GET logging<commit_after>var fs = require('fs');
var AWS = require('aws-sdk');
var async = require('async');
var mime = require('mime');
var ogger = require('./ogger');
var path = require('path');
var config = require('config');

AWS.config.update({
  accessKeyId: config.server.aws.accessKeyId,
  secretAccessKey: config.server.aws.secretAccessKey,
});

// TODO: Separate middleware from other logic.
module.exports = function(app, config) {

  var resourcesDir = config.server.resources;

  // Create folders if they don't exist.
  if(!(fs.existsSync(resourcesDir)))            {fs.mkdirSync(resourcesDir);}
  if(!(fs.existsSync(resourcesDir +'/sound')))  {fs.mkdirSync(resourcesDir + '/sound');}
    if(!(fs.existsSync(resourcesDir +'/image')))  {fs.mkdirSync(resourcesDir + '/image');}

  var amazonBaseAddress = "https://storypalette.s3.eu-central-1.amazonaws.com/sound%2F";

  var middleware = {

    // GET resized image: /image/64x64/myimage.jpg
    getImage: function(req, res) {

      // get image dimensions
      var params = req.params[0].split('/');
      var dimensions = params[0].split('x');
      var url;
      // console.log(params);
      if (dimensions[1])
        url = 'http://storypalette.imgix.net/' + params[1] + "?fit=clip&h=" + dimensions[0] + "&w=" + dimensions[1];
      else
        url = 'http://storypalette.imgix.net/' + params[0];

      // console.log('GET ' + url);

      res.redirect(url);
    },
    // GET sound /sound/mysound.ogg
    // params = {id: 12345, ext: 'mp3'}
    getSound: function(req, res)
    {
      var url = amazonBaseAddress + req.params.id + '.ogg';
      // console.log('GET ' + url);
      res.redirect(url);
    },
    // Handle file uploads.
    postFile: function(req, res) {
      console.log('postfile');
      if(req.xhr) {
        var basename = require('path').basename,
            fName = basename(req.header('x-file-name'));

        var ext2type = {
          mp3: 'sound',
          ogg: 'sound',
          jpg: 'image',
          gif: 'image',
          jpeg: 'image',
          png: 'image'
        };

        var extension = fName.substring(fName.lastIndexOf('.')+1);
        var fileId = fName.substr(0, fName.indexOf("."));
        var localPathRaw = resourcesDir + '/' + ext2type[extension]+'/' + fName;
        var localPathOgg = resourcesDir + '/' + ext2type[extension]+'/' + fileId + '.ogg';
        var localPath = localPathRaw;
        var s3PathImage = 'image/' + fName;
        var s3PathSound = 'sound/' + fileId + '.ogg';
        var s3Path;

        if (ext2type[extension] == "image") {
          localPath = localPathRaw;
          s3Path    = s3PathImage;
        } else if (ext2type[extension] == "sound") {
          localPath = localPathOgg;
          s3Path    = s3PathSound;
        }

        // all sounds are uploaded to AWS S3
        async.waterfall([

          // upload temporarily to web server
          function(callback) {
            var ws = fs.createWriteStream(localPathRaw);
            req.on('data', function(data) {ws.write(data);});
            req.on('end', function(data) {ws.end();
              callback();
            });
          },
          function(callback) {
            if (ext2type[extension] == "sound") {
              // convert to ogg format
              ogger.convert(localPathRaw, localPathOgg, function() {
                callback();
              });
            } else {
              callback();
            }
          },
          // upload to amazon
          function(callback) {
            var body = fs.createReadStream(localPath);
            var s3obj = new AWS.S3({
              signatureVersion: 'v4',
              region: 'eu-central-1',
              params: {
                Bucket: 'storypalette',
                Key: s3Path
              }
            });

            s3obj.upload({Body: body}).
            on('httpUploadProgress', function (progress) {
              console.log(progress.loaded + " of " + progress.total + " bytes");

              if (progress.loaded == progress.total) {
                res.end('finished');
              }
            }).
            send(function(err, data) { console.log(err, data); });

            callback();
          },
          // remove temp file from web server
          function(callback) {
            fs.unlink(localPathRaw);

            // TODO improve?
            if (ext2type[extension] == "sound") {
              fs.unlink(localPathOgg);
            }

            callback();
          }
        ], function(err) {
          if (err) {
            res.status(500).send('upload failure');
          }
        });
      }
    }
  }; // middleware

  return middleware;
};
<|endoftext|>