<commit_before>"use strict";

var Application = require("neat-base").Application;
var Module = require("neat-base").Module;
var Tools = require("neat-base").Tools;
var Promise = require("bluebird");
var fs = require('fs');
var moment = require('moment');

var validFormats = [
    "gpx",
    "asc",
    "kml",
    "loc",
    // TODO: "wpt",
    "xml",
    "csv",
    "json"
];

var contentTypes = {
    gpx: "text/gpx; charset=utf-8",
    xml: "text/xml; charset=utf-8",
    asc: "text/asc; charset=utf-8",
    loc: "text/loc; charset=utf-8",
    csv: "text/csv; charset=utf-8",
    kml: "text/kml; charset=utf-8",
    json: "application/json; charset=utf-8"
};

module.exports = class PoiExport extends Module {

    static defaultConfig() {
        return {
            authModuleName: "auth",
            exportRoute: "/poi-export",
            elementsModuleName: "elements",
            webserverModuleName: "webserver",
            dbModuleName: "database",
            dbModelName: "pitch",
            exportFieldsMap: { // these are required
                gps: "gps", // required because mongoose pitch model has virtuals ( latitude, longitude ) that are referring to this field
                lat: "latitude",
                long: "longitude",
                country: "country",
                city: "city",
                zip: "zip",
                street: "street",
                type: "pitchType",
                name: "_de.name",
                createdAt: "_createdAt"
            },
            optionalFieldsMap: {
                seaLevel: {
                    key: "seaLevel"
                }
            }
        }
    }

    init() {
        return new Promise((resolve, reject) => {
            this.log.debug("Initializing...");

            if (Application.modules[this.config.webserverModuleName] && this.config.exportRoute) {
                Application.modules[this.config.webserverModuleName].addRoute("post", this.config.exportRoute, (req, res, next) => {
                    this.handleRequest(req, res);
                }, 9999);
            }

            return resolve(this);
        });
    }

    handleRequest(req, res) {

        if(!req.body.format || !req.body.query) {
            return res.status(400).end('no format and/or query given');
        }

        try {
            var model = Application.modules[this.config.dbModuleName].getModel(this.config.dbModelName);
        } catch (e) {
            return res.status(400).end('model "' + this.config.dbModelName + '" does not exist');
        }

        if (Application.modules[this.config.authModuleName]) {
            if (!Application.modules[this.config.authModuleName].hasPermission(req, this.config.dbModelName, "find")) {
                return res.status(401).end();
            }
        }

        var format = req.body.format.toLowerCase();
        var query = req.body.query.query || {};
        var limit = req.body.query.limit || 10;
        var page = req.body.query.page || 0;
        var sort = req.body.query.sort || {"_createdAt": -1};

        if(validFormats.indexOf(format) === -1) {
            return res.status(400).end('invalid format "'+ format +'". Valid formats are: ' + validFormats.join(', '));
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
        this.config.exportFields = [];

        for(var key in this.config.exportFieldsMap) {
            this.config.exportFields.push(this.config.exportFieldsMap[key]);
        }

        for(var key in this.config.optionalFieldsMap) {
            this.config.exportFields.push(this.config.optionalFieldsMap[key].key);
        }

        var dbQuery = model.find(query, this.config.exportFields.join(" ")).limit(limit).skip(limit * page).sort(sort);

        dbQuery.exec().then((docs) => {

            this.validatePOIs(docs).then((validPOIs) => {

                if(format === "json") {
                    return res.json(validPOIs);
                }

                this.exportPOIs(format, validPOIs, res);

            }, (err) => {
                res.err(err);
            })

        }, (err) => {
            res.err(err);
        });

    }

    validatePOIs(docs) {
        return new Promise((resolve, reject) => {
            var validPOIs = [];

            for(var i = 0; i<docs.length; i++) {
                var POI = docs[i];
                var skip = false;

                var tempObj = {};
                for(var key in this.config.exportFieldsMap) {
                    var field = this.config.exportFieldsMap[key];

                    if(!POI.get(field)) {
                        skip = true;
                        break;
                    } else {
                        tempObj[key] = POI.get(field);
                    }
                }

                if(!skip) {

                    for(var key in this.config.optionalFieldsMap) {
                        var field = this.config.optionalFieldsMap[key];

                        if(POI.get(field.key)) {
                            tempObj[key] = POI.get(field.key);
                        } else {
                            tempObj[key] = (field.default)?field.default:null;
                        }
                    }

                    validPOIs.push(tempObj);
                }
            }

            resolve(validPOIs);
        });
    }

    exportPOIs(format, POIs, res) {

        var fileData = this.getMainFileDataForFormat(format);
        var fileName = "POI-EXPORT_" + moment().format("DD.MM.YYYY");
        var waypoints = "";

        res.setHeader("content-type", contentTypes[format]);
        res.setHeader("Content-Disposition", "attachment;filename="+ fileName + "." + format);

        for(var i = 0; i<POIs.length; i++) {
            waypoints += this.createWaypoint(format,POIs[i]);
        }

        fileData = fileData.replace("{{POIDATA}}",waypoints);
        res.send(fileData);
    }

    getMainFileDataForFormat(format) {
        switch(format) {
            case "gpx":
                return '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>' +
                    '<gpx version="1.1" creator="Neat POI Export"><metadata>' +
                    '<author><name>Neat POI Export</name></author></metadata>' +
                    '{{POIDATA}}' +
                    '</gpx>';
                break;
            case "xml":
                return '<rss version="2.0" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:dc="http://purl.org/dc/elements/1.1/">\n' +
                    '  <channel>\n' +
                    '    <title>Neat POI Export</title>\n' +
                    '{{POIDATA}}' +
                    '  </channel>\n' +
                    '</rss>';
                break;
            case "loc":
                return '<?xml version="1.0" encoding="UTF-8" ?>\n' +
                    '<loc version="1.0" src="Neat POI Export">\n' +
                    '{{POIDATA}}' +
                    '</loc>'
                break;
            case "kml":
                return '<?xml version="1.0" encoding="UTF-8"?>\n' +
                    '<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/kml/2.2 http://schemas.opengis.net/kml/2.2.0/ogckml22.xsd">\n' +
                    '<Document>\n' +
                    '<name>Neat POI Export</name>' +
                    '<description><![CDATA[Generated by Neat POI Export - https://www.npmjs.com/package/neat-poi-export]]></description>' +
                    '{{POIDATA}}' +
                    '</Document>' +
                    '</kml>';
                break;
            case "csv":
                return 'Latitude,Longitude,Elevation\n{{POIDATA}}';
            default:
                return '{{POIDATA}}';
        }
    }

    createWaypoint(format, POI) {

        var waypoint = "";

        switch(format) {
            case "gpx":
                waypoint = '<wpt lat="'+ POI.lat +'" lon="'+ POI.long +'"><name>'+ POI.name +'</name><time>' + POI.createdAt + '</time><sym>RV Park (Outdoors)</sym></wpt>';
                break;
            case "xml":
                waypoint = '    <item>\n      <title>'+ POI.name +'</title>\n      <georss:point>'+ POI.lat +' '+ POI.long +'</georss:point>\n    </item>\n';
                break;
            case "asc":
                waypoint = POI.long + ',' + POI.lat + ',' + '"' + POI.name + '"\n';
                break;
            case "loc":
                waypoint = '<waypoint>' +
                    '<coord lat="'+ POI.lat +'" lon="'+ POI.long +'" />' +
                    '<type>RV Park (Outdoors)</type>' +
                    '<sym>RV Park (Outdoors)</sym>' +
                    '<ele>'+ (POI.seaLevel?POI.seaLevel:"0.0000000") +'</ele>' +
                    '<name id="'+ POI.name +'">'+ POI.name +'</name>' +
                    '</waypoint>';
                break;
            case "kml":
                waypoint = '<Placemark>' +
                    '<name>'+ POI.name +'</name>' +
                    '<Point>' +
                    '<coordinates>'+ POI.long + ',' + POI.lat + ',' + (POI.seaLevel?POI.seaLevel:"0.0000000") +'</coordinates>' +
                    '</Point>' +
                    '</Placemark>'
                break;
            case "csv":
                waypoint = POI.lat + ',' + POI.long + ',' + (POI.seaLevel?POI.seaLevel:"0.0") +'\n';
                break;
        }

        return waypoint;
    }
};<commit_msg>added neat-projection<commit_after>"use strict";

var Application = require("neat-base").Application;
var Module = require("neat-base").Module;
var Promise = require("bluebird");
var fs = require('fs');
var moment = require('moment');

var validFormats = [
    "gpx",
    "asc",
    "kml",
    "loc",
    // TODO: "wpt",
    "xml",
    "csv",
    "json"
];

var requiredFields = [
    "lat",
    "long",
    "name"
];

var contentTypes = {
    gpx: "text/gpx; charset=utf-8",
    xml: "text/xml; charset=utf-8",
    asc: "text/asc; charset=utf-8",
    loc: "text/loc; charset=utf-8",
    csv: "text/csv; charset=utf-8",
    kml: "text/kml; charset=utf-8",
    json: "application/json; charset=utf-8"
};

module.exports = class PoiExport extends Module {

    static defaultConfig() {
        return {
            projectionModuleName: "projection",
            authModuleName: "auth",
            exportRoute: "/poi-export",
            elementsModuleName: "elements",
            webserverModuleName: "webserver",
            dbModuleName: "database",
            dbModelName: "pitch"
        }
    }

    init() {
        return new Promise((resolve, reject) => {
            this.log.debug("Initializing...");

            if (Application.modules[this.config.webserverModuleName] && this.config.exportRoute) {
                Application.modules[this.config.webserverModuleName].addRoute("post", this.config.exportRoute, (req, res, next) => {
                    this.handleRequest(req, res);
                }, 9999);
            }

            return resolve(this);
        });
    }

    handleRequest(req, res) {

        if(!req.body.format || !req.body.query) {
            return res.status(400).end('no format and/or query given');
        }

        try {
            var model = Application.modules[this.config.dbModuleName].getModel(this.config.dbModelName);
        } catch (e) {
            return res.status(400).end('model "' + this.config.dbModelName + '" does not exist');
        }

        if (Application.modules[this.config.authModuleName]) {
            if (!Application.modules[this.config.authModuleName].hasPermission(req, this.config.dbModelName, "find")) {
                return res.status(401).end('permission denied');
            }
        }

        var format = req.body.format.toLowerCase();
        var query = req.body.query.query || {};
        var limit = req.body.query.limit || 10;
        var page = req.body.query.page || 0;
        var sort = req.body.query.sort || {"_createdAt": -1};
        var projection = req.body.query.projection || null;

        if(validFormats.indexOf(format) === -1) {
            return res.status(400).end('invalid format "'+ format +'". Valid formats are: ' + validFormats.join(', '));
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
        this.config.exportFields = [];

        for(var key in this.config.exportFieldsMap) {
            this.config.exportFields.push(this.config.exportFieldsMap[key]);
        }

        for(var key in this.config.optionalFieldsMap) {
            this.config.exportFields.push(this.config.optionalFieldsMap[key].key);
        }

        var dbQuery = model.find(query, this.config.exportFields.join(" ")).limit(limit).skip(limit * page).sort(sort);

        if (projection && Application.modules[this.config.projectionModuleName]) {
            dbQuery.projection(projection, req);
        } else {
            this.log.debug("Neat-projection is missing. Include it into your project to map fields from your mongoose Schema to the required ( " + requiredFields.join(', ') + " )!")
        }

        dbQuery.exec().then((docs) => {

            this.validatePOIs(docs).then((validPOIs) => {

                if(format === "json") {
                    return res.json(validPOIs);
                }

                this.exportPOIs(format, validPOIs, res);

            }, (err) => {
                res.err(err);
            })

        }, (err) => {
            res.err(err);
        });

    }

    validatePOIs(docs) {
        return new Promise((resolve, reject) => {
            var validPOIs = [];

            for(var i = 0; i<docs.length; i++) {
                var POI = docs[i];
                var skip = false;

                for(var x = 0; x<requiredFields.length; x++) {
                    if(!POI[requiredFields[x]]) {
                        skip = true;
                        break;
                    }
                }

                if(!skip) {
                    validPOIs.push(POI);
                }
            }

            resolve(validPOIs);
        });
    }

    exportPOIs(format, POIs, res) {

        var fileData = this.getMainFileDataForFormat(format);
        var fileName = "POI-EXPORT_" + moment().format("DD.MM.YYYY");
        var waypoints = "";

        res.setHeader("content-type", contentTypes[format]);
        res.setHeader("Content-Disposition", "attachment;filename="+ fileName + "." + format);

        for(var i = 0; i<POIs.length; i++) {
            waypoints += this.createWaypoint(format,POIs[i]);
        }

        fileData = fileData.replace("{{POIDATA}}",waypoints);
        res.send(fileData);
    }

    getMainFileDataForFormat(format) {
        switch(format) {
            case "gpx":
                return '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>' +
                    '<gpx version="1.1" creator="Neat POI Export"><metadata>' +
                    '<author><name>Neat POI Export</name></author></metadata>' +
                    '{{POIDATA}}' +
                    '</gpx>';
                break;
            case "xml":
                return '<rss version="2.0" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:dc="http://purl.org/dc/elements/1.1/">\n' +
                    '  <channel>\n' +
                    '    <title>Neat POI Export</title>\n' +
                    '{{POIDATA}}' +
                    '  </channel>\n' +
                    '</rss>';
                break;
            case "loc":
                return '<?xml version="1.0" encoding="UTF-8" ?>\n' +
                    '<loc version="1.0" src="Neat POI Export">\n' +
                    '{{POIDATA}}' +
                    '</loc>'
                break;
            case "kml":
                return '<?xml version="1.0" encoding="UTF-8"?>\n' +
                    '<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/kml/2.2 http://schemas.opengis.net/kml/2.2.0/ogckml22.xsd">\n' +
                    '<Document>\n' +
                    '<name>Neat POI Export</name>' +
                    '<description><![CDATA[Generated by Neat POI Export - https://www.npmjs.com/package/neat-poi-export]]></description>' +
                    '{{POIDATA}}' +
                    '</Document>' +
                    '</kml>';
                break;
            case "csv":
                return 'Latitude,Longitude,Elevation\n{{POIDATA}}';
            default:
                return '{{POIDATA}}';
        }
    }

    createWaypoint(format, POI) {

        var waypoint = "";

        switch(format) {
            case "gpx": // latitude, longitude, name, description, type, seaLevel, createdAt, symbol
                waypoint = '<wpt lat="'+ POI.lat +'" lon="'+ POI.long +'"><name>'+ POI.name +'</name>'+ ((POI.description)?('<desc>'+ POI.description +'</desc>'):'') +''+ ((POI.type)?('<type>'+ POI.type +'</type>'):'') +'<ele>'+ (POI.seaLevel?POI.seaLevel:"0.0000000") +'</ele><time>' + (new Date(POI.createdAt).toISOString()) + '</time><sym>'+ ((POI.symbol)?POI.symbol:'0') +'</sym></wpt>';
                break;
            case "xml":
                waypoint = '    <item>\n      '+ ((POI.createdAt)?('<pubDate>'+ (new Date(POI.createdAt).toISOString()) +'</pubDate>\n      '):'') +'<title>'+ POI.name +'</title>\n      '+ ((POI.description)?('<description>'+ POI.description +'</description>\n      '):'') +'<georss:point>'+ POI.lat +' '+ POI.long +'</georss:point>\n    </item>\n';
                break;
            case "asc":
                waypoint = POI.long + ',' + POI.lat + ',' + '"' + POI.name + '"\n';
                break;
            case "loc":
                waypoint = '<waypoint>' +
                    '<coord lat="'+ POI.lat +'" lon="'+ POI.long +'" />' +
                    ((POI.type)?('<type>'+ POI.type +'</type>'):'') +
                    '<sym>'+ ((POI.symbol)?POI.symbol:'0') +'</sym>' +
                    '<ele>'+ (POI.seaLevel?POI.seaLevel:"0.0000000") +'</ele>' +
                    '<name id="'+ POI.name +'">'+ POI.name +'</name>' +
                    '</waypoint>';
                break;
            case "kml":
                waypoint = '<Placemark>' +
                    '<name>'+ POI.name +'</name>' +
                    '<Point>' +
                    '<coordinates>'+ POI.long + ',' + POI.lat + ',' + (POI.seaLevel?POI.seaLevel:"0.0000000") +'</coordinates>' +
                    '</Point>' +
                    '</Placemark>'
                break;
            case "csv":
                waypoint = POI.lat + ',' + POI.long + ',' + (POI.seaLevel?POI.seaLevel:"0.0") +'\n';
                break;
        }

        return waypoint;
    }
};<|endoftext|>


<commit_before>/*
At the center of Tessel's GPRS Module lies the SIM900, documentation for which, including a full list of commands, can be found at: http://wm.sim.com/producten.aspx?id=1019
The full AT command manual is here: http://wm.sim.com/downloaden.aspx?id=2986
*/

var util = require('util');
var EventEmitter = require('events').EventEmitter;
var Packetizer = require('./packetizer.js');
var Postmaster = require('./postmaster.js');

function GPRS (hardware, secondaryHardware, baud) {
  /*
  Constructor

  Args
    hardware
      The Tessel port to be used for priary communication
    secondaryHardware
      The additional port that can be used for debug purposes. not required. Typically, D/A will be primary, C/B will be secondary
    baud
      Override the defualt baud rate of 115200 if necessary (for software UART)
  */

  var self = this;

  baud = baud || 9600;

  self.hardware = hardware;
  self.uart = new hardware.UART({baudrate: baud});
  self.power = hardware.gpio(3).high();
  self.packetizer = new Packetizer(self.uart);
  self.packetizer.packetize();
  self.inACall = false;
  self.notificationCallbacks = {};
  //  the defaults are fine for most of Postmaster's args
  self.postmaster = new Postmaster(self.packetizer, ['OK', 'ERROR', '> ']);

  //  second debug port is optional and largely unnecessary
  if (secondaryHardware) {
    self.debugHardware = secondaryHardware;
    self.debugUART = secondaryHardware.UART({baudrate: 115200});
    self.ringIndicator = secondaryHardware.gpio(3);
    self.debugPacketizer = new Packetizer(self.debugUART);
    self.debugPacketizer.packetize();
  }
}

util.inherits(GPRS, EventEmitter);

function use(hardware, debug, baud, callback) {
  /*
  Connect the GPRS module and establish contact with the SIM900

  Args
    hardware
      The Tessel port to use for the main GPRS hardware
    debug
      The debug port, if any, to use (null most of the time).
    baud
      Alternate baud rate for the UART
    callback
      Callback frunction for once the module is set up

    Callback parameters
      err
        Error, if any, while connecting. Passes null if successful.
  */

  callback = callback || function() {;};
  var radio = new GPRS(hardware, debug, baud);
  radio.establishContact(callback);
  return radio;
}

GPRS.prototype.txrx = function(message, patience, callback, alternate) {
  /*
  every time we interact with the sim900, it's through a series of uart calls and responses. this fucntion makes that less painful.

  Args
    message
      Atring you're sending, ie 'AT'
    patience
      Milliseconds until we stop listening. It's likely that the module is no longer responding to any single event if the reponse comes too much after we ping it.
    callback
      Callback function
    alternate
      An array of arrays of alternate starts and ends of reply post. of the form [[s1, s2 ...], [e1, e2, ...]]. Used in place of traditional controls.
      If the third element of alternate is truth-y, then the given start values only need exist within the incoming data (good for posts with known headers but unknown bodies).

 Callback parameters
    err
      Error object, if applicable
    recieved
      Ehe reply recieved within the interval
  */

  var self = this;

  message  = message  || 'AT';
  patience = patience || 250;
  callback = callback || ( function(err, arg) {
    if (err) {
      console.log('err:\n', err);
    }
    else {
      console.log('reply:\n', arg);
    };
  });
  alternate = alternate || null;
  //  it's a virtue, but mostly the module won't work if you're impatient
  patience = Math.max(patience, 100);

  self.postmaster.send(message, patience, callback, alternate);
}

GPRS.prototype.txrxchain = function(messages, patiences, replies, callback) {
  /*
  send a series of back-to-back messages recursively, do something with the final result. other results, if not of the form [<message>, <OK>] error out and pass false to the callback. args messages and patience must be of the same length.

  mesages
    An array of Strings to send as commands
  patiences
    An array of numbers; how long to wait for each command to return
  replies
    An array of expected replies (arrays of strings). If any index is false-y, its reply simply must not error out.
  callback
    Callback function. Args come from the last function in the chain.

  Callback parameters
    err
      Error
    data
      What the final message command returned OR false if the replies were not as expected
  */

  var self = this;
  if (messages.length != patiences.length || messages.length != replies.length) {
    console.log('length mismatch');
    callback(new Error('array lengths must match'), false);
  }
  else {
    var intermediate = function(err, data) {
      console.log('startng intermediate. e, r:', err, data)
      var correct = !err;
      if (replies[0]) {
        for (var i = 0; i < data.length; i++) {
          console.log('di', data[i], 'r0i', replies[0][i])
          correct = correct && data[i] == replies[0][i];
        }
      }
      self.emit('intermediate', correct);
    }
    //  not yet to the callback
    if (messages.length > 0) {
      var func = (messages.length === 1) ? callback:intermediate;
      self.txrx(messages[0], patiences[0], func, [[messages[0]], [replies[0][replies[0].length - 1]]]);
      if (func === intermediate) {
        self.once('intermediate', function(correct) {
          if (correct) {
            self.txrxchain(messages.slice(1), patiences.slice(1), replies.slice(1), callback);
          }
          else {
            self.postmaster.forceClear();
          }
        });
      }
    }
  }
}

GPRS.prototype.togglePower = function(callback) {
  /*
  Turn the module on or off by switching the power buton (G3) electronically
  */

  var self = this;
  self.power.high();
  setTimeout(function() {
    self.power.low();
    setTimeout(function() {
      self.power.high();
      setTimeout(function() {
        self.emit('powertoggled');
        if (callback) {
          callback();
        }
      }, 5000);
    }, 1000);
  }, 100);
}

GPRS.prototype.establishContact = function(callback, rep, reps) {
  /*
  Make contact with the GPRS module, emit the 'ready' event

  Args
    callback
      Callback function
    rep
      How many times have we tried?
    reps
      How many times until we give up

  Callback parameters
    err
      An error
    contacted
      true/false
  */

  var self = this;
  rep = rep || 0;
  reps = reps || 5;

  self.postmaster.send('AT', 1000, function(err, data) {
    //  too many tries = fail
    if (rep > reps) {
      var mess = 'Failed to connect to module because it could not be powered on and contacted after ' + reps + ' attempt(s)'
      callback(new Error(mess));
    }
    //  if we timeout on an AT, we're probably powered off. Toggle the power button and try again
    else if (err && err.message === 'no reply after 1000 ms to message "AT"') {
      self.togglePower();
      self.once('powertoggled', function() {
        self.establishContact(callback, rep + 1, reps);
      });
    }
    //  this is where we want to be
    else if (!err && data.length === 2 && data[0] === 'AT' && data[1] === 'OK') {
      setTimeout(function() {
        self.emit('ready');
      }, 1500);
      if (callback) {
        callback(null);
      }
    }
    else if (err && err.message != 'Postmaster busy') {
      self.establishContact(callback, rep + 1, reps);
    }
    //  this is just to be sure errors (with UART, presumably) get thrown properly
    else if (err) {
      callback(err);
    }
    else {
      setTimeout(function() {
        self.establishContact(callback, rep + 1, reps);
      }, 1500);
    }
  });
}

GPRS.prototype.sendSMS = function(number, message, callback) {
  /*
  Send an SMS to the specified number

  Args
    number
      String representation of the number. Must be at least 10 digits.
    message
      String to send
    callback
      Callback function

  Callback parameters
    err
      Error
    success
      Did it send properly? If yes, get back the ID number of the text, if not, the error and -1 as the ID.
  */

  var self = this;
  number = String(number) || '15555555555';
  message = message || 'text from a Tessel';

  commands  = ['AT+CMGF=1', 'AT+CMGS="' + number + '"', message];
  patiences = [2000, 5000, 5000];
  replies = [['AT+CMGF=1', 'OK'], ['AT+CMGS="' + number + '"', '> '], [message, '> ']];

  self.txrxchain(commands, patiences, replies, function(err, data) {
    //  manually check the last one
    var correct = !err && data[0] == message && data[1] == '> ';
    if (correct) {
      self.txrx(new Buffer([0x1a]), 10000, function(err, data) {
        var id = -1;
        var err = err || new Error('Unable to send SMS');
        if (data[0].indexOf('+CMGS: ') === 0 && data[1] == 'OK') {
          //  message sent!
          id = parseInt(data[0].slice(7), 10);
          err = null;
        }
        callback(err, id);
      }, [['+CMGS: ', 'ERROR'], ['OK', 'ERROR'], 1]);
    }
  });
}

GPRS.prototype.dial = function(number, callback) {
  /*
  Call the specified number (voice call, not data call)

  Args
    number
      String representation of the number. Must be at least 10 digits
    callback
      Callback function

  Callback parameters
    err
      Error, if applicable
    data
      [command echo, 'OK'] if all goes well
  */

  if (this.inACall) {
    callback(new Error('Currently in a call'), []);
  }
  else if (String(number).length < 10) {
    callback(new Error('Number must be at least 10 digits'), []);
  }
  else {
    this.inACall = true;
                                //  hang up in a year
    this.txrx('ATD' + number + ';', 1000*60*60*24*365, function(err, data) {
      this.inACall = false;
      callback(err, data);
    });
  }
}

GPRS.prototype.hangUp = function(callback) {
  /*
  terminate a voice call

  Args
    callback
      Callback function

  Callback parameters
    err
      Error
    data
      Reply upon hangup, hopefully ['ATH', 'OK']
  */

  var self = this;
  this.txrx('ATH', 100000, function(err, data) {
    self.inACall = false;
    callback(err, data);
  });
}

GPRS.prototype.answerCall = function(callback) {
  /*
  answer an incoming voice call

  Args
    callback
      Callback function

  Callback parameters
    err
      Error
    data
      ['ATA', 'OK'] if all goes well
  */

  var self = this;
  self.txrx('ATA', 10000, function(err, data) {
    if (!err) {
      self.inACall = true;
    }
    callback(err, data);
  });
}

GPRS.prototype.readSMS = function(index, mode, callback) {
  /*
  Read the specified SMS

  args - two possibilities
    index
      The index of the message to read. If not specified, the newest message is read. Note that the SIM900 is 1-indexed, not 0-indexed.
    mode
      0 - Mark the message as read
      1 - Do not chage the status of the message
    callback
      Callback function

  Callback parameters
    err
      Error
    message
      An array with
        0 - Command echo
        1 - Message information (read state, soure number, date, etc.)
        2 - Message text
        3 - 'OK'
      if successful
  */

  this.txrx('AT+CMGR=' + index + ',' + mode, 10000, callback);
}

GPRS.prototype.notifyOn = function(pairs) {
  /*
  Many unsolicited events are very useful to the user, such as when an SMS is recieved or a call is pending.

  Args
    pairs
      An Object which maps unsolicited message header Strings (e.g. '+CMT' = text message recieved) to callback functions.

 Ccallback parameters
    None, but the given functions in pairs should accept the follwing arguments:
      err
        An error, if applicable
      data
        The text from the unsolicited packet
  */

  var self = this;
  if (Object.keys(self.notificationCallbacks).length === 0) {
    //  this is the first time this was called, you shaould start notifying
    self.notify();
  }
  Object.keys(pairs).forEach(function(newKey) {
    //  note that this overwrites whatever may have been there
    self.notificationCallbacks[newKey] = pairs[newKey];
  });
}

GPRS.prototype.notify = function() {
  /*
  Run through the notificationCallbacks every time an unsolicited message comes in and call the related functions

  Args
    None (see notifyOn)

  Callback parameters
    None, but err and data are passed to the callbacks in notificationCallbacks
  */

  var self = this;
  self.postmaster.on('unsolicited', function(err, data) {
    //  on every unsolicited event
    Object.keys(self.notificationCallbacks).forEach(function(key) {
      if (data.indexOf(key) === 0) { //callThisFunction) {
        self.notificationCallbacks[key](err, data);
      }
    });
  });
}

module.exports.GPRS = GPRS;
module.exports.use = use;
module.exports.txrx = txrx;
module.exports.txrxchain = txrxchain;
module.exports.togglePower = togglePower;
module.exports.establishContact = establishContact;
module.exports.sendSMS = sendSMS;
module.exports.dial = dial;
module.exports.hangUp = hangUp;
module.exports.answerCall = answerCall;
module.exports.readSMS = readSMS;
module.exports.notifyOn = notifyOn;
module.exports.notify = notify;<commit_msg>fixed error on length mismatch for txrxchain<commit_after>/*
At the center of Tessel's GPRS Module lies the SIM900, documentation for which, including a full list of commands, can be found at: http://wm.sim.com/producten.aspx?id=1019
The full AT command manual is here: http://wm.sim.com/downloaden.aspx?id=2986
*/

var util = require('util');
var EventEmitter = require('events').EventEmitter;
var Packetizer = require('./packetizer.js');
var Postmaster = require('./postmaster.js');

function GPRS (hardware, secondaryHardware, baud) {
  /*
  Constructor

  Args
    hardware
      The Tessel port to be used for priary communication
    secondaryHardware
      The additional port that can be used for debug purposes. not required. Typically, D/A will be primary, C/B will be secondary
    baud
      Override the defualt baud rate of 115200 if necessary (for software UART)
  */

  var self = this;

  baud = baud || 9600;

  self.hardware = hardware;
  self.uart = new hardware.UART({baudrate: baud});
  self.power = hardware.gpio(3).high();
  self.packetizer = new Packetizer(self.uart);
  self.packetizer.packetize();
  self.inACall = false;
  self.notificationCallbacks = {};
  //  the defaults are fine for most of Postmaster's args
  self.postmaster = new Postmaster(self.packetizer, ['OK', 'ERROR', '> ']);

  //  second debug port is optional and largely unnecessary
  if (secondaryHardware) {
    self.debugHardware = secondaryHardware;
    self.debugUART = secondaryHardware.UART({baudrate: 115200});
    self.ringIndicator = secondaryHardware.gpio(3);
    self.debugPacketizer = new Packetizer(self.debugUART);
    self.debugPacketizer.packetize();
  }
}

util.inherits(GPRS, EventEmitter);

function use(hardware, debug, baud, callback) {
  /*
  Connect the GPRS module and establish contact with the SIM900

  Args
    hardware
      The Tessel port to use for the main GPRS hardware
    debug
      The debug port, if any, to use (null most of the time).
    baud
      Alternate baud rate for the UART
    callback
      Callback frunction for once the module is set up

    Callback parameters
      err
        Error, if any, while connecting. Passes null if successful.
  */

  callback = callback || function() {;};
  var radio = new GPRS(hardware, debug, baud);
  radio.establishContact(callback);
  return radio;
}

GPRS.prototype.txrx = function(message, patience, callback, alternate) {
  /*
  every time we interact with the sim900, it's through a series of uart calls and responses. this fucntion makes that less painful.

  Args
    message
      Atring you're sending, ie 'AT'
    patience
      Milliseconds until we stop listening. It's likely that the module is no longer responding to any single event if the reponse comes too much after we ping it.
    callback
      Callback function
    alternate
      An array of arrays of alternate starts and ends of reply post. of the form [[s1, s2 ...], [e1, e2, ...]]. Used in place of traditional controls.
      If the third element of alternate is truth-y, then the given start values only need exist within the incoming data (good for posts with known headers but unknown bodies).

 Callback parameters
    err
      Error object, if applicable
    recieved
      Ehe reply recieved within the interval
  */

  var self = this;

  message  = message  || 'AT';
  patience = patience || 250;
  callback = callback || ( function(err, arg) {
    if (err) {
      console.log('err:\n', err);
    }
    else {
      console.log('reply:\n', arg);
    };
  });
  alternate = alternate || null;
  //  it's a virtue, but mostly the module won't work if you're impatient
  patience = Math.max(patience, 100);

  self.postmaster.send(message, patience, callback, alternate);
}

GPRS.prototype.txrxchain = function(messages, patiences, replies, callback) {
  /*
  send a series of back-to-back messages recursively, do something with the final result. other results, if not of the form [<message>, <OK>] error out and pass false to the callback. args messages and patience must be of the same length.

  mesages
    An array of Strings to send as commands
  patiences
    An array of numbers; how long to wait for each command to return
  replies
    An array of expected replies (arrays of strings). If any index is false-y, its reply simply must not error out.
  callback
    Callback function. Args come from the last function in the chain.

  Callback parameters
    err
      Error
    data
      What the final message command returned OR false if the replies were not as expected
  */

  var self = this;
  if (messages.length != patiences.length || messages.length != replies.length) {
    callback(new Error('array lengths must match'), false);
  }
  else {
    var intermediate = function(err, data) {
      var correct = !err;
      if (replies[0]) {
        for (var i = 0; i < data.length; i++) {
          correct = correct && data[i] == replies[0][i];
        }
      }
      self.emit('intermediate', correct);
    }
    //  not yet to the callback
    if (messages.length > 0) {
      var func = (messages.length === 1) ? callback:intermediate;
      self.txrx(messages[0], patiences[0], func, [[messages[0]], [replies[0][replies[0].length - 1]]]);
      if (func === intermediate) {
        self.once('intermediate', function(correct) {
          if (correct) {
            self.txrxchain(messages.slice(1), patiences.slice(1), replies.slice(1), callback);
          }
          else {
            self.postmaster.forceClear();
          }
        });
      }
    }
  }
}

GPRS.prototype.togglePower = function(callback) {
  /*
  Turn the module on or off by switching the power buton (G3) electronically
  */

  var self = this;
  self.power.high();
  setTimeout(function() {
    self.power.low();
    setTimeout(function() {
      self.power.high();
      setTimeout(function() {
        self.emit('powertoggled');
        if (callback) {
          callback();
        }
      }, 5000);
    }, 1000);
  }, 100);
}

GPRS.prototype.establishContact = function(callback, rep, reps) {
  /*
  Make contact with the GPRS module, emit the 'ready' event

  Args
    callback
      Callback function
    rep
      How many times have we tried?
    reps
      How many times until we give up

  Callback parameters
    err
      An error
    contacted
      true/false
  */

  var self = this;
  rep = rep || 0;
  reps = reps || 5;

  self.postmaster.send('AT', 1000, function(err, data) {
    //  too many tries = fail
    if (rep > reps) {
      var mess = 'Failed to connect to module because it could not be powered on and contacted after ' + reps + ' attempt(s)'
      callback(new Error(mess));
    }
    //  if we timeout on an AT, we're probably powered off. Toggle the power button and try again
    else if (err && err.message === 'no reply after 1000 ms to message "AT"') {
      self.togglePower();
      self.once('powertoggled', function() {
        self.establishContact(callback, rep + 1, reps);
      });
    }
    //  this is where we want to be
    else if (!err && data.length === 2 && data[0] === 'AT' && data[1] === 'OK') {
      setTimeout(function() {
        self.emit('ready');
      }, 1500);
      if (callback) {
        callback(null);
      }
    }
    else if (err && err.message != 'Postmaster busy') {
      self.establishContact(callback, rep + 1, reps);
    }
    //  this is just to be sure errors (with UART, presumably) get thrown properly
    else if (err) {
      callback(err);
    }
    else {
      setTimeout(function() {
        self.establishContact(callback, rep + 1, reps);
      }, 1500);
    }
  });
}

GPRS.prototype.sendSMS = function(number, message, callback) {
  /*
  Send an SMS to the specified number

  Args
    number
      String representation of the number. Must be at least 10 digits.
    message
      String to send
    callback
      Callback function

  Callback parameters
    err
      Error
    success
      Did it send properly? If yes, get back the ID number of the text, if not, the error and -1 as the ID.
  */

  var self = this;
  number = String(number) || '15555555555';
  message = message || 'text from a Tessel';

  commands  = ['AT+CMGF=1', 'AT+CMGS="' + number + '"', message];
  patiences = [2000, 5000, 5000];
  replies = [['AT+CMGF=1', 'OK'], ['AT+CMGS="' + number + '"', '> '], [message, '> ']];

  self.txrxchain(commands, patiences, replies, function(err, data) {
    //  manually check the last one
    var correct = !err && data[0] == message && data[1] == '> ';
    if (correct) {
      self.txrx(new Buffer([0x1a]), 10000, function(err, data) {
        var id = -1;
        var err = err || new Error('Unable to send SMS');
        if (data[0].indexOf('+CMGS: ') === 0 && data[1] == 'OK') {
          //  message sent!
          id = parseInt(data[0].slice(7), 10);
          err = null;
        }
        callback(err, id);
      }, [['+CMGS: ', 'ERROR'], ['OK', 'ERROR'], 1]);
    }
  });
}

GPRS.prototype.dial = function(number, callback) {
  /*
  Call the specified number (voice call, not data call)

  Args
    number
      String representation of the number. Must be at least 10 digits
    callback
      Callback function

  Callback parameters
    err
      Error, if applicable
    data
      [command echo, 'OK'] if all goes well
  */

  if (this.inACall) {
    callback(new Error('Currently in a call'), []);
  }
  else if (String(number).length < 10) {
    callback(new Error('Number must be at least 10 digits'), []);
  }
  else {
    this.inACall = true;
                                //  hang up in a year
    this.txrx('ATD' + number + ';', 1000*60*60*24*365, function(err, data) {
      this.inACall = false;
      callback(err, data);
    });
  }
}

GPRS.prototype.hangUp = function(callback) {
  /*
  terminate a voice call

  Args
    callback
      Callback function

  Callback parameters
    err
      Error
    data
      Reply upon hangup, hopefully ['ATH', 'OK']
  */

  var self = this;
  this.txrx('ATH', 100000, function(err, data) {
    self.inACall = false;
    callback(err, data);
  });
}

GPRS.prototype.answerCall = function(callback) {
  /*
  answer an incoming voice call

  Args
    callback
      Callback function

  Callback parameters
    err
      Error
    data
      ['ATA', 'OK'] if all goes well
  */

  var self = this;
  self.txrx('ATA', 10000, function(err, data) {
    if (!err) {
      self.inACall = true;
    }
    callback(err, data);
  });
}

GPRS.prototype.readSMS = function(index, mode, callback) {
  /*
  Read the specified SMS

  args - two possibilities
    index
      The index of the message to read. If not specified, the newest message is read. Note that the SIM900 is 1-indexed, not 0-indexed.
    mode
      0 - Mark the message as read
      1 - Do not chage the status of the message
    callback
      Callback function

  Callback parameters
    err
      Error
    message
      An array with
        0 - Command echo
        1 - Message information (read state, soure number, date, etc.)
        2 - Message text
        3 - 'OK'
      if successful
  */

  this.txrx('AT+CMGR=' + index + ',' + mode, 10000, callback);
}

GPRS.prototype.notifyOn = function(pairs) {
  /*
  Many unsolicited events are very useful to the user, such as when an SMS is recieved or a call is pending.

  Args
    pairs
      An Object which maps unsolicited message header Strings (e.g. '+CMT' = text message recieved) to callback functions.

 Ccallback parameters
    None, but the given functions in pairs should accept the follwing arguments:
      err
        An error, if applicable
      data
        The text from the unsolicited packet
  */

  var self = this;
  if (Object.keys(self.notificationCallbacks).length === 0) {
    //  this is the first time this was called, you shaould start notifying
    self.notify();
  }
  Object.keys(pairs).forEach(function(newKey) {
    //  note that this overwrites whatever may have been there
    self.notificationCallbacks[newKey] = pairs[newKey];
  });
}

GPRS.prototype.notify = function() {
  /*
  Run through the notificationCallbacks every time an unsolicited message comes in and call the related functions

  Args
    None (see notifyOn)

  Callback parameters
    None, but err and data are passed to the callbacks in notificationCallbacks
  */

  var self = this;
  self.postmaster.on('unsolicited', function(err, data) {
    //  on every unsolicited event
    Object.keys(self.notificationCallbacks).forEach(function(key) {
      if (data.indexOf(key) === 0) { //callThisFunction) {
        self.notificationCallbacks[key](err, data);
      }
    });
  });
}

module.exports.GPRS = GPRS;
module.exports.use = use;
module.exports.txrx = txrx;
module.exports.txrxchain = txrxchain;
module.exports.togglePower = togglePower;
module.exports.establishContact = establishContact;
module.exports.sendSMS = sendSMS;
module.exports.dial = dial;
module.exports.hangUp = hangUp;
module.exports.answerCall = answerCall;
module.exports.readSMS = readSMS;
module.exports.notifyOn = notifyOn;
module.exports.notify = notify;<|endoftext|>


<commit_before>var isPromise = require('is-promise')

var extensions = [];

module.exports = Promise
function Promise(fn) {
  if (!(this instanceof Promise)) {
    return typeof fn === 'function' ? new Promise(fn) : defer()
  }
  var isResolved = false
  var isFulfilled = false
  var value
  var waiting = []
  var running = false

  function next(skipTimeout) {
    if (waiting.length) {
      running = true
      waiting.shift()(skipTimeout || false)
    } else {
      running = false
    }
  }
  this.then = then;
  function then(cb, eb) {
    return new Promise(function (resolver) {
      function done(skipTimeout) {
        var callback = isFulfilled ? cb : eb
        if (typeof callback === 'function') {
          function timeoutDone() {
            var val;
            try {
              val = callback(value)
            } catch (ex) {
              resolver.reject(ex)
              return next()
            }
            resolver.fulfill(val);
            next(true);
          }
          if (skipTimeout) timeoutDone()
          else setTimeout(timeoutDone, 0)
        } else if (isFulfilled) {
          resolver.fulfill(value)
          next(skipTimeout)
        } else {
          resolver.reject(value)
          next(skipTimeout)
        }
      }
      waiting.push(done)
      if (isResolved && !running) next()
    });
  }

  (function () {
    function resolve(val, success) {
      if (isResolved) return
      if (isPromise(val)) val.then(fulfill, reject)
      else {
        isResolved = true
        isFulfilled = success
        value = val
        next()
      }
    }
    function fulfill(val) {
      resolve(val, true)
    }
    function reject(err) {
      resolve(err, false)
    }
    var resolver = {fulfill: fulfill, reject: reject};
    for (var i = 0; i < extensions.length; i++) {
      extensions[i](this, resolver);
    }
    if (typeof fn === 'function') {
      fn(resolver)
    }
  }());
}
function defer() {
  var resolver
  var promise = new Promise(function (res) { resolver = res })
  return {resolver: resolver, promise: promise}
}
Promise.use = function (extension) {
  extensions.push(extension);
};<commit_msg>Support rejecting with an error<commit_after>var isPromise = require('is-promise')

var extensions = [];

module.exports = Promise
function Promise(fn) {
  if (!(this instanceof Promise)) {
    return typeof fn === 'function' ? new Promise(fn) : defer()
  }
  var isResolved = false
  var isFulfilled = false
  var value
  var waiting = []
  var running = false

  function next(skipTimeout) {
    if (waiting.length) {
      running = true
      waiting.shift()(skipTimeout || false)
    } else {
      running = false
    }
  }
  this.then = then;
  function then(cb, eb) {
    return new Promise(function (resolver) {
      function done(skipTimeout) {
        var callback = isFulfilled ? cb : eb
        if (typeof callback === 'function') {
          function timeoutDone() {
            var val;
            try {
              val = callback(value)
            } catch (ex) {
              resolver.reject(ex)
              return next()
            }
            resolver.fulfill(val);
            next(true);
          }
          if (skipTimeout) timeoutDone()
          else setTimeout(timeoutDone, 0)
        } else if (isFulfilled) {
          resolver.fulfill(value)
          next(skipTimeout)
        } else {
          resolver.reject(value)
          next(skipTimeout)
        }
      }
      waiting.push(done)
      if (isResolved && !running) next()
    });
  }

  (function () {
    function resolve(val, success) {
    }
    function fulfill(val) {
      if (isResolved) return
      if (isPromise(val)) val.then(fulfill, reject)
      else {
        isResolved = true
        isFulfilled = true
        value = val
        next()
      }
    }
    function reject(err) {
      if (isResolved) return
      isResolved = true
      isFulfilled = false
      value = err
      next()
    }
    var resolver = {fulfill: fulfill, reject: reject};
    for (var i = 0; i < extensions.length; i++) {
      extensions[i](this, resolver);
    }
    if (typeof fn === 'function') {
      fn(resolver)
    }
  }());
}
function defer() {
  var resolver
  var promise = new Promise(function (res) { resolver = res })
  return {resolver: resolver, promise: promise}
}
Promise.use = function (extension) {
  extensions.push(extension);
};<|endoftext|>


<commit_before>/*jslint node: true */
/* global -Promise */
"use strict";

var _ = require('lodash');
var Promise = require('bluebird');

var eventTree = function () {
    return {
        hash: {}
        , funcs: []
    };
};

var chainExecutions = function () {
    var ps = _.toArray(arguments);

    var execution = Promise.reduce(ps, function (called, p) {
        var result = p();
        if (result.isPending()) {
            return result.then(function (fcalled) {
                return called || fcalled;
            });
        }
        else {
            if (result.isRejected()) {
                return result;
            }
            else {
                return Promise.resolve(called || result.value());
            }
        }
    }, false);
    return execution;
};

var addCallback = function (route, tree, cb) {
    var head = _.head(route);
    if (!tree.hash.hasOwnProperty(head)) {
        tree.hash[head] = eventTree();
    }

    if (route.length > 1) {
        return addCallback(_.rest(route), tree.hash[head], cb);
    }
    else if (route.length === 1) {
        tree.hash[head].funcs.push(cb);
    }
};

var execTree = function (tree, msg) {
    if (!tree || (tree.funcs.length === 0)) {
        return Promise.resolve(false);
    }
    var funcs = tree.funcs;
    var execution = Promise.reduce(funcs, function (called, f) {
        return f(msg);
    }, false);
    if (execution.isPending()) {
        return execution.then(true);
    }
    else {
        if (execution.isRejected()) {
            return execution;
        }
        else {
            return Promise.resolve(true);
        }
    }
};

var applySubTrees = function (tree, method) {
    return Promise.map(_.pairs(tree.hash)
                       , function (kv) {
                           if (kv[0] === '**') {
                               return false;
                           }
                           else {
                               return method(kv[1]);
                           }
                       }, {concurrency: 1});
};

var execAll = function (tree, msg) {
    return chainExecutions(
        function () {
            return execTree(tree, msg);
        }
        , function () {
            return applySubTrees(tree, function (subtree) {
                return execAll(subtree, msg);
            });
        }
    );
};

var execMatch = function (target, tree, msg) {
    if (!tree) {
        return Promise.resolve(false);
    }
    var targetTree = tree.hash.hasOwnProperty(target) && tree.hash[target];
    if (!targetTree) {
        return Promise.resolve(false);
    }
    var doubleStarTree = targetTree.hash['**'];
    return Promise.join(execTree(targetTree, msg)
                        , execTree(doubleStarTree, msg))
        .then(_.any);
};

var noExecution = function () {
    return Promise.resolve(false);
};

var execCallbacks = function (route, tree, msg) {
    if (!tree) {
        return noExecution();
    }
    var head = _.head(route);
    if (head === '**') {
        return execAll(tree, msg);
    }
    else if (route.length > 1) {
        var rest = _.rest(route);
        if (head === '*') {
            return applySubTrees(tree, function (subtree) {
                return Promise.join(execTree(subtree.hash['**'], msg)
                                    , execCallbacks(rest, subtree, msg))
                    .then(_.any);
            });
        }
        else {
            var matchTree = tree.hash.hasOwnProperty(head) && tree.hash[head];
            return chainExecutions(function () {
                return execTree(tree.hash['*'] && tree.hash['*'].hash['**'], msg);
            }, function () {
                return execTree(matchTree && matchTree.hash['**'], msg);
            }, function () {
                return execCallbacks(rest, tree.hash['*'], msg);
            }, function () {
                return execCallbacks(rest, matchTree, msg);
            });
        }
    }
    else if (route.length === 1) {
        if (head === '*') {
            return applySubTrees(tree, function (subtree) {
                return execTree(subtree, msg);
            })
                .then(_.any);
        }
        else {
            return chainExecutions(function () {
                return execMatch('*', tree, msg);
            }, function () {
                return execMatch(head, tree, msg);
            });
        }
    }
    else {
        return noExecution();
    }
};

var filterFuncs = function (funcs, f) {
    if (f) {
        return _.filter(funcs, function (func) {
            return func.listener !== f;
        });
    }
    else {
        return [];
    }
};

var emptyTree = function (tree) {
    return tree.funcs.length === 0
        && _.isEmpty(tree.hash);
};

var deleteIfEmpty = function (target, hash) {
    if (emptyTree(hash[target])) {
        delete hash[target];
    }
};

var removeFunc = function (tree, f) {
    tree.funcs = filterFuncs(tree.funcs, f);
};

var removeCallback = function (route, tree, f) {
    var head = _.head(route);
    if (route.length > 1) {
        var rest = _.rest(route);
        if (head === '*') {
            return _.each(tree.hash, function (subtree, subhead) {
                if (subhead !== '**') {
                    removeCallback(rest, subtree, f);
                    deleteIfEmpty(subhead, tree.hash);
                }
            });
        }
        else if (tree.hash.hasOwnProperty(head)) {
            removeCallback(_.rest(route), tree.hash[head], f);
            deleteIfEmpty(head, tree.hash);
        }
    }
    else if (route.length === 1) {
        if (head === '**')  {
            removeFunc(tree, f);
            _.each(tree.hash, function (subtree, subhead) {
                removeCallback(route, subtree, f);
                deleteIfEmpty(subhead, tree.hash);
            });
        }
        if (head === '*') {
            _.each(tree.hash, function (subtree, subhead) {
                removeFunc(subtree, f);
                deleteIfEmpty(subhead, tree.hash);
            });
        }
        else if (tree.hash.hasOwnProperty(head)) {
            removeFunc(tree.hash[head], f);
            deleteIfEmpty(head, tree.hash);
        }
    }
};

var adaptCallback = function (cb) {
    var f;
    if (cb.length === 2) {
        f = function (msg) {
            return new Promise(function (resolve, reject) {
                return cb(msg, function (abort) {
                    if (abort) {
                        reject({
                            message: abort
                        });
                    }
                    else {
                        resolve(true);
                    }
                });
            });
        };
    }
    else {
        f = function (msg) {
            cb(msg);
            return Promise.resolve(true);
        };
    }
    f.listener = cb;
    return f;
};

var allListeners = function (eventTree) {
    return [].concat.apply(eventTree.funcs, _.map(eventTree.hash, allListeners));
};

var listenerFuncs = function (funcs) {
    return _.map(funcs, function (f) {
        return f.listener || f;
    });
};

var listeners = function (route, eventTree) {
    var head = _.head(route);
    var starListeners;
    if (route.length > 1) {
        var rest = _.rest(route);
        if (head == '*') {
            var funcs = eventTree.funcs;
            if (rest[0] == '**') {
                return [].concat.apply(funcs, _.map(eventTree.hash, function (subtree) {
                    return allListeners(subtree);
                }));
            }
            else {
                return [].concat.apply(funcs, _.map(eventTree.hash, function (subtree) {
                    return listeners(rest, subtree);
                }));
            }
        }
        else {
            starListeners = [];
            if (eventTree.hash.hasOwnProperty('**')) {
                starListeners = starListeners.concat(listeners(rest, eventTree.hash['**']));
            }
            if (eventTree.hash.hasOwnProperty('*')) {
                starListeners = starListeners.concat(listeners(rest, eventTree.hash['*']));
            }
            if (eventTree.hash.hasOwnProperty(head)) {
                if (rest[0] == '**') {
                    return starListeners.concat(allListeners(eventTree.hash[head]));
                }
                else {
                    return starListeners.concat(listeners(rest, eventTree.hash[head]));
                }
            }
            else {
                return starListeners;
            }
        }
    }
    else {
        if (head == '*') {
            return [].concat.apply(eventTree.funcs, _.map(eventTree.hash, function (subtree, name) {
                if (name == '**') {
                    return [];
                }
                else {
                    return subtree.funcs;
                }
            }));
        }
        else {
            starListeners = [];
            if (eventTree.hash.hasOwnProperty('*')) {
                starListeners = starListeners.concat(eventTree.hash['*'].funcs);
            }

            if (eventTree.hash.hasOwnProperty(head)) {
                return starListeners.concat(eventTree.hash[head].funcs);
            }
            else {
                return starListeners;
            }
        }
    }
};

var emit = function (eventTree, route, msg) {
    return chainExecutions(
        function () {
            return execTree(eventTree.hash['**'], msg);
        }
        , function () {
            return execCallbacks(route, eventTree, msg);
        });
};

var EventEmitter = function (options) {
    options = _.defaults({}, options, {
        delimiter: '/'
    });
    this._eventTree = eventTree();
    this._newListenerTree = eventTree();
    this.delimiter = options.delimiter;
};

_.extend(EventEmitter.prototype, {
    parseRoute: function (route) {
        if (_.isString(route))  {
            route = route.split(this.delimiter);
        }
        return route;
    }
    , on: function (route, cb) {
        route = this.parseRoute(route);
        if (route.length === 1
            && route[0] === 'newListener') {
            route = ['newListener', '**'];
        }
        emit(this._newListenerTree, route, {
            event: route
            , listener: cb
        });
        if (route[0] === 'newListener') {
            addCallback(route.slice(1), this._newListenerTree, adaptCallback(cb));
        }
        else {
            addCallback(route, this._eventTree, adaptCallback(cb));
        }
    }
    , emit: function (route, msg) {
        var _this = this;
        route = this.parseRoute(route);
        if (route[0] === 'newListener') {
            // why even allow this?
            emit(_this.newListenerTree, route.slice(1), msg);
        }
        else {
            emit(_this._eventTree, route, msg);
        }
    }
    , removeListener: function (route, f) {
        route = this.parseRoute(route);
        removeCallback(route, this._eventTree, f);
    }
    , once: function (route, cb) {
        route = this.parseRoute(route);
        var _this = this;
        var f = adaptCallback(cb);
        var g = function (msg) {
            _this.removeListener(route, cb);
            return f(msg);
        };
        g.listener = cb;
        emit(this._newListenerTree, route, {
            event: route
            , listener: cb
        });
        return addCallback(route, this._eventTree, g);
    }
    , removeAllListeners: function (route) {
        route = this.parseRoute(route);
        if (route[0] === '**') {
            this._eventTree = eventTree();
        }
        else {
            removeCallback(route, this._eventTree);
        }
    }
    , listeners: function (route) {
        route = this.parseRoute(route);
        if (route[0] == '**') {
            return listenerFuncs(allListeners(this._eventTree));
        }
        else {
            var starListeners = [];
            if (this._eventTree.hash.hasOwnProperty('**')) {
                starListeners = this._eventTree.hash['**'].funcs;
            }
            return listenerFuncs(starListeners.concat(listeners(route, this._eventTree)));
        }
    }
});

module.exports = {
    EventEmitter: EventEmitter
};
<commit_msg>Fix returning promise chain.<commit_after>/*jslint node: true */
/* global -Promise */
"use strict";

var _ = require('lodash');
var Promise = require('bluebird');

var eventTree = function () {
    return {
        hash: {}
        , funcs: []
    };
};

var chainExecutions = function () {
    var ps = _.toArray(arguments);

    var execution = Promise.reduce(ps, function (called, p) {
        var result = p();
        if (result.isPending()) {
            return result.then(function (fcalled) {
                return called || fcalled;
            });
        }
        else {
            if (result.isRejected()) {
                return result;
            }
            else {
                return Promise.resolve(called || result.value());
            }
        }
    }, false);
    return execution;
};

var addCallback = function (route, tree, cb) {
    var head = _.head(route);
    if (!tree.hash.hasOwnProperty(head)) {
        tree.hash[head] = eventTree();
    }

    if (route.length > 1) {
        return addCallback(_.rest(route), tree.hash[head], cb);
    }
    else if (route.length === 1) {
        tree.hash[head].funcs.push(cb);
    }
};

var execTree = function (tree, msg) {
    if (!tree || (tree.funcs.length === 0)) {
        return Promise.resolve(false);
    }
    var funcs = tree.funcs;
    var execution = Promise.reduce(funcs, function (called, f) {
        return f(msg);
    }, false);
    if (execution.isPending()) {
        return execution.then(true);
    }
    else {
        if (execution.isRejected()) {
            return execution;
        }
        else {
            return Promise.resolve(true);
        }
    }
};

var applySubTrees = function (tree, method) {
    return Promise.map(_.pairs(tree.hash)
                       , function (kv) {
                           if (kv[0] === '**') {
                               return false;
                           }
                           else {
                               return method(kv[1]);
                           }
                       }, {concurrency: 1});
};

var execAll = function (tree, msg) {
    return chainExecutions(
        function () {
            return execTree(tree, msg);
        }
        , function () {
            return applySubTrees(tree, function (subtree) {
                return execAll(subtree, msg);
            });
        }
    );
};

var execMatch = function (target, tree, msg) {
    if (!tree) {
        return Promise.resolve(false);
    }
    var targetTree = tree.hash.hasOwnProperty(target) && tree.hash[target];
    if (!targetTree) {
        return Promise.resolve(false);
    }
    var doubleStarTree = targetTree.hash['**'];
    return Promise.join(execTree(targetTree, msg)
                        , execTree(doubleStarTree, msg))
        .then(_.any);
};

var noExecution = function () {
    return Promise.resolve(false);
};

var execCallbacks = function (route, tree, msg) {
    if (!tree) {
        return noExecution();
    }
    var head = _.head(route);
    if (head === '**') {
        return execAll(tree, msg);
    }
    else if (route.length > 1) {
        var rest = _.rest(route);
        if (head === '*') {
            return applySubTrees(tree, function (subtree) {
                return Promise.join(execTree(subtree.hash['**'], msg)
                                    , execCallbacks(rest, subtree, msg))
                    .then(_.any);
            });
        }
        else {
            var matchTree = tree.hash.hasOwnProperty(head) && tree.hash[head];
            return chainExecutions(function () {
                return execTree(tree.hash['*'] && tree.hash['*'].hash['**'], msg);
            }, function () {
                return execTree(matchTree && matchTree.hash['**'], msg);
            }, function () {
                return execCallbacks(rest, tree.hash['*'], msg);
            }, function () {
                return execCallbacks(rest, matchTree, msg);
            });
        }
    }
    else if (route.length === 1) {
        if (head === '*') {
            return applySubTrees(tree, function (subtree) {
                return execTree(subtree, msg);
            })
                .then(_.any);
        }
        else {
            return chainExecutions(function () {
                return execMatch('*', tree, msg);
            }, function () {
                return execMatch(head, tree, msg);
            });
        }
    }
    else {
        return noExecution();
    }
};

var filterFuncs = function (funcs, f) {
    if (f) {
        return _.filter(funcs, function (func) {
            return func.listener !== f;
        });
    }
    else {
        return [];
    }
};

var emptyTree = function (tree) {
    return tree.funcs.length === 0
        && _.isEmpty(tree.hash);
};

var deleteIfEmpty = function (target, hash) {
    if (emptyTree(hash[target])) {
        delete hash[target];
    }
};

var removeFunc = function (tree, f) {
    tree.funcs = filterFuncs(tree.funcs, f);
};

var removeCallback = function (route, tree, f) {
    var head = _.head(route);
    if (route.length > 1) {
        var rest = _.rest(route);
        if (head === '*') {
            return _.each(tree.hash, function (subtree, subhead) {
                if (subhead !== '**') {
                    removeCallback(rest, subtree, f);
                    deleteIfEmpty(subhead, tree.hash);
                }
            });
        }
        else if (tree.hash.hasOwnProperty(head)) {
            removeCallback(_.rest(route), tree.hash[head], f);
            deleteIfEmpty(head, tree.hash);
        }
    }
    else if (route.length === 1) {
        if (head === '**')  {
            removeFunc(tree, f);
            _.each(tree.hash, function (subtree, subhead) {
                removeCallback(route, subtree, f);
                deleteIfEmpty(subhead, tree.hash);
            });
        }
        if (head === '*') {
            _.each(tree.hash, function (subtree, subhead) {
                removeFunc(subtree, f);
                deleteIfEmpty(subhead, tree.hash);
            });
        }
        else if (tree.hash.hasOwnProperty(head)) {
            removeFunc(tree.hash[head], f);
            deleteIfEmpty(head, tree.hash);
        }
    }
};

var adaptCallback = function (cb) {
    var f;
    if (cb.length === 2) {
        f = function (msg) {
            return new Promise(function (resolve, reject) {
                return cb(msg, function (abort) {
                    if (abort) {
                        reject({
                            message: abort
                        });
                    }
                    else {
                        resolve(true);
                    }
                });
            });
        };
    }
    else {
        f = function (msg) {
            cb(msg);
            return Promise.resolve(true);
        };
    }
    f.listener = cb;
    return f;
};

var allListeners = function (eventTree) {
    return [].concat.apply(eventTree.funcs, _.map(eventTree.hash, allListeners));
};

var listenerFuncs = function (funcs) {
    return _.map(funcs, function (f) {
        return f.listener || f;
    });
};

var listeners = function (route, eventTree) {
    var head = _.head(route);
    var starListeners;
    if (route.length > 1) {
        var rest = _.rest(route);
        if (head == '*') {
            var funcs = eventTree.funcs;
            if (rest[0] == '**') {
                return [].concat.apply(funcs, _.map(eventTree.hash, function (subtree) {
                    return allListeners(subtree);
                }));
            }
            else {
                return [].concat.apply(funcs, _.map(eventTree.hash, function (subtree) {
                    return listeners(rest, subtree);
                }));
            }
        }
        else {
            starListeners = [];
            if (eventTree.hash.hasOwnProperty('**')) {
                starListeners = starListeners.concat(listeners(rest, eventTree.hash['**']));
            }
            if (eventTree.hash.hasOwnProperty('*')) {
                starListeners = starListeners.concat(listeners(rest, eventTree.hash['*']));
            }
            if (eventTree.hash.hasOwnProperty(head)) {
                if (rest[0] == '**') {
                    return starListeners.concat(allListeners(eventTree.hash[head]));
                }
                else {
                    return starListeners.concat(listeners(rest, eventTree.hash[head]));
                }
            }
            else {
                return starListeners;
            }
        }
    }
    else {
        if (head == '*') {
            return [].concat.apply(eventTree.funcs, _.map(eventTree.hash, function (subtree, name) {
                if (name == '**') {
                    return [];
                }
                else {
                    return subtree.funcs;
                }
            }));
        }
        else {
            starListeners = [];
            if (eventTree.hash.hasOwnProperty('*')) {
                starListeners = starListeners.concat(eventTree.hash['*'].funcs);
            }

            if (eventTree.hash.hasOwnProperty(head)) {
                return starListeners.concat(eventTree.hash[head].funcs);
            }
            else {
                return starListeners;
            }
        }
    }
};

var emit = function (eventTree, route, msg) {
    return chainExecutions(
        function () {
            return execTree(eventTree.hash['**'], msg);
        }
        , function () {
            return execCallbacks(route, eventTree, msg);
        });
};

var EventEmitter = function (options) {
    options = _.defaults({}, options, {
        delimiter: '/'
    });
    this._eventTree = eventTree();
    this._newListenerTree = eventTree();
    this.delimiter = options.delimiter;
};

_.extend(EventEmitter.prototype, {
    parseRoute: function (route) {
        if (_.isString(route))  {
            route = route.split(this.delimiter);
        }
        return route;
    }
    , on: function (route, cb) {
        route = this.parseRoute(route);
        if (route.length === 1
            && route[0] === 'newListener') {
            route = ['newListener', '**'];
        }
        emit(this._newListenerTree, route, {
            event: route
            , listener: cb
        });
        if (route[0] === 'newListener') {
            addCallback(route.slice(1), this._newListenerTree, adaptCallback(cb));
        }
        else {
            addCallback(route, this._eventTree, adaptCallback(cb));
        }
    }
    , emit: function (route, msg) {
        var _this = this;
        route = this.parseRoute(route);
        if (route[0] === 'newListener') {
            // why even allow this?
            return emit(_this.newListenerTree, route.slice(1), msg);
        }
        else {
            return emit(_this._eventTree, route, msg);
        }
    }
    , removeListener: function (route, f) {
        route = this.parseRoute(route);
        removeCallback(route, this._eventTree, f);
    }
    , once: function (route, cb) {
        route = this.parseRoute(route);
        var _this = this;
        var f = adaptCallback(cb);
        var g = function (msg) {
            _this.removeListener(route, cb);
            return f(msg);
        };
        g.listener = cb;
        emit(this._newListenerTree, route, {
            event: route
            , listener: cb
        });
        return addCallback(route, this._eventTree, g);
    }
    , removeAllListeners: function (route) {
        route = this.parseRoute(route);
        if (route[0] === '**') {
            this._eventTree = eventTree();
        }
        else {
            removeCallback(route, this._eventTree);
        }
    }
    , listeners: function (route) {
        route = this.parseRoute(route);
        if (route[0] == '**') {
            return listenerFuncs(allListeners(this._eventTree));
        }
        else {
            var starListeners = [];
            if (this._eventTree.hash.hasOwnProperty('**')) {
                starListeners = this._eventTree.hash['**'].funcs;
            }
            return listenerFuncs(starListeners.concat(listeners(route, this._eventTree)));
        }
    }
});

module.exports = {
    EventEmitter: EventEmitter
};
<|endoftext|>


<commit_before>console.log('Loading event');

var doc = require('dynamodb-doc');
var request = require('request');

var dynamodb = new doc.DynamoDB();

exports.handler = function(event, context) {
    console.log("Request received:", JSON.stringify(event));
    console.log("Context received:", JSON.stringify(context));

    var options = {
      url: 'http://www.stateful.co/c/apievent/inc?value=1',
      headers: {
        'X-Sttc-URN': 'urn:github:5019965',
        'X-Sttc-Token': '8F37-909C-E861-3855',
        'Accept': 'text/plain'
      }
    };

    function callback(error,response,body) {
      if (!error && response.statusCode == 200) {
        saveData(body);
      } else {
        context.fail('ERROR: counter call failed with statusCode ('+response.statusCode+'): ' + error);
      }
    }

    request(options,callback);

    function saveData(counter) {
      var tableName = "APIEvents";
      var datetime = new Date().getTime().toString();
      event.id = counter;
      event.date = datetime;

      dynamodb.putItem(
        {
          "TableName": tableName,
          "Item": event
        },
        function(err, data) {
          if (err) {
            context.fail('ERROR: Dynamo failed: ' + err);
          } else {
            console.log('Dynamo Success: ' + JSON.stringify(data, null, '  '));
            context.succeed('SUCCESS');
          }
        }
      );

      console.log("Item:", event);
    }

};
<commit_msg>force id to be a number<commit_after>console.log('Loading event');

var doc = require('dynamodb-doc');
var request = require('request');

var dynamodb = new doc.DynamoDB();

exports.handler = function(event, context) {
    console.log("Request received:", JSON.stringify(event));
    console.log("Context received:", JSON.stringify(context));

    var options = {
      url: 'http://www.stateful.co/c/apievent/inc?value=1',
      headers: {
        'X-Sttc-URN': 'urn:github:5019965',
        'X-Sttc-Token': '8F37-909C-E861-3855',
        'Accept': 'text/plain'
      }
    };

    function callback(error,response,body) {
      if (!error && response.statusCode == 200) {
        saveData(body);
      } else {
        context.fail('ERROR: counter call failed with statusCode ('+response.statusCode+'): ' + error);
      }
    }

    request(options,callback);

    function saveData(counter) {
      var tableName = "APIEvents";
      var datetime = new Date().getTime().toString();
      event.id = Number(counter);
      event.date = datetime;

      dynamodb.putItem(
        {
          "TableName": tableName,
          "Item": event
        },
        function(err, data) {
          if (err) {
            context.fail('ERROR: Dynamo failed: ' + err);
          } else {
            console.log('Dynamo Success: ' + JSON.stringify(data, null, '  '));
            context.succeed('SUCCESS');
          }
        }
      );

      console.log("Item:", event);
    }

};
<|endoftext|>


<commit_before>/**
 * Module dependencies
 */

var path = require('path');
var Writable = require('stream').Writable;
var Transform = require('stream').Transform;
var concat = require('concat-stream');
var _ = require('lodash');
_.defaultsDeep = require('merge-defaults');
var oss = require('ali-oss');
var knox = require('knox');
var S3MultipartUpload = require('knox-mpu-alt');
var S3Lister = require('s3-lister');
var mime = require('mime');
var co = require('co');

/**
 * skipper-aliyunoss
 *
 * @param  {Object} globalOpts
 * @return {Object}
 */

module.exports = function SkipperS3(globalOpts) {
  globalOpts = globalOpts || {};

  var adapter = {
    read: function (fd, cb) {
      var prefix = fd;
      var store = oss({
        accessKeyId: globalOpts.key,
        accessKeySecret: globalOpts.secret,
        bucket: globalOpts.bucket,
        region: globalOpts.region || undefined
      });

      // Build a noop transform stream that will pump the S3 output through
      var __transform__ = new Transform();
      __transform__._transform = function (chunk, encoding, callback) {
        return callback(null, chunk);
      };

      co(store.getStream(prefix))
        .then(function (getStreamResult) {
          // check whether we got an actual file stream:
          if (getStreamResult.status < 300) {
            getStreamResult.stream.once('error', function (err) {
              __transform__.emit('error', err);
            });
            getStreamResult.stream.pipe(__transform__);
          } else {
            var err = new Error();
            err.status = getStreamResult.status;
            err.headers = getStreamResult.headers;
            err.message = 'Non-200 status code returned from Aliyun for requested file.';
            __transform__.emit('error', err);
          }
        })
        .catch(function (err) {
          __transform__.emit('error', err);
        });

      if (cb) {
        var firedCb;
        __transform__.once('error', function (err) {
          if (firedCb) return;
          firedCb = true;
          cb(err);
        });
        __transform__.pipe(concat(function (data) {
          if (firedCb) return;
          firedCb = true;
          cb(null, data);
        }));
      }

      return __transform__;
    },

    rm: function (fd, cb) {
      var store = oss({
        accessKeyId: globalOpts.key,
        accessKeySecret: globalOpts.secret,
        bucket: globalOpts.bucket,
        region: globalOpts.region || undefined
      });

      co(store.delete(fd))
        .then(function (result) {
          if (result.status == 200) {
            cb();
          } else {
            cb({
              statusCode: result.status,
              message: result
            });
          }
        })
        .catch(function (err) {
          cb(err);
        });
    },
    ls: function (dirname, cb) {
      var store = oss({
        accessKeyId: globalOpts.key,
        accessKeySecret: globalOpts.secret,
        bucket: globalOpts.bucket,
        region: globalOpts.region || undefined
      });

      // Strip leading slash from dirname to form prefix
      var prefix = dirname.replace(/^\//, '');

      co(store.list({prefix: prefix}))
        .then(function (result) {
          cb(null, result.objects);
        })
        .catch(function (err) {
          cb(err, null);
        });
    },

    receive: AliyunReceiver
  };

  return adapter;

  /**
   * A simple receiver for Skipper that writes Upstreams to
   * S3 to the configured bucket at the configured path.
   *
   * Includes a garbage-collection mechanism for failed
   * uploads.
   *
   * @param  {Object} options
   * @return {Stream.Writable}
   */
  function AliyunReceiver(options) {
    options = options || {};
    options = _.defaults(options, globalOpts);

    var receiver__ = Writable({
      objectMode: true
    });

    receiver__._write = function onFile(__newFile, encoding, next) {
      var store = oss({
        accessKeyId: options.key,
        accessKeySecret: options.secret,
        bucket: options.bucket,
        region: globalOpts.region || 'oss-cn-qingdao'
      });
      console.log(JSON.stringify(__newFile.fd));
      co(store.putStream(__newFile.fd, __newFile))
        .then(function (result) {
          __newFile.extra = result;
          __newFile.byteCount = result.size;
          receiver__.emit('writefile', __newFile);
          next();
        })
        .catch(function (err) {
          return next({
            incoming: __newFile,
            code: 'E_WRITE',
            stack: typeof err === 'object' ? err.stack : new Error(err),
            name: typeof err === 'object' ? err.name : err,
            message: typeof err === 'object' ? err.message : err
          });
        });

    };

    return receiver__;
  }
};

<commit_msg>fix size<commit_after>/**
 * Module dependencies
 */

var path = require('path');
var Writable = require('stream').Writable;
var Transform = require('stream').Transform;
var concat = require('concat-stream');
var _ = require('lodash');
_.defaultsDeep = require('merge-defaults');
var oss = require('ali-oss');
var knox = require('knox');
var S3MultipartUpload = require('knox-mpu-alt');
var S3Lister = require('s3-lister');
var mime = require('mime');
var co = require('co');

/**
 * skipper-aliyunoss
 *
 * @param  {Object} globalOpts
 * @return {Object}
 */

module.exports = function SkipperS3(globalOpts) {
  globalOpts = globalOpts || {};

  var adapter = {
    read: function (fd, cb) {
      var prefix = fd;
      var store = oss({
        accessKeyId: globalOpts.key,
        accessKeySecret: globalOpts.secret,
        bucket: globalOpts.bucket,
        region: globalOpts.region || undefined
      });

      // Build a noop transform stream that will pump the S3 output through
      var __transform__ = new Transform();
      __transform__._transform = function (chunk, encoding, callback) {
        return callback(null, chunk);
      };

      co(store.getStream(prefix))
        .then(function (getStreamResult) {
          // check whether we got an actual file stream:
          if (getStreamResult.status < 300) {
            getStreamResult.stream.once('error', function (err) {
              __transform__.emit('error', err);
            });
            getStreamResult.stream.pipe(__transform__);
          } else {
            var err = new Error();
            err.status = getStreamResult.status;
            err.headers = getStreamResult.headers;
            err.message = 'Non-200 status code returned from Aliyun for requested file.';
            __transform__.emit('error', err);
          }
        })
        .catch(function (err) {
          __transform__.emit('error', err);
        });

      if (cb) {
        var firedCb;
        __transform__.once('error', function (err) {
          if (firedCb) return;
          firedCb = true;
          cb(err);
        });
        __transform__.pipe(concat(function (data) {
          if (firedCb) return;
          firedCb = true;
          cb(null, data);
        }));
      }

      return __transform__;
    },

    rm: function (fd, cb) {
      var store = oss({
        accessKeyId: globalOpts.key,
        accessKeySecret: globalOpts.secret,
        bucket: globalOpts.bucket,
        region: globalOpts.region || undefined
      });

      co(store.delete(fd))
        .then(function (result) {
          if (result.status == 200) {
            cb();
          } else {
            cb({
              statusCode: result.status,
              message: result
            });
          }
        })
        .catch(function (err) {
          cb(err);
        });
    },
    ls: function (dirname, cb) {
      var store = oss({
        accessKeyId: globalOpts.key,
        accessKeySecret: globalOpts.secret,
        bucket: globalOpts.bucket,
        region: globalOpts.region || undefined
      });

      // Strip leading slash from dirname to form prefix
      var prefix = dirname.replace(/^\//, '');

      co(store.list({prefix: prefix}))
        .then(function (result) {
          cb(null, result.objects);
        })
        .catch(function (err) {
          cb(err, null);
        });
    },

    receive: AliyunReceiver
  };

  return adapter;

  /**
   * A simple receiver for Skipper that writes Upstreams to
   * S3 to the configured bucket at the configured path.
   *
   * Includes a garbage-collection mechanism for failed
   * uploads.
   *
   * @param  {Object} options
   * @return {Stream.Writable}
   */
  function AliyunReceiver(options) {
    options = options || {};
    options = _.defaults(options, globalOpts);

    var receiver__ = Writable({
      objectMode: true
    });

    receiver__._write = function onFile(__newFile, encoding, next) {
      var store = oss({
        accessKeyId: options.key,
        accessKeySecret: options.secret,
        bucket: options.bucket,
        region: globalOpts.region || 'oss-cn-qingdao'
      });
      console.log(JSON.stringify(__newFile.fd));
      co(store.putStream(__newFile.fd, __newFile))
        .then(function (result) {
          __newFile.size = result.size;
          __newFile.extra = result;
          __newFile.byteCount = result.size;
          receiver__.emit('writefile', __newFile);
          next();
        })
        .catch(function (err) {
          return next({
            incoming: __newFile,
            code: 'E_WRITE',
            stack: typeof err === 'object' ? err.stack : new Error(err),
            name: typeof err === 'object' ? err.name : err,
            message: typeof err === 'object' ? err.message : err
          });
        });

    };

    return receiver__;
  }
};

<|endoftext|>


<commit_before>module.exports = require("./dist/Closest");
<commit_msg>index now requires src<commit_after>module.exports = require("./src/Closest");
<|endoftext|>


<commit_before>var halCache = {};

module.exports = (server, options) => {
  options = options || {};
  options.prefix = options.prefix || "";
  options.makeObjects = options.makeObjects || false;

  var getHAL = require('./getHAL.js')(server, options);
  var makeAddLink = require('./addLink.js')(server, options);

  // override json formatter
  // add HAL data to body and then invoke normal json formatter
  var _formatJSON = server.formatters["application/json"];
  var halFormatter = (request, response, body, cb) => {
    body._links = request.hal;
    return _formatJSON(request, response, body, cb);
  }
  server.acceptable.push('application/hal+json');
  server.formatters["application/hal+json"] = halFormatter;
  if (options.overrideJSON) {
    server.formatters["application/json"] = halFormatter;
  }

  // fires on every request
  // renders the HAL data and puts it in cache if not present already
  return (request, response, next) => {
    var method = request.method;
    var url = request.url;

    if (!halCache[url]) {
      halCache[url] = getHAL(url);
    }
    // clone the cached object
    // the developer can add custom links, and if we pass this object by reference
    // this will (accidentally) get into the cache
    if (options.makeObjects) {
      Object.assign(request.hal, halCache[url]);
    } else {
      request.hal = halCache[url].slice(0);
    }
    response.addLink = makeAddLink(request.hal, response);
    next();
  }
}
<commit_msg>fixed bug when sending 302 redirect<commit_after>var halCache = {};

module.exports = (server, options) => {
  options = options || {};
  options.prefix = options.prefix || "";
  options.makeObjects = options.makeObjects || false;

  var getHAL = require('./getHAL.js')(server, options);
  var makeAddLink = require('./addLink.js')(server, options);

  // override json formatter
  // add HAL data to body and then invoke normal json formatter
  var _formatJSON = server.formatters["application/json"];
  var halFormatter = (request, response, body, cb) => {
    // a response can have no body. (ex: 302 redirect)
    body && (body._links = request.hal);
    return _formatJSON(request, response, body, cb);
  }
  server.acceptable.push('application/hal+json');
  server.formatters["application/hal+json"] = halFormatter;
  if (options.overrideJSON) {
    server.formatters["application/json"] = halFormatter;
  }

  // fires on every request
  // renders the HAL data and puts it in cache if not present already
  return (request, response, next) => {
    var method = request.method;
    var url = request.url;

    if (!halCache[url]) {
      halCache[url] = getHAL(url);
    }
    // clone the cached object
    // the developer can add custom links, and if we pass this object by reference
    // this will (accidentally) get into the cache
    if (options.makeObjects) {
      Object.assign(request.hal, halCache[url]);
    } else {
      request.hal = halCache[url].slice(0);
    }
    response.addLink = makeAddLink(request.hal, response);
    next();
  }
}
<|endoftext|>


<commit_before>const {Builder, By, until} = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const firefox = require('selenium-webdriver/firefox');
const fs = require('fs')
require('chromedriver');
require('geckodriver');

function encode(file) {
  var stream = fs.readFileSync(file);
  return new Buffer(stream).toString('base64');
}

(async function chromeExample() {
  const options = new chrome.Options()
      .addExtensions(encode('extension.crx'));
  let driver = await new Builder()
      .forBrowser('chrome')
      .setChromeOptions(options)
      .build();
  try {
    // Modify the header and make sure it is done before proceeding.
    await driver.get("http://mod-header.appspot.com/add?Test=1");
    await driver.wait(until.titleIs('Done'), 1000);

    await driver.get('http://www.xhaus.com/headers');
    await driver.wait(until.elementTextContains(driver.findElement(By.tagName('body')), 'Test'), 1000);
  } finally {
    await driver.quit();
  }
})();

(async function firefoxExample() {
  const options = new firefox.Options();
  options.setPreference('xpinstall.signatures.required', false);
  options.addExtensions('extension.xpi');
  let driver = await new Builder()
      .forBrowser('firefox')
      .setFirefoxOptions(options)
      .build();
  try {
    // Modify the header and make sure it is done before proceeding.
    await driver.get("http://mod-header.appspot.com/add?Test=1");
    await driver.wait(until.titleIs('Done'), 1000);

    await driver.get('http://www.xhaus.com/headers');
    await driver.wait(until.elementTextContains(driver.findElement(By.tagName('body')), 'Test'), 1000);
  } finally {
    await driver.quit();
  }
})();
<commit_msg>Fix the extension rename.<commit_after>const {Builder, By, until} = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const firefox = require('selenium-webdriver/firefox');
const fs = require('fs')
require('chromedriver');
require('geckodriver');

function encode(file) {
  var stream = fs.readFileSync(file);
  return new Buffer(stream).toString('base64');
}

(async function chromeExample() {
  const options = new chrome.Options()
      .addExtensions(encode('modheader.crx'));
  let driver = await new Builder()
      .forBrowser('chrome')
      .setChromeOptions(options)
      .build();
  try {
    // Modify the header and make sure it is done before proceeding.
    await driver.get("http://mod-header.appspot.com/add?Test=1");
    await driver.wait(until.titleIs('Done'), 1000);

    await driver.get('http://www.xhaus.com/headers');
    await driver.wait(until.elementTextContains(driver.findElement(By.tagName('body')), 'Test'), 1000);
  } finally {
    await driver.quit();
  }
})();

(async function firefoxExample() {
  const options = new firefox.Options();
  options.setPreference('xpinstall.signatures.required', false);
  options.addExtensions('modheader.xpi');
  let driver = await new Builder()
      .forBrowser('firefox')
      .setFirefoxOptions(options)
      .build();
  try {
    // Modify the header and make sure it is done before proceeding.
    await driver.get("http://mod-header.appspot.com/add?Test=1");
    await driver.wait(until.titleIs('Done'), 1000);

    await driver.get('http://www.xhaus.com/headers');
    await driver.wait(until.elementTextContains(driver.findElement(By.tagName('body')), 'Test'), 1000);
  } finally {
    await driver.quit();
  }
})();
<|endoftext|>


<commit_before>var express = require('express'),
    serveStatic = require('serve-static'),
    fs = require('fs'),
    http = require('http'),
    https = require('https'),
    bodyParser = require('body-parser'),
    ipn = require('paypal-ipn'),
    ParseServer = require('parse-server').ParseServer;

var app = express()
var settings = {
  "appId": process.env.APPLICATION_ID || "APPLICATION_ID",
  "appName": process.env.APPLICATION_NAME || "Yorick",
  "masterKey": process.env.MASTER_KEY || "MASTER_KEY",
  "databaseURI": process.env.MONGODB_URI || "mongodb://localhost:27017/anotherstore",
  "mountPath": process.env.MOUNT_PATH || "/parse/1",
  "cloud": process.env.CLOUD_CODE_MAIN || "/home/ubuntu/workspace/cloud/main.js",
  "verbose": true,
  "publicServerURL": process.env.PUBLIC_SERVER_URL || "https://yorick-latest-parse-server-gnu-lorien.c9users.io/parse/1",
  "serverURL": "http://0.0.0.0:" + process.env.PORT + "/parse/1",
  "auth": {
      "facebook": {
          "appIds": process.env.FACEBOOK_APP_IDS || ""
      }
  }
}

var api = new ParseServer(settings);
app.use('/parse/1', api);

app.get('/deez', function (req, res) {
    new Parse.Query("Vampire").first({useMasterKey: true}).then(function (v) {
        res.send("I got something named what exactly? " + v.get("name"));
    }).fail(function (error) {
        res.send(error.message);
    });
});

app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());

app.post('/deez', function (req, res) {
    console.log(JSON.stringify(req.body));
    ipn.verify(req.body, {allow_sandbox: true}, function (err, msg) {
        if (err) {
            console.error(err);
        } else {
            console.log("I verified it");
            var PaymentPaypal = Parse.Object.extend("PaymentPaypal");
            var p = new PaymentPaypal;
            p.save(req.body, {useMasterKey: true}).then(function (newpaymente) {
                console.log("Boom new payment");
            }).fail(function (error) {
                console.error(error.message);
            })
        }
    })
    res.send("I found something");
})

http.createServer(app).listen(process.env.PORT, '0.0.0.0');<commit_msg>Another attempt to configure Facebook<commit_after>var express = require('express'),
    serveStatic = require('serve-static'),
    fs = require('fs'),
    http = require('http'),
    https = require('https'),
    bodyParser = require('body-parser'),
    ipn = require('paypal-ipn'),
    ParseServer = require('parse-server').ParseServer;

var app = express()
var settings = {
  "appId": process.env.APPLICATION_ID || "APPLICATION_ID",
  "appName": process.env.APPLICATION_NAME || "Yorick",
  "masterKey": process.env.MASTER_KEY || "MASTER_KEY",
  "databaseURI": process.env.MONGODB_URI || "mongodb://localhost:27017/anotherstore",
  "mountPath": process.env.MOUNT_PATH || "/parse/1",
  "cloud": process.env.CLOUD_CODE_MAIN || "/home/ubuntu/workspace/cloud/main.js",
  "verbose": true,
  "publicServerURL": process.env.PUBLIC_SERVER_URL || "https://yorick-latest-parse-server-gnu-lorien.c9users.io/parse/1",
  "serverURL": "http://0.0.0.0:" + process.env.PORT + "/parse/1",
  "oauth": {
      "facebook": {
          "appIds": process.env.FACEBOOK_APP_IDS || ""
      }
  }
}

var api = new ParseServer(settings);
app.use('/parse/1', api);

app.get('/deez', function (req, res) {
    new Parse.Query("Vampire").first({useMasterKey: true}).then(function (v) {
        res.send("I got something named what exactly? " + v.get("name"));
    }).fail(function (error) {
        res.send(error.message);
    });
});

app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());

app.post('/deez', function (req, res) {
    console.log(JSON.stringify(req.body));
    ipn.verify(req.body, {allow_sandbox: true}, function (err, msg) {
        if (err) {
            console.error(err);
        } else {
            console.log("I verified it");
            var PaymentPaypal = Parse.Object.extend("PaymentPaypal");
            var p = new PaymentPaypal;
            p.save(req.body, {useMasterKey: true}).then(function (newpaymente) {
                console.log("Boom new payment");
            }).fail(function (error) {
                console.error(error.message);
            })
        }
    })
    res.send("I found something");
})

http.createServer(app).listen(process.env.PORT, '0.0.0.0');<|endoftext|>