<commit_before>var capturePhoto = (function(app) {
    "use strict";

    app.utils.geolocalize = function() {
        var dfd = $.Deferred();
        if (navigator.geolocation) {
            var callbackSuccess = $.proxy(function(position) {
                this.global.currentPosition = {
                    lat: position.coords.latitude,
                    lon: position.coords.longitude
                };
                this.dfd.resolve();
            }, {dfd: dfd, global: app.global});
            var callbackError = $.proxy(function(position) {
                // Don't break any process if geolocation fail, just rely on default values
                this.dfd.resolve();
            }, {dfd: dfd});
            navigator.geolocation.getCurrentPosition(
                callbackSuccess,
                callbackError,
                {maximumAge: 200000, enableHighAccuracy: false}
            );
        } else {
            // Don't break any process if geolocation fail, just rely on default values
            dfd.resolve();
        }
        return dfd;
    };

    app.utils.FlickrAPI = function(options) {
        return this.initialize(options);
    };

    app.utils.FlickrAPI.prototype = {
        initialize: function(options) {
            this.oauth = OAuth(_.extend({
                requestTokenUrl: 'http://www.flickr.com/services/oauth/request_token',
                authorizationUrl: 'http://www.flickr.com/services/oauth/authorize',
                accessTokenUrl: 'http://www.flickr.com/services/oauth/access_token'
            }, options));
            var storedToken = localStorage.getItem('FlickrAccessToken');
            if (storedToken !== null) {
                var token = JSON.parse(storedToken);
                this.oauth.setAccessToken(token);
            }
            this.oauth.callbackUrl = options.callbackUrl; // Force OAuth to expose callback URL.
            this._dfd = {};
            return this;
        },

        authenticate: function() {
            this._dfd.auth = $.Deferred();
            // Handler for successful user-granted OAuth authorization
            window.plugins.childBrowser.onLocationChange = _.bind(function(url) {
                if (url.indexOf(this.oauth.callbackUrl) === 0) {
                    // First, close ChildBrowser which should be trying to parse our fake callbackUrl
                    window.plugins.childBrowser.close();
                    // Then, parse and use querystring
                    var params = url.substr(url.indexOf('?') + 1),
                        token = this.oauth.parseTokenRequest(params);
                    //this.oauth.setAccessToken([token.oauth_token, token.oauth_token_secret]);
                    this.oauth.setVerifier(token.oauth_verifier);
                    // OAuth step 3: Finally exchange the request token for an ACCESS TOKEN
                    this.oauth.fetchAccessToken(
                        _.bind(function(data) {
                            localStorage.setItem('FlickrAccessToken', JSON.stringify(this.oauth.getAccessToken()));
                            this._dfd.auth.resolve();
                        }, this),
                        _.bind(function(data) {this._dfd.auth.reject(this.buildOAuthErrorMessage(data));}, this)
                    );
                    return false;
                }
                return true;
            }, this);

            // Start OAuth authentication by obtaining a REQUEST TOKEN
            this.oauth.fetchRequestToken(
                function (url) {
                    // OAuth step 2: present authorization page to the user and (try to) obtain a VERIFIER in return
                    window.plugins.childBrowser.showWebPage(url + '&perms=write');
                },
                _.bind(function(data) {this._dfd.auth.reject(this.buildOAuthErrorMessage(data));}, this)
            );

            return this._dfd.auth;
        },

        callAPI: function(method, params, success, fail) {
            if (! params) params = {};
            params.method = method;
            this.oauth.get(
                'http://api.flickr.com/services/rest?nojsoncallback=1&format=json&' + $.param(params),
                _.bind(function(data) {
                    var args = JSON.parse(data.text);
                    if (args.stat === 'ok') {
                        success(args);
                    } else {
                        fail(this.buildFlickrErrorMessage(args));
                    }
                }, this),
                _.bind(function(data) {fail(this.buildOAuthErrorMessage(data));}, this)
            );
        },

        getSizeLimit: function() {
            var dfd = $.Deferred();
            this.callAPI(
                'flickr.people.getUploadStatus',
                {},
                _.bind(function (args) {this.resolve(args.user.bandwidth.remainingkb);}, dfd),
                _.bind(dfd.reject, dfd)
            );
            return dfd;
        },

        sendPicture: function(imageURI, feature) {
            var dfd = $.Deferred();
            // Authenticate if user has no access token
            if (this.oauth.getAccessTokenKey() === '') {
                this.authenticate().then(
                    _.bind(function () {
                        this.self.sendPicture(this.imageURI, this.feature).then(
                            _.bind(this.dfd.resolve, this.dfd),
                            _.bind(this.dfd.reject, this.dfd)
                        );
                    }, {self: this, dfd: dfd, feature: feature, imageURI: imageURI}),
                    _.bind(dfd.reject, dfd)
                );
                return dfd;
            }

            var tags = 'osm:' + feature.fid.toLowerCase().replace(/\./g, '=');
            // Check user account limits (async level 1)
            this.getSizeLimit().then(
                _.bind(function(maxSize) {
                    this.maxSize = maxSize;
                    // Obtain a file descriptor (async level 2)
                    window.resolveLocalFileSystemURI(
                        this.imageURI,
                        _.bind(function (fe) {
                            // Convert to a sort of sub-descriptor... (async level 3)
                            fe.file(
                                _.bind(function (f) {
                                    // Actually compare file descriptor size with allowed size
                                    if (f.size > this.maxSize) {
                                        this.dfd.reject('Upload failed, you\'ve reached your Flickr monthly bandwith limit.');
                                    }
                                    // Finally, send the data
                                    // TODO: take advantage of onprogress + HTML5 progressBar
                                    this.oauth.postPG(
                                        'http://api.flickr.com/services/upload/',
                                        {
                                            uri: this.imageURI,
                                            key: 'photo',
                                            name: f.name,
                                            type: f.type,
                                            params: {is_public: '1', tags: this.tags}
                                        },
                                        _.bind(function (data) {
                                            var resp = $($.parseXML(data.response)).children(),
                                                stat = resp.attr('stat');
                                            if (stat === 'fail') {
                                                var err = resp.find('err');
                                                this.dfd.reject(this.flickrFail({
                                                    code: err.attr('code'),
                                                    message: err.attr('msg')
                                                }));
                                            } else {
                                                this.dfd.resolve('Picture successfully uploaded to Flickr under the id ' + resp.find('photoid').text());
                                            }
                                        }, this),
                                        _.bind(function (err) {this.dfd.reject('An error occured while uploading the picture to Flickr (code: ' + err.code + ').');}, this)
                                    );
                                }, this),
                                _.bind(function (err) {this.dfd.reject('An error occured while reading the picture size from the camera (code: ' + err.code + ').');}, this)
                            );
                        }, this),
                        _.bind(function (err) {this.dfd.reject('An error occured while getting the picture from the camera (code: ' + err.code + ').');}, this)
                    );
                }, {dfd: dfd, imageURI: imageURI, oauth: this.oauth, tags: tags, flickrFail: this.buildFlickrErrorMessage}),
                _.bind(dfd.reject, dfd)
            );
            return dfd;
        },

        buildFlickrErrorMessage: function(args) {
            return 'Error while communicating with Flickr services.\nThe server reports: (' + args.code + ') ' + args.message;
        },

        buildOAuthErrorMessage: function(resp) {
            var args = this.oauth.parseTokenRequest(resp.text),
                msg = 'Error while communicating with Flickr services.\nThe server reports: oauth_problem=' + args.oauth_problem;
            if (args.oauth_problem === 'timestamp_refused') {
                msg += '\nServer time is: ' + resp.responseHeaders.Date;
            }
            return msg;
        }
    };

    return app;
})(capturePhoto);<commit_msg>Factorize more code in the base API call method<commit_after>var capturePhoto = (function(app) {
    "use strict";

    app.utils.geolocalize = function() {
        var dfd = $.Deferred();
        if (navigator.geolocation) {
            var callbackSuccess = $.proxy(function(position) {
                this.global.currentPosition = {
                    lat: position.coords.latitude,
                    lon: position.coords.longitude
                };
                this.dfd.resolve();
            }, {dfd: dfd, global: app.global});
            var callbackError = $.proxy(function(position) {
                // Don't break any process if geolocation fail, just rely on default values
                this.dfd.resolve();
            }, {dfd: dfd});
            navigator.geolocation.getCurrentPosition(
                callbackSuccess,
                callbackError,
                {maximumAge: 200000, enableHighAccuracy: false}
            );
        } else {
            // Don't break any process if geolocation fail, just rely on default values
            dfd.resolve();
        }
        return dfd;
    };

    app.utils.FlickrAPI = function(options) {
        return this.initialize(options);
    };

    app.utils.FlickrAPI.prototype = {
        initialize: function(options) {
            this.oauth = OAuth(_.extend({
                requestTokenUrl: 'http://www.flickr.com/services/oauth/request_token',
                authorizationUrl: 'http://www.flickr.com/services/oauth/authorize',
                accessTokenUrl: 'http://www.flickr.com/services/oauth/access_token'
            }, options));
            var storedToken = localStorage.getItem('FlickrAccessToken');
            if (storedToken !== null) {
                var token = JSON.parse(storedToken);
                this.oauth.setAccessToken(token);
            }
            this.oauth.callbackUrl = options.callbackUrl; // Force OAuth to expose callback URL.
            this._dfd = {};
            return this;
        },

        authenticate: function() {
            this._dfd.auth = $.Deferred();
            // Handler for successful user-granted OAuth authorization
            window.plugins.childBrowser.onLocationChange = _.bind(function(url) {
                if (url.indexOf(this.oauth.callbackUrl) === 0) {
                    // First, close ChildBrowser which should be trying to parse our fake callbackUrl
                    window.plugins.childBrowser.close();
                    // Then, parse and use querystring
                    var params = url.substr(url.indexOf('?') + 1),
                        token = this.oauth.parseTokenRequest(params);
                    //this.oauth.setAccessToken([token.oauth_token, token.oauth_token_secret]);
                    this.oauth.setVerifier(token.oauth_verifier);
                    // OAuth step 3: Finally exchange the request token for an ACCESS TOKEN
                    this.oauth.fetchAccessToken(
                        _.bind(function(data) {
                            localStorage.setItem('FlickrAccessToken', JSON.stringify(this.oauth.getAccessToken()));
                            this._dfd.auth.resolve();
                        }, this),
                        _.bind(function(data) {this._dfd.auth.reject(this.buildOAuthErrorMessage(data));}, this)
                    );
                    return false;
                }
                return true;
            }, this);

            // Start OAuth authentication by obtaining a REQUEST TOKEN
            this.oauth.fetchRequestToken(
                function (url) {
                    // OAuth step 2: present authorization page to the user and (try to) obtain a VERIFIER in return
                    window.plugins.childBrowser.showWebPage(url + '&perms=write');
                },
                _.bind(function(data) {this._dfd.auth.reject(this.buildOAuthErrorMessage(data));}, this)
            );

            return this._dfd.auth;
        },

        callAPI: function(method, params) {
            var dfd = $.Deferred();
            if (! params) params = {};
            params.method = method;
            this.oauth.get(
                'http://api.flickr.com/services/rest?nojsoncallback=1&format=json&' + $.param(params),
                _.bind(function(data) {
                    var args = JSON.parse(data.text);
                    if (args.stat === 'ok') {
                        dfd.resolve(args);
                    } else {
                        dfd.reject(this.buildFlickrErrorMessage(args));
                    }
                }, this),
                _.bind(function(data) {dfd.reject(this.buildOAuthErrorMessage(data));}, this)
            );
            return dfd;
        },

        getSizeLimit: function() {
            return this.callAPI(
                'flickr.people.getUploadStatus',
                {}
            );
        },

        sendPicture: function(imageURI, feature) {
            var dfd = $.Deferred();
            // Authenticate if user has no access token
            if (this.oauth.getAccessTokenKey() === '') {
                this.authenticate().then(
                    _.bind(function () {
                        this.self.sendPicture(this.imageURI, this.feature).then(
                            _.bind(this.dfd.resolve, this.dfd),
                            _.bind(this.dfd.reject, this.dfd)
                        );
                    }, {self: this, dfd: dfd, feature: feature, imageURI: imageURI}),
                    _.bind(dfd.reject, dfd)
                );
                return dfd;
            }

            var tags = 'osm:' + feature.fid.toLowerCase().replace(/\./g, '=');
            // Check user account limits (async level 1)
            this.getSizeLimit().then(
                _.bind(function(args) {
                    this.maxSize = args.user.bandwidth.remainingkb;
                    // Obtain a file descriptor (async level 2)
                    window.resolveLocalFileSystemURI(
                        this.imageURI,
                        _.bind(function (fe) {
                            // Convert to a sort of sub-descriptor... (async level 3)
                            fe.file(
                                _.bind(function (f) {
                                    // Actually compare file descriptor size with allowed size
                                    if (f.size > this.maxSize) {
                                        this.dfd.reject('Upload failed, you\'ve reached your Flickr monthly bandwith limit.');
                                    }
                                    // Finally, send the data
                                    // TODO: take advantage of onprogress + HTML5 progressBar
                                    this.oauth.postPG(
                                        'http://api.flickr.com/services/upload/',
                                        {
                                            uri: this.imageURI,
                                            key: 'photo',
                                            name: f.name,
                                            type: f.type,
                                            params: {is_public: '1', tags: this.tags}
                                        },
                                        _.bind(function (data) {
                                            var resp = $($.parseXML(data.response)).children(),
                                                stat = resp.attr('stat');
                                            if (stat === 'fail') {
                                                var err = resp.find('err');
                                                this.dfd.reject(this.flickrFail({
                                                    code: err.attr('code'),
                                                    message: err.attr('msg')
                                                }));
                                            } else {
                                                this.dfd.resolve('Picture successfully uploaded to Flickr under the id ' + resp.find('photoid').text());
                                            }
                                        }, this),
                                        _.bind(function (err) {this.dfd.reject('An error occured while uploading the picture to Flickr (code: ' + err.code + ').');}, this)
                                    );
                                }, this),
                                _.bind(function (err) {this.dfd.reject('An error occured while reading the picture size from the camera (code: ' + err.code + ').');}, this)
                            );
                        }, this),
                        _.bind(function (err) {this.dfd.reject('An error occured while getting the picture from the camera (code: ' + err.code + ').');}, this)
                    );
                }, {dfd: dfd, imageURI: imageURI, oauth: this.oauth, tags: tags, flickrFail: this.buildFlickrErrorMessage}),
                _.bind(dfd.reject, dfd)
            );
            return dfd;
        },

        buildFlickrErrorMessage: function(args) {
            return 'Error while communicating with Flickr services.\nThe server reports: (' + args.code + ') ' + args.message;
        },

        buildOAuthErrorMessage: function(resp) {
            var args = this.oauth.parseTokenRequest(resp.text),
                msg = 'Error while communicating with Flickr services.\nThe server reports: oauth_problem=' + args.oauth_problem;
            if (args.oauth_problem === 'timestamp_refused') {
                msg += '\nServer time is: ' + resp.responseHeaders.Date;
            }
            return msg;
        }
    };

    return app;
})(capturePhoto);<|endoftext|>


<commit_before>'use strict';

var nodemailer = require('nodemailer');
var sgTransport = require('nodemailer-sendgrid-transport');
var mandrillTransport = require('nodemailer-mandrill-transport');
var mailgunTransport = require('nodemailer-mailgun-transport');
var nunjucks = require('nunjucks');
var debug = require('debug')('formio:settings:email');
var _ = require('lodash');

// Configure nunjucks to not watch any files
nunjucks.configure([], {
  watch: false
});

/**
 * The email sender for emails.
 * @param formio
 * @returns {{send: Function}}
 */
module.exports = function(formio) {
  var hook = require('./hook')(formio);
  return {
    availableTransports: function(req, next) {
      hook.settings(req, function(err, settings) {
        if (err) {
          return next(err);
        }

        var availableTransports = [
          {
            transport: 'default',
            title: 'Default (charges may apply)'
          }
        ];
        if (_.get(settings, 'email.gmail.auth.user')
          && _.get(settings, 'email.gmail.auth.pass')) {
          availableTransports.push(
            {
              transport: 'gmail',
              title: 'G-Mail'
            }
          );
        }
        if (_.get(settings, 'email.sendgrid.auth.api_user') && _.get(settings, 'email.sendgrid.auth.api_key')
          || (!_.get(settings, 'email.sendgrid.auth.api_user') && _.get(settings, 'email.sendgrid.auth.api_key'))
        ) {
          // Omit the username if user has configured sendgrid for api key access.
          if (_.get(settings, 'email.sendgrid.auth.api_user') === 'apikey') {
            settings.email.sendgrid.auth = _.omit(settings.email.sendgrid.auth, 'api_user');
          }
          availableTransports.push(
            {
              transport: 'sendgrid',
              title: 'SendGrid'
            }
          );
        }
        if (_.get(settings, 'email.mandrill.auth.apiKey')) {
          availableTransports.push(
            {
              transport: 'mandrill',
              title: 'Mandrill'
            }
          );
        }
        if (_.get(settings, 'email.mailgun.auth.api_key')) {
          availableTransports.push(
            {
              transport: 'mailgun',
              title: 'Mailgun'
            }
          );
        }

        availableTransports = hook.alter('emailTransports', availableTransports, settings);
        next(null, availableTransports);
      });
    },
    settings: function(transport, from, emails, subject, message) {
      return {
        transport: transport,
        from: from,
        emails: emails,
        subject: subject,
        message: message
      };
    },
    send: function(req, res, message, params, next) {
      // The transporter object.
      var transporter = null;

      // To send the mail.
      var sendMail = function() {
        if (transporter) {
          var emails = (typeof message.emails === 'string') ? message.emails : message.emails.join(', ');
          // Allow disabling of sending emails to external email accounts.
          if (process.env.EMAIL_OVERRIDE) {
            emails = process.env.EMAIL_OVERRIDE;
          }
          transporter.sendMail({
            from: message.from ? message.from : 'no-reply@form.io',
            to: nunjucks.renderString(emails, params),
            subject: nunjucks.renderString(message.subject, params),
            html: nunjucks.renderString(message.message, params)
          });

          debug('We do not want to block the request to send an email.');
          return next();
        }
        else {
          debug('No transport configured, dont send an email.');
          return next();
        }
      };

      // Get the settings.
      hook.settings(req, function(err, settings) {
        if (err) {
          return;
        }

        var emailType = message.transport ? message.transport : 'default';
        var _config = (formio && formio.config && formio.config.email && formio.config.email.type);

        debug(formio.config);
        debug(emailType);
        switch (emailType) {
          case 'default':
            if (_config && formio.config.email.type === 'sendgrid') {
              /* eslint-disable camelcase */
              // Check if the user has configured sendgrid for api key access.
              if (!formio.config.email.username && formio.config.email.password) {
                transporter = nodemailer.createTransport(sgTransport({
                  auth: {
                    api_key: formio.config.email.password
                  }
                }));
              }
              else {
                transporter = nodemailer.createTransport(sgTransport({
                  auth: {
                    api_user: formio.config.email.username,
                    api_key: formio.config.email.password
                  }
                }));
              }
              /* eslint-enable camelcase */
            }
            else if (_config && formio.config.email.type === 'mandrill') {
              transporter = nodemailer.createTransport(mandrillTransport({
                auth: {
                  apiKey: formio.config.email.apiKey
                }
              }));
            }
            sendMail();
            break;
          case 'sendgrid':
            debug(settings.email.sendgrid);
            if (settings.email.sendgrid) {
              // Check if the user has configured sendgrid for api key access.
              if (_.get(settings, 'email.sendgrid.auth.api_user')
                && _.get(settings, 'email.sendgrid.auth.api_user').toString() === 'apikey'
              ) {
                settings.email.sendgrid.auth = _.omit(settings.email.sendgrid.auth, 'api_user');
              }

              debug(settings.email.sendgrid);
              transporter = nodemailer.createTransport(sgTransport(settings.email.sendgrid));
              sendMail();
            }
            break;
          case 'mandrill':
            if (settings.email.mandrill) {
              transporter = nodemailer.createTransport(mandrillTransport(settings.email.mandrill));
              sendMail();
            }
            break;
          case 'mailgun':
            if (settings.email.mailgun) {
              transporter = nodemailer.createTransport(mailgunTransport(settings.email.mailgun));
              sendMail();
            }
            break;
          case 'gmail':
            if (settings.email.gmail) {
              var options = settings.email.gmail;
              options.service = 'Gmail';
              transporter = nodemailer.createTransport(options);
              sendMail();
            }
            break;
          default:
            hook.invoke('email', emailType, message, settings, req, res);
            return next();
        }
      });
    }
  };
};
<commit_msg>Fixed an issue with emails.<commit_after>'use strict';

var nodemailer = require('nodemailer');
var sgTransport = require('nodemailer-sendgrid-transport');
var mandrillTransport = require('nodemailer-mandrill-transport');
var mailgunTransport = require('nodemailer-mailgun-transport');
var nunjucks = require('nunjucks');
var debug = require('debug')('formio:settings:email');
var _ = require('lodash');

// Configure nunjucks to not watch any files
nunjucks.configure([], {
  watch: false
});

/**
 * The email sender for emails.
 * @param formio
 * @returns {{send: Function}}
 */
module.exports = function(formio) {
  var hook = require('./hook')(formio);
  return {
    availableTransports: function(req, next) {
      hook.settings(req, function(err, settings) {
        if (err) {
          return next(err);
        }

        var availableTransports = [
          {
            transport: 'default',
            title: 'Default (charges may apply)'
          }
        ];
        if (_.get(settings, 'email.gmail.auth.user')
          && _.get(settings, 'email.gmail.auth.pass')) {
          availableTransports.push(
            {
              transport: 'gmail',
              title: 'G-Mail'
            }
          );
        }
        if (_.get(settings, 'email.sendgrid.auth.api_user') && _.get(settings, 'email.sendgrid.auth.api_key')
          || (!_.get(settings, 'email.sendgrid.auth.api_user') && _.get(settings, 'email.sendgrid.auth.api_key'))
        ) {
          // Omit the username if user has configured sendgrid for api key access.
          if (_.get(settings, 'email.sendgrid.auth.api_user') === 'apikey') {
            settings.email.sendgrid.auth = _.omit(settings.email.sendgrid.auth, 'api_user');
          }
          availableTransports.push(
            {
              transport: 'sendgrid',
              title: 'SendGrid'
            }
          );
        }
        if (_.get(settings, 'email.mandrill.auth.apiKey')) {
          availableTransports.push(
            {
              transport: 'mandrill',
              title: 'Mandrill'
            }
          );
        }
        if (_.get(settings, 'email.mailgun.auth.api_key')) {
          availableTransports.push(
            {
              transport: 'mailgun',
              title: 'Mailgun'
            }
          );
        }

        availableTransports = hook.alter('emailTransports', availableTransports, settings);
        next(null, availableTransports);
      });
    },
    settings: function(transport, from, emails, subject, message) {
      return {
        transport: transport,
        from: from,
        emails: emails,
        subject: subject,
        message: message
      };
    },
    send: function(req, res, message, params, next) {
      // The transporter object.
      var transporter = null;

      // To send the mail.
      var sendMail = function() {
        if (transporter) {
          var emails = (typeof message.emails === 'string') ? message.emails : message.emails.join(', ');
          // Allow disabling of sending emails to external email accounts.
          if (process.env.EMAIL_OVERRIDE) {
            emails = process.env.EMAIL_OVERRIDE;
          }
          transporter.sendMail({
            from: message.from ? message.from : 'no-reply@form.io',
            to: nunjucks.renderString(emails, params),
            subject: nunjucks.renderString(message.subject, params),
            html: nunjucks.renderString(message.message, params)
          });

          debug('We do not want to block the request to send an email.');
          return next();
        }
        else {
          debug('No transport configured, dont send an email.');
          return next();
        }
      };

      // Get the settings.
      hook.settings(req, function(err, settings) {
        if (err) {
          return;
        }

        var emailType = message.transport ? message.transport : 'default';
        var _config = (formio && formio.config && formio.config.email && formio.config.email.type);

        debug(formio.config);
        debug(emailType);
        switch (emailType) {
          case 'default':
            if (_config && formio.config.email.type === 'sendgrid') {
              /* eslint-disable camelcase */
              // Check if the user has configured sendgrid for api key access.
              if (!formio.config.email.username && formio.config.email.password) {
                transporter = nodemailer.createTransport(sgTransport({
                  auth: {
                    api_key: formio.config.email.password
                  }
                }));
              }
              else {
                transporter = nodemailer.createTransport(sgTransport({
                  auth: {
                    api_user: formio.config.email.username,
                    api_key: formio.config.email.password
                  }
                }));
              }
              /* eslint-enable camelcase */
            }
            else if (_config && formio.config.email.type === 'mandrill') {
              transporter = nodemailer.createTransport(mandrillTransport({
                auth: {
                  apiKey: formio.config.email.apiKey
                }
              }));
            }
            sendMail();
            break;
          case 'sendgrid':
            debug(settings.email.sendgrid);
            if (settings.email.sendgrid) {
              // Check if the user has configured sendgrid for api key access.
              if (_.get(settings, 'email.sendgrid.auth.api_user')
                && _.get(settings, 'email.sendgrid.auth.api_user').toString() === 'apikey'
              ) {
                settings.email.sendgrid.auth = _.omit(settings.email.sendgrid.auth, 'api_user');
              }

              debug(settings.email.sendgrid);
              transporter = nodemailer.createTransport(sgTransport(settings.email.sendgrid));
              sendMail();
            }
            break;
          case 'mandrill':
            if (settings.email.mandrill) {
              transporter = nodemailer.createTransport(mandrillTransport(settings.email.mandrill));
              sendMail();
            }
            break;
          case 'mailgun':
            if (settings.email.mailgun) {
              transporter = nodemailer.createTransport(mailgunTransport(settings.email.mailgun));
              sendMail();
            }
            break;
          case 'gmail':
            if (settings.email.gmail) {
              var options = settings.email.gmail;
              options.service = 'Gmail';
              transporter = nodemailer.createTransport(options);
              sendMail();
            }
            break;
          default:
            hook.invoke('email', emailType, message, settings, req, res, params);
            return next();
        }
      });
    }
  };
};
<|endoftext|>


<commit_before>'use strict';

const	freeport	= require('freeport'),
	request	= require('requestretry'),
	cheerio	= require('cheerio'),
	async	= require('async'),
	test	= require('tape'),
	//App	= require(__dirname + '/../index.js'),
	db	= require('larvitdb');

if ( ! process.env.DBCONFFILE) {
	process.env.DBCONFFILE	= 'config/db_test.json';
}

db.setup(require(__dirname + '/../' + process.env.DBCONFFILE));

test('Basic request', function (t) {
	const	tasks	= [];

	let	port,
		app;

	// Get free port
	tasks.push(function (cb) {
		freeport(function (err, result) {
			port	= result;
			cb(err);
		});
	});

	// Start gui http server
	tasks.push(function (cb) {
		app	= require(__dirname + '/../server.js')({
			'port':	port
		});
		cb();
	});

	// Check if the GUI is up
	tasks.push(function (cb) {
		request('http://127.0.0.1:' + port, function (err, response, body) {
			let	$;

			if (err) return cb(err);

			$	= cheerio.load(body);

			t.equal(response.statusCode,	200);
			t.equal($('#login_form').length,	1);

			cb();
		});
	});

	// Shut down the http server
	tasks.push(function (cb) {
		app.server.close(cb);
	});

	// Shut down the database pool
	tasks.push(function (cb) {
		db.pool.end(cb);
	});

	async.series(tasks, function (err) {
		if (err) throw err;
		t.end();
	});
});<commit_msg>Working tests<commit_after>'use strict';

const	Intercom	= require('larvitamintercom'),
	freeport	= require('freeport'),
	request	= require('requestretry'),
	userLib	= require('larvituser'),
	cheerio	= require('cheerio'),
	async	= require('async'),
	test	= require('tape'),
	//App	= require(__dirname + '/../index.js'),
	db	= require('larvitdb');

let	port,
	app;

userLib.dataWriter.mode	= 'master';
userLib.dataWriter.intercom	= new Intercom('loopback interface');

if ( ! process.env.DBCONFFILE) {
	process.env.DBCONFFILE	= 'config/db_test.json';
}

function startStuff(cb) {
	const	tasks	= [];

	db.setup(require(__dirname + '/../' + process.env.DBCONFFILE));

	// Get free port
	tasks.push(function (cb) {
		freeport(function (err, result) {
			port	= result;
			cb(err);
		});
	});

	// Start gui http server
	tasks.push(function (cb) {
		app	= require(__dirname + '/../server.js')({
			'port':	port
		});
		cb();
	});

	// Create admin user
	tasks.push(function (cb) {
		userLib.create('flump', 'blärk', {'role': 'admin'}, cb);
	});

	async.series(tasks, cb);
}

function stopStuff(cb) {
	const	tasks	= [];

	// Shut down the http server
	tasks.push(function (cb) {
		app.server.close(cb);
	});

	// Clean out the database
	tasks.push(function (cb) {
		db.removeAllTables(cb);
	});

	// Shut down the database pool
	tasks.push(function (cb) {
		db.pool.end(cb);
	});

	async.series(tasks, cb);
}

test('startup', function (t) {
	startStuff(function (err) {
		if (err) throw err;
		t.end();
	});
});

// Check if the GUI is up
//test('Basic request', function (t) {
//	request('http://127.0.0.1:' + port, function (err, response, body) {
//		let	$;
//
//		if (err) throw err;
//
//		$	= cheerio.load(body);
//
//		t.equal(response.statusCode,	200);
//		t.equal($('#login_form').length,	1);
//
//		t.end();
//	});
//});

test('Login', function (t) {
	const	reqOptions	= {},
		jar	= request.jar();

	reqOptions.url	= 'http://127.0.0.1:' + port;
	reqOptions.jar	= jar;
	reqOptions.method	= 'post';
	reqOptions.form	= {};
	reqOptions.form.username	= 'flump';
	reqOptions.form.password	= 'blärk';

	request(reqOptions, function (err, response, body) {
		let	$;

		if (err) throw err;

		$	= cheerio.load(body);

		t.equal(response.statusCode,	200);
		t.equal($('a[href="/logout"]').length,	1);

		// Check so its still up
		request({'url': reqOptions.url, 'jar': jar}, function (err, response, body) {
			let	$;

			if (err) throw err;

			$	= cheerio.load(body);

			t.equal(response.statusCode,	200);
			t.equal($('a[href="/logout"]').length,	1);

			t.end();
		});
	});
});

test('shutdown', function (t) {
	stopStuff(function (err) {
		if (err) throw err;
		t.end();
	});
});<|endoftext|>


<commit_before>/* jshint -W084 */
/*
 * Merge a number of objects into one.
 *
 * Usage example:
 *  var obj1 = {
 *      a: 'a'
 *    },
 *    obj2 = {
 *      b: 'b'
 *    },
 *    obj3 = {
 *      c: 'c'
 *    },
 *    mergedObj = Util.merge(obj1, obj2, obj3);
 *
 * mergedObj is: {
 *   a: 'a',
 *   b: 'b',
 *   c: 'c'
 * }
 *
 */
var merge = (function() {
  function processProperty (key, dest, src) {
    if (src.hasOwnProperty(key)) {
      dest[key] = src[key];
    }
  }

  return function() {
    var objects = Array.prototype.slice.call(arguments),
      obj,
      key,
      result = {},
      i, l = objects.length;

    for (i = 0; i < l; i++) {
      obj = objects[i];
      if (obj) {
        for (key in obj) {
          processProperty(key, result, obj);
        }
      }
    }

    return result;
  };
}());

module.exports = merge;
<commit_msg>Destructive merge<commit_after>/* jshint -W084 */
/*
 * Merge a number of objects into one.
 *
 * Usage example:
 *  var obj1 = {
 *      a: 'a'
 *    },
 *    obj2 = {
 *      b: 'b'
 *    },
 *    obj3 = {
 *      c: 'c'
 *    },
 *    mergedObj = Util.merge(obj1, obj2, obj3);
 *
 * mergedObj is: {
 *   a: 'a',
 *   b: 'b',
 *   c: 'c'
 * }
 *
 */
var merge = (function() {
  function processProperty (key, dest, src) {
    if (src.hasOwnProperty(key)) {
      dest[key] = src[key];
    }
  }

  return function() {
    var objects = Array.prototype.slice.call(arguments),
      obj,
      key,
      result = objects.shift(),
      i, l = objects.length;

    for (i = 0; i < l; i++) {
      obj = objects[i];
      if (obj) {
        for (key in obj) {
          processProperty(key, result, obj);
        }
      }
    }

    return result;
  };
}());

module.exports = merge;
<|endoftext|>


<commit_before>const fs = require('fs');
const util = require('util');
const toml = require('toml');
const backoff = require('backoff');
const Twitter = require('twitter');
const sqlite3 = require('sqlite3');

const streamBackoff = backoff.exponential({
  maxDelay: 60 * 1000
});
var dbCon = null;
streamBackoff.failAfter(15);

function makeError(err) {
  return new Error(util.format('twitter error %d: %d', err.code, err.message));
}

function streamTweets(db, client) {
  client.stream('user', {}, (stream) => {
    console.log('streaming for user');
    stream.on('data', function(event) {
      streamBackoff.reset();
      var keys = Object.keys(event);
      if (keys.length == 1) {
        console.log('received object type %s', keys[0]);
        if (event.delete) {
          var sid = event.delete.status.status_id;
          console.log('deleting tweet %s', sid);
          db.run('DELETE FROM tweets WHERE tweet_id = ?', [sid], (err) => {
            if (err) console.error('error deleting tweet: %s', err);
          })
        }
      } else if (event.event) {
        console.log('received event type %s', event.event);
      } else {
        console.log('received tweet ‘%s’', event.text);
        var date = new Date(event.created_at);
        db.run('INSERT INTO tweets (tweet_id, user, timestamp, body) VALUES (?, ?, ?, ?)', [
          event.tweet_id, event.user.screen_name, date.toISOString(),
          JSON.stringify(event)
        ], (err) => {
          if (err) {
            console.error('error saving tweet: %s', err);
          }
        })
      }
    });
    // stream.on('error', function(err) {
    //   console.error(err);
    //   streamBackoff.backoff();
    // });
    stream.on('end', function() {
      console.log('stream disconnected, reconnecting');
      streamBackoff.backoff();
    });
  })
}

function openDatabase(callback) {
  var db = new sqlite3.Database('tweets.db', function(err) {
    if (err) return callback(err);
    db.run('CREATE TABLE IF NOT EXISTS tweets (tweet_id INTEGER PRIMARY KEY, user VARCHAR, timestamp VARCHAR, body VARCHAR)', (err) => {
      callback(err, db);
    })
  })
}

streamBackoff.on('backoff', function(number, delay) {
  console.log('will attempt to reconnect in %d ms', delay);
});
streamBackoff.on('fail', function() {
  console.error('failed to reconnect, exiting');
  if (dbCon != null) {
    dbCon.close();
  }
  process.exit(3);
})

fs.readFile('config.toml', (err, data) => {
  if (err) throw err;
  var str = data.toString('utf-8');
  var config = toml.parse(str);
  var client = new Twitter({
    consumer_key: config.app.key,
    consumer_secret: config.app.secret,
    access_token_key: config.user.token,
    access_token_secret: config.user.secret
  });
  client.get('account/verify_credentials', {}, function(err, user) {
    if (err) throw makeError(err);
    console.log('logged in as %s', user.screen_name);
    openDatabase((err, db) => {
      if (err) throw err;
      dbCon = db;
      streamTweets(db, client);
      streamBackoff.on('ready', function() {
        streamTweets(db, client);
      });
    })
  })
})
<commit_msg>fix deletes<commit_after>const fs = require('fs');
const util = require('util');
const toml = require('toml');
const backoff = require('backoff');
const Twitter = require('twitter');
const sqlite3 = require('sqlite3');

const streamBackoff = backoff.exponential({
  maxDelay: 60 * 1000
});
var dbCon = null;
streamBackoff.failAfter(15);

function makeError(err) {
  return new Error(util.format('twitter error %d: %d', err.code, err.message));
}

function streamTweets(db, client) {
  client.stream('user', {}, (stream) => {
    console.log('streaming for user');
    stream.on('data', function(event) {
      streamBackoff.reset();
      var keys = Object.keys(event);
      if (keys.length == 1) {
        console.log('received object type %s', keys[0]);
        if (event.delete) {
          var sid = event.delete.status.id_str;
          console.log('deleting tweet %s', sid);
          db.run('DELETE FROM tweets WHERE tweet_id = ?', [sid], (err) => {
            if (err) console.error('error deleting tweet: %s', err);
          })
        }
      } else if (event.event) {
        console.log('received event type %s', event.event);
      } else {
        console.log('received tweet ‘%s’', event.text);
        var date = new Date(event.created_at);
        db.run('INSERT INTO tweets (tweet_id, user, timestamp, body) VALUES (?, ?, ?, ?)', [
          event.tweet_id, event.user.screen_name, date.toISOString(),
          JSON.stringify(event)
        ], (err) => {
          if (err) {
            console.error('error saving tweet: %s', err);
          }
        })
      }
    });
    // stream.on('error', function(err) {
    //   console.error(err);
    //   streamBackoff.backoff();
    // });
    stream.on('end', function() {
      console.log('stream disconnected, reconnecting');
      streamBackoff.backoff();
    });
  })
}

function openDatabase(callback) {
  var db = new sqlite3.Database('tweets.db', function(err) {
    if (err) return callback(err);
    db.run('CREATE TABLE IF NOT EXISTS tweets (tweet_id INTEGER PRIMARY KEY, user VARCHAR, timestamp VARCHAR, body VARCHAR)', (err) => {
      callback(err, db);
    })
  })
}

streamBackoff.on('backoff', function(number, delay) {
  console.log('will attempt to reconnect in %d ms', delay);
});
streamBackoff.on('fail', function() {
  console.error('failed to reconnect, exiting');
  if (dbCon != null) {
    dbCon.close();
  }
  process.exit(3);
})

fs.readFile('config.toml', (err, data) => {
  if (err) throw err;
  var str = data.toString('utf-8');
  var config = toml.parse(str);
  var client = new Twitter({
    consumer_key: config.app.key,
    consumer_secret: config.app.secret,
    access_token_key: config.user.token,
    access_token_secret: config.user.secret
  });
  client.get('account/verify_credentials', {}, function(err, user) {
    if (err) throw makeError(err);
    console.log('logged in as %s', user.screen_name);
    openDatabase((err, db) => {
      if (err) throw err;
      dbCon = db;
      streamTweets(db, client);
      streamBackoff.on('ready', function() {
        streamTweets(db, client);
      });
    })
  })
})
<|endoftext|>


<commit_before>// @flow
import logger from '../logger'
import * as I from 'immutable'
import * as FsGen from '../actions/fs-gen'
import * as Constants from '../constants/fs'
import * as Flow from '../util/flow'
import * as Types from '../constants/types/fs'

const initialState = Constants.makeState()

const coalesceFolderUpdate = (
  original: Types.FolderPathItem,
  updated: Types.FolderPathItem
): Types.FolderPathItem =>
  // We don't want to override a loaded folder into pending, because otherwise
  // next time user goes into that folder we'd show placeholders. We also don't
  // want to simply use the original PathItem, since it's possible some
  // metadata has updated. So use the new item, but reuse children and
  // progress.
  original.progress === 'loaded' && updated.progress === 'pending'
    ? updated.withMutations(u => u.set('children', original.children).set('progress', 'loaded'))
    : updated

const withFsErrorBar = (state: Types.State, action: FsGen.FsErrorPayload): Types.State => {
  const fsError = action.payload.error
  logger.error('error (fs)', fsError.erroredAction.type, fsError.error)
  return state.update('errors', errors => errors.set(Constants.makeUUID(), fsError))
}

const reduceFsError = (state: Types.State, action: FsGen.FsErrorPayload): Types.State => {
  const fsError = action.payload.error
  const {erroredAction} = fsError
  switch (erroredAction.type) {
    case FsGen.commitEdit:
      return withFsErrorBar(state, action).update('edits', edits =>
        edits.update(erroredAction.payload.editID, edit => edit.set('status', 'failed'))
      )
    case FsGen.upload:
      // Don't show error bar in this case, as the uploading row already shows
      // a "retry" button.
      return state.update('uploads', uploads =>
        uploads.update('errors', errors =>
          errors.set(
            Constants.getUploadedPath(erroredAction.payload.parentPath, erroredAction.payload.localPath),

            fsError
          )
        )
      )
    case FsGen.saveMedia:
    case FsGen.shareNative:
    case FsGen.download:
      const download = state.downloads.get(erroredAction.payload.key)
      if (!download || download.state.canceled) {
        // Ignore errors for canceled downloads.
        return state
      }
      return withFsErrorBar(state, action).update('downloads', downloads =>
        downloads.update(
          erroredAction.payload.key,
          download => download && download.update('state', original => original.set('error', fsError))
        )
      )
    default:
      return withFsErrorBar(state, action)
  }
}

export default function(state: Types.State = initialState, action: FsGen.Actions): Types.State {
  switch (action.type) {
    case FsGen.resetStore:
      return initialState
    case FsGen.pathItemLoaded:
      return state.updateIn(['pathItems', action.payload.path], (original: Types.PathItem) => {
        const {meta} = action.payload

        // Since updateIn passes `original` in as `any`, it may not be an
        // actual PathItem.
        if (!original) {
          return meta
        }

        if (original.type === 'folder' && meta.type === 'folder') {
          return coalesceFolderUpdate(original, meta)
        } else if (original.type !== 'file' || meta.type !== 'file') {
          return meta
        }

        return Constants.shouldUseOldMimeType(original, meta) ? meta.set('mimeType', original.mimeType) : meta
      })
    case FsGen.folderListLoaded: {
      let toRemove = new Set()
      const toMerge = action.payload.pathItems.map((item, path) => {
        const original = state.pathItems.get(path, Constants.unknownPathItem)

        if (original.type === 'file' && item.type === 'file') {
          return Constants.shouldUseOldMimeType(original, item)
            ? item.set('mimeType', original.mimeType)
            : item
        }

        if (item.type !== 'folder') return item
        if (original.type !== 'folder') return item

        // Make flow happy by referencing them with a new name that's
        // explicitly typed.
        const originalFolder: Types.FolderPathItem = original
        let newItem: Types.FolderPathItem = item

        newItem = coalesceFolderUpdate(originalFolder, newItem)

        originalFolder.children.forEach(
          name => !newItem.children.includes(name) && toRemove.add(Types.pathConcat(path, name))
        )

        return newItem
      })
      return state.set(
        'pathItems',
        state.pathItems.withMutations(pathItems => pathItems.deleteAll(toRemove).merge(toMerge))
      )
    }
    case FsGen.loadingPath:
      return state.updateIn(['loadingPaths', action.payload.path], set =>
        action.payload.done ? set && set.delete(action.payload.id) : (set || I.Set()).add(action.payload.id)
      )
    case FsGen.favoritesLoaded:
      return state.set(
        'tlfs',
        Constants.makeTlfs({
          private: action.payload.private,
          public: action.payload.public,
          team: action.payload.team,
        })
      )
    case FsGen.sortSetting:
      const {path, sortSetting} = action.payload
      return state.setIn(['pathUserSettings', path, 'sort'], sortSetting)
    case FsGen.downloadStarted: {
      const {key, path, localPath, intent, opID} = action.payload
      const entryType = action.payload.entryType || state.pathItems.get(path, Constants.unknownPathItem).type
      return state.setIn(
        ['downloads', key],
        Constants.makeDownload({
          meta: Constants.makeDownloadMeta({
            entryType,
            intent,
            localPath,
            opID,
            path,
          }),
          state: Constants.makeDownloadState({
            completePortion: 0,
            isDone: false,
            startedAt: Date.now(),
          }),
        })
      )
    }
    case FsGen.downloadProgress: {
      const {key, completePortion, endEstimate} = action.payload
      return state.update('downloads', d =>
        d.update(key, k =>
          k.update('state', original => original && original.merge({completePortion, endEstimate}))
        )
      )
    }
    case FsGen.downloadSuccess: {
      return state.updateIn(
        ['downloads', action.payload.key, 'state'],
        original => original && original.set('isDone', true)
      )
    }
    case FsGen.dismissDownload: {
      return state.removeIn(['downloads', action.payload.key])
    }
    case FsGen.cancelDownload:
      return state.update('downloads', downloads =>
        downloads.update(action.payload.key, download =>
          download.update('state', state => state.set('canceled', true))
        )
      )
    case FsGen.uploadStarted:
      return state.updateIn(['uploads', 'writingToJournal'], writingToJournal =>
        writingToJournal.add(action.payload.path)
      )
    case FsGen.uploadWritingSuccess: {
      const {path} = action.payload
      return state.withMutations(s =>
        s
          .removeIn(['uploads', 'errors', path])
          .updateIn(['uploads', 'writingToJournal'], writingToJournal => writingToJournal.remove(path))
      )
    }
    case FsGen.journalUpdate: {
      const {syncingPaths, totalSyncingBytes, endEstimate} = action.payload
      return state.withMutations(s => {
        s.setIn(['uploads', 'syncingPaths'], I.Set(syncingPaths))
        s.setIn(['uploads', 'totalSyncingBytes'], totalSyncingBytes)
        if (endEstimate) {
          s.setIn(['uploads', 'endEstimate'], endEstimate)
        } else {
          s.deleteIn(['uploads', 'endEstimate'])
        }
      })
    }
    case FsGen.localHTTPServerInfo:
      return state.set('localHTTPServerInfo', Constants.makeLocalHTTPServer(action.payload))
    case FsGen.favoriteIgnore: // fallthrough
    case FsGen.favoriteIgnoreError:
      const elems = Types.getPathElements(action.payload.path)
      const visibility = Types.getVisibilityFromElems(elems)
      if (!visibility) {
        return state
      }
      return state.mergeIn(['tlfs', visibility, elems[2]], {
        isIgnored: action.type === FsGen.favoriteIgnore,
      })
    case FsGen.mimeTypeLoaded:
      return state.update('pathItems', pis =>
        pis.update(action.payload.path, pathItem =>
          pathItem
            ? pathItem.type === 'file'
              ? pathItem.set('mimeType', action.payload.mimeType)
              : pathItem
            : Constants.makeFile({
                mimeType: action.payload.mimeType,
                name: Types.getPathName(action.payload.path),
              })
        )
      )
    case FsGen.newFolderRow:
      const {parentPath} = action.payload
      const parentPathItem = state.pathItems.get(parentPath, Constants.unknownPathItem)
      if (parentPathItem.type !== 'folder') {
        console.warn(`bad parentPath: ${parentPathItem.type}`)
        return state
      }

      const existingNewFolderNames = new Set(
        state.edits
          .filter(edit => edit.parentPath === parentPath)
          .map(edit => edit.name)
          .toSet()
      )

      let newFolderName = 'New Folder'
      for (
        let i = 2;
        parentPathItem.children.has(newFolderName) || existingNewFolderNames.has(newFolderName);
        ++i
      ) {
        newFolderName = `New Folder ${i}`
      }

      return state.mergeIn(
        ['edits'],
        [
          [
            Constants.makeEditID(),
            Constants.makeNewFolder({
              hint: newFolderName,
              name: newFolderName,
              parentPath,
            }),
          ],
        ]
      )
    case FsGen.newFolderName:
      // $FlowFixMe
      return state.updateIn(
        ['edits', action.payload.editID],
        editItem => editItem && editItem.set('name', action.payload.name)
      )
    case FsGen.commitEdit:
      // $FlowFixMe
      return state.setIn(['edits', action.payload.editID, 'status'], 'saving')
    case FsGen.discardEdit:
      // $FlowFixMe
      return state.removeIn(['edits', action.payload.editID])
    case FsGen.fsError:
      return reduceFsError(state, action)
    case FsGen.userFileEditsLoaded:
      return state.set('tlfUpdates', action.payload.tlfUpdates)
    case FsGen.dismissFsError:
      return state.removeIn(['errors', action.payload.key])
    case FsGen.showMoveOrCopy:
      return state.update('moveOrCopy', mc =>
        mc.set('destinationParentPath', I.List([action.payload.initialDestinationParentPath]))
      )
    case FsGen.setMoveOrCopySource:
      return state.update('moveOrCopy', mc => mc.set('sourceItemPath', action.payload.path))
    case FsGen.setMoveOrCopyDestinationParentPath:
      return state.update('moveOrCopy', mc =>
        mc.update('destinationParentPath', list => list.set(action.payload.index, action.payload.path))
      )
    case FsGen.showSendLinkToChat:
      return state.set('sendLinkToChat', Constants.makeSendLinkToChat({path: action.payload.path}))
    case FsGen.setSendLinkToChatConvID:
      // $FlowIssue
      return state.setIn(['sendLinkToChat', 'convID'], action.payload.convID)
    case FsGen.setSendLinkToChatChannels:
      // $FlowIssue
      return state.setIn(['sendLinkToChat', 'channels'], action.payload.channels)
    case FsGen.setPathItemActionMenuView:
      return state.update('pathItemActionMenu', pathItemActionMenu =>
        pathItemActionMenu.set('previousView', pathItemActionMenu.view).set('view', action.payload.view)
      )
    case FsGen.setPathItemActionMenuDownloadKey:
      return state.update('pathItemActionMenu', pathItemActionMenu =>
        pathItemActionMenu.set('downloadKey', action.payload.key)
      )
    case FsGen.kbfsDaemonConnected:
      return state.set('kbfsDaemonConnected', true)
    case FsGen.kbfsDaemonDisconnected:
      return state.set('kbfsDaemonConnected', false)
    case FsGen.setDriverStatus:
      return state.update('sfmi', sfmi => sfmi.set('driverStatus', action.payload.driverStatus))
    case FsGen.showSystemFileManagerIntegrationBanner:
      return state.update('sfmi', sfmi => sfmi.set('showingBanner', true))
    case FsGen.hideSystemFileManagerIntegrationBanner:
      return state.update('sfmi', sfmi => sfmi.set('showingBanner', false))
    case FsGen.driverEnable:
      return state.update('sfmi', sfmi =>
        sfmi.update('driverStatus', driverStatus =>
          driverStatus.type === 'disabled' ? driverStatus.set('isEnabling', true) : driverStatus
        )
      )
    case FsGen.driverKextPermissionError:
      return state.update('sfmi', sfmi =>
        sfmi.update('driverStatus', driverStatus =>
          driverStatus.type === 'disabled'
            ? driverStatus.set('kextPermissionError', true).set('isEnabling', false)
            : driverStatus
        )
      )
    case FsGen.driverDisable:
      return state.update('sfmi', sfmi =>
        sfmi.update('driverStatus', driverStatus =>
          driverStatus.type === 'enabled' ? driverStatus.set('isDisabling', false) : driverStatus
        )
      )
    case FsGen.folderListLoad:
    case FsGen.placeholderAction:
    case FsGen.pathItemLoad:
    case FsGen.download:
    case FsGen.favoritesLoad:
    case FsGen.uninstallKBFSConfirm:
    case FsGen.notifySyncActivity:
    case FsGen.notifyTlfUpdate:
    case FsGen.openSecurityPreferences:
    case FsGen.refreshLocalHTTPServerInfo:
    case FsGen.shareNative:
    case FsGen.saveMedia:
    case FsGen.mimeTypeLoad:
    case FsGen.openPathItem:
    case FsGen.openPathInFilesTab:
    case FsGen.openPathInSystemFileManager:
    case FsGen.openLocalPathInSystemFileManager:
    case FsGen.editSuccess:
    case FsGen.letResetUserBackIn:
    case FsGen.openAndUpload:
    case FsGen.pickAndUpload:
    case FsGen.upload:
    case FsGen.openFilesFromWidget:
    case FsGen.userFileEditsLoad:
    case FsGen.deleteFile:
    case FsGen.move:
    case FsGen.copy:
    case FsGen.moveOrCopyOpen:
    case FsGen.closeMoveOrCopy:
    case FsGen.clearRefreshTag:
    case FsGen.loadPathMetadata:
    case FsGen.refreshDriverStatus:
      return state
    default:
      Flow.ifFlowComplainsAboutThisFunctionYouHaventHandledAllCasesInASwitch(action)
      return state
  }
}
<commit_msg>fix disabling state<commit_after>// @flow
import logger from '../logger'
import * as I from 'immutable'
import * as FsGen from '../actions/fs-gen'
import * as Constants from '../constants/fs'
import * as Flow from '../util/flow'
import * as Types from '../constants/types/fs'

const initialState = Constants.makeState()

const coalesceFolderUpdate = (
  original: Types.FolderPathItem,
  updated: Types.FolderPathItem
): Types.FolderPathItem =>
  // We don't want to override a loaded folder into pending, because otherwise
  // next time user goes into that folder we'd show placeholders. We also don't
  // want to simply use the original PathItem, since it's possible some
  // metadata has updated. So use the new item, but reuse children and
  // progress.
  original.progress === 'loaded' && updated.progress === 'pending'
    ? updated.withMutations(u => u.set('children', original.children).set('progress', 'loaded'))
    : updated

const withFsErrorBar = (state: Types.State, action: FsGen.FsErrorPayload): Types.State => {
  const fsError = action.payload.error
  logger.error('error (fs)', fsError.erroredAction.type, fsError.error)
  return state.update('errors', errors => errors.set(Constants.makeUUID(), fsError))
}

const reduceFsError = (state: Types.State, action: FsGen.FsErrorPayload): Types.State => {
  const fsError = action.payload.error
  const {erroredAction} = fsError
  switch (erroredAction.type) {
    case FsGen.commitEdit:
      return withFsErrorBar(state, action).update('edits', edits =>
        edits.update(erroredAction.payload.editID, edit => edit.set('status', 'failed'))
      )
    case FsGen.upload:
      // Don't show error bar in this case, as the uploading row already shows
      // a "retry" button.
      return state.update('uploads', uploads =>
        uploads.update('errors', errors =>
          errors.set(
            Constants.getUploadedPath(erroredAction.payload.parentPath, erroredAction.payload.localPath),

            fsError
          )
        )
      )
    case FsGen.saveMedia:
    case FsGen.shareNative:
    case FsGen.download:
      const download = state.downloads.get(erroredAction.payload.key)
      if (!download || download.state.canceled) {
        // Ignore errors for canceled downloads.
        return state
      }
      return withFsErrorBar(state, action).update('downloads', downloads =>
        downloads.update(
          erroredAction.payload.key,
          download => download && download.update('state', original => original.set('error', fsError))
        )
      )
    default:
      return withFsErrorBar(state, action)
  }
}

export default function(state: Types.State = initialState, action: FsGen.Actions): Types.State {
  switch (action.type) {
    case FsGen.resetStore:
      return initialState
    case FsGen.pathItemLoaded:
      return state.updateIn(['pathItems', action.payload.path], (original: Types.PathItem) => {
        const {meta} = action.payload

        // Since updateIn passes `original` in as `any`, it may not be an
        // actual PathItem.
        if (!original) {
          return meta
        }

        if (original.type === 'folder' && meta.type === 'folder') {
          return coalesceFolderUpdate(original, meta)
        } else if (original.type !== 'file' || meta.type !== 'file') {
          return meta
        }

        return Constants.shouldUseOldMimeType(original, meta) ? meta.set('mimeType', original.mimeType) : meta
      })
    case FsGen.folderListLoaded: {
      let toRemove = new Set()
      const toMerge = action.payload.pathItems.map((item, path) => {
        const original = state.pathItems.get(path, Constants.unknownPathItem)

        if (original.type === 'file' && item.type === 'file') {
          return Constants.shouldUseOldMimeType(original, item)
            ? item.set('mimeType', original.mimeType)
            : item
        }

        if (item.type !== 'folder') return item
        if (original.type !== 'folder') return item

        // Make flow happy by referencing them with a new name that's
        // explicitly typed.
        const originalFolder: Types.FolderPathItem = original
        let newItem: Types.FolderPathItem = item

        newItem = coalesceFolderUpdate(originalFolder, newItem)

        originalFolder.children.forEach(
          name => !newItem.children.includes(name) && toRemove.add(Types.pathConcat(path, name))
        )

        return newItem
      })
      return state.set(
        'pathItems',
        state.pathItems.withMutations(pathItems => pathItems.deleteAll(toRemove).merge(toMerge))
      )
    }
    case FsGen.loadingPath:
      return state.updateIn(['loadingPaths', action.payload.path], set =>
        action.payload.done ? set && set.delete(action.payload.id) : (set || I.Set()).add(action.payload.id)
      )
    case FsGen.favoritesLoaded:
      return state.set(
        'tlfs',
        Constants.makeTlfs({
          private: action.payload.private,
          public: action.payload.public,
          team: action.payload.team,
        })
      )
    case FsGen.sortSetting:
      const {path, sortSetting} = action.payload
      return state.setIn(['pathUserSettings', path, 'sort'], sortSetting)
    case FsGen.downloadStarted: {
      const {key, path, localPath, intent, opID} = action.payload
      const entryType = action.payload.entryType || state.pathItems.get(path, Constants.unknownPathItem).type
      return state.setIn(
        ['downloads', key],
        Constants.makeDownload({
          meta: Constants.makeDownloadMeta({
            entryType,
            intent,
            localPath,
            opID,
            path,
          }),
          state: Constants.makeDownloadState({
            completePortion: 0,
            isDone: false,
            startedAt: Date.now(),
          }),
        })
      )
    }
    case FsGen.downloadProgress: {
      const {key, completePortion, endEstimate} = action.payload
      return state.update('downloads', d =>
        d.update(key, k =>
          k.update('state', original => original && original.merge({completePortion, endEstimate}))
        )
      )
    }
    case FsGen.downloadSuccess: {
      return state.updateIn(
        ['downloads', action.payload.key, 'state'],
        original => original && original.set('isDone', true)
      )
    }
    case FsGen.dismissDownload: {
      return state.removeIn(['downloads', action.payload.key])
    }
    case FsGen.cancelDownload:
      return state.update('downloads', downloads =>
        downloads.update(action.payload.key, download =>
          download.update('state', state => state.set('canceled', true))
        )
      )
    case FsGen.uploadStarted:
      return state.updateIn(['uploads', 'writingToJournal'], writingToJournal =>
        writingToJournal.add(action.payload.path)
      )
    case FsGen.uploadWritingSuccess: {
      const {path} = action.payload
      return state.withMutations(s =>
        s
          .removeIn(['uploads', 'errors', path])
          .updateIn(['uploads', 'writingToJournal'], writingToJournal => writingToJournal.remove(path))
      )
    }
    case FsGen.journalUpdate: {
      const {syncingPaths, totalSyncingBytes, endEstimate} = action.payload
      return state.withMutations(s => {
        s.setIn(['uploads', 'syncingPaths'], I.Set(syncingPaths))
        s.setIn(['uploads', 'totalSyncingBytes'], totalSyncingBytes)
        if (endEstimate) {
          s.setIn(['uploads', 'endEstimate'], endEstimate)
        } else {
          s.deleteIn(['uploads', 'endEstimate'])
        }
      })
    }
    case FsGen.localHTTPServerInfo:
      return state.set('localHTTPServerInfo', Constants.makeLocalHTTPServer(action.payload))
    case FsGen.favoriteIgnore: // fallthrough
    case FsGen.favoriteIgnoreError:
      const elems = Types.getPathElements(action.payload.path)
      const visibility = Types.getVisibilityFromElems(elems)
      if (!visibility) {
        return state
      }
      return state.mergeIn(['tlfs', visibility, elems[2]], {
        isIgnored: action.type === FsGen.favoriteIgnore,
      })
    case FsGen.mimeTypeLoaded:
      return state.update('pathItems', pis =>
        pis.update(action.payload.path, pathItem =>
          pathItem
            ? pathItem.type === 'file'
              ? pathItem.set('mimeType', action.payload.mimeType)
              : pathItem
            : Constants.makeFile({
                mimeType: action.payload.mimeType,
                name: Types.getPathName(action.payload.path),
              })
        )
      )
    case FsGen.newFolderRow:
      const {parentPath} = action.payload
      const parentPathItem = state.pathItems.get(parentPath, Constants.unknownPathItem)
      if (parentPathItem.type !== 'folder') {
        console.warn(`bad parentPath: ${parentPathItem.type}`)
        return state
      }

      const existingNewFolderNames = new Set(
        state.edits
          .filter(edit => edit.parentPath === parentPath)
          .map(edit => edit.name)
          .toSet()
      )

      let newFolderName = 'New Folder'
      for (
        let i = 2;
        parentPathItem.children.has(newFolderName) || existingNewFolderNames.has(newFolderName);
        ++i
      ) {
        newFolderName = `New Folder ${i}`
      }

      return state.mergeIn(
        ['edits'],
        [
          [
            Constants.makeEditID(),
            Constants.makeNewFolder({
              hint: newFolderName,
              name: newFolderName,
              parentPath,
            }),
          ],
        ]
      )
    case FsGen.newFolderName:
      // $FlowFixMe
      return state.updateIn(
        ['edits', action.payload.editID],
        editItem => editItem && editItem.set('name', action.payload.name)
      )
    case FsGen.commitEdit:
      // $FlowFixMe
      return state.setIn(['edits', action.payload.editID, 'status'], 'saving')
    case FsGen.discardEdit:
      // $FlowFixMe
      return state.removeIn(['edits', action.payload.editID])
    case FsGen.fsError:
      return reduceFsError(state, action)
    case FsGen.userFileEditsLoaded:
      return state.set('tlfUpdates', action.payload.tlfUpdates)
    case FsGen.dismissFsError:
      return state.removeIn(['errors', action.payload.key])
    case FsGen.showMoveOrCopy:
      return state.update('moveOrCopy', mc =>
        mc.set('destinationParentPath', I.List([action.payload.initialDestinationParentPath]))
      )
    case FsGen.setMoveOrCopySource:
      return state.update('moveOrCopy', mc => mc.set('sourceItemPath', action.payload.path))
    case FsGen.setMoveOrCopyDestinationParentPath:
      return state.update('moveOrCopy', mc =>
        mc.update('destinationParentPath', list => list.set(action.payload.index, action.payload.path))
      )
    case FsGen.showSendLinkToChat:
      return state.set('sendLinkToChat', Constants.makeSendLinkToChat({path: action.payload.path}))
    case FsGen.setSendLinkToChatConvID:
      // $FlowIssue
      return state.setIn(['sendLinkToChat', 'convID'], action.payload.convID)
    case FsGen.setSendLinkToChatChannels:
      // $FlowIssue
      return state.setIn(['sendLinkToChat', 'channels'], action.payload.channels)
    case FsGen.setPathItemActionMenuView:
      return state.update('pathItemActionMenu', pathItemActionMenu =>
        pathItemActionMenu.set('previousView', pathItemActionMenu.view).set('view', action.payload.view)
      )
    case FsGen.setPathItemActionMenuDownloadKey:
      return state.update('pathItemActionMenu', pathItemActionMenu =>
        pathItemActionMenu.set('downloadKey', action.payload.key)
      )
    case FsGen.kbfsDaemonConnected:
      return state.set('kbfsDaemonConnected', true)
    case FsGen.kbfsDaemonDisconnected:
      return state.set('kbfsDaemonConnected', false)
    case FsGen.setDriverStatus:
      return state.update('sfmi', sfmi => sfmi.set('driverStatus', action.payload.driverStatus))
    case FsGen.showSystemFileManagerIntegrationBanner:
      return state.update('sfmi', sfmi => sfmi.set('showingBanner', true))
    case FsGen.hideSystemFileManagerIntegrationBanner:
      return state.update('sfmi', sfmi => sfmi.set('showingBanner', false))
    case FsGen.driverEnable:
      return state.update('sfmi', sfmi =>
        sfmi.update('driverStatus', driverStatus =>
          driverStatus.type === 'disabled' ? driverStatus.set('isEnabling', true) : driverStatus
        )
      )
    case FsGen.driverKextPermissionError:
      return state.update('sfmi', sfmi =>
        sfmi.update('driverStatus', driverStatus =>
          driverStatus.type === 'disabled'
            ? driverStatus.set('kextPermissionError', true).set('isEnabling', false)
            : driverStatus
        )
      )
    case FsGen.driverDisable:
      return state.update('sfmi', sfmi =>
        sfmi.update('driverStatus', driverStatus =>
          driverStatus.type === 'enabled' ? driverStatus.set('isDisabling', true) : driverStatus
        )
      )
    case FsGen.folderListLoad:
    case FsGen.placeholderAction:
    case FsGen.pathItemLoad:
    case FsGen.download:
    case FsGen.favoritesLoad:
    case FsGen.uninstallKBFSConfirm:
    case FsGen.notifySyncActivity:
    case FsGen.notifyTlfUpdate:
    case FsGen.openSecurityPreferences:
    case FsGen.refreshLocalHTTPServerInfo:
    case FsGen.shareNative:
    case FsGen.saveMedia:
    case FsGen.mimeTypeLoad:
    case FsGen.openPathItem:
    case FsGen.openPathInFilesTab:
    case FsGen.openPathInSystemFileManager:
    case FsGen.openLocalPathInSystemFileManager:
    case FsGen.editSuccess:
    case FsGen.letResetUserBackIn:
    case FsGen.openAndUpload:
    case FsGen.pickAndUpload:
    case FsGen.upload:
    case FsGen.openFilesFromWidget:
    case FsGen.userFileEditsLoad:
    case FsGen.deleteFile:
    case FsGen.move:
    case FsGen.copy:
    case FsGen.moveOrCopyOpen:
    case FsGen.closeMoveOrCopy:
    case FsGen.clearRefreshTag:
    case FsGen.loadPathMetadata:
    case FsGen.refreshDriverStatus:
      return state
    default:
      Flow.ifFlowComplainsAboutThisFunctionYouHaventHandledAllCasesInASwitch(action)
      return state
  }
}
<|endoftext|>


<commit_before>var assert = require("assert");
var express = require("express");
var r = require("supertest");
var parseString = require('xml2js').parseString;
var should = require('should');
/**
* Test
*
* @param {Object} req - The request value.
**/
process.env.PORT = 4000;
var app = require("../app.js");

describe('General incoming request', function(){

  it('should respond with xml', function(done){
    r(app)
    .post('/msg')
    .send({Body:"8004"})
    .set('Accept', 'application/xml')
    .expect(200)
    .end(function(err, res){
      if (err) throw err;
      done();
    });
  });

  it('for a light rail stop named 8004', function(done){
    r(app)
    .post('/msg')
    .send({Body:"8004"})
    .set('Accept', 'application/xml')
    .expect(200)
    .end(function(err, res){
      if (err) throw err;
      should.not.exist(err);

      // cleans utf8 parsing for xml in form
      // see http://www.multiasking.com/blog/xml2js-sax-js-non-whitespace-before-first-tag/
      var xml = res.text.replace("\ufeff", "");
      parseString(xml, function (err, ouput) {
         var parse = ouput.Response.Message[0];
         parse.should.startWith('Light rail ');
      });
      done();
    });
  });
  it('should decode a bus stop for an address in norfolk ', function(done){
    r(app)
    .post('/msg')
    .send({Body:"111 Granby street "})
    .set('Accept', 'application/xml')
    .expect(200)
    .end(function(err, res){
      if (err) throw err;
      should.not.exist(err);

      // cleans utf8 parsing for xml in form
      // see http://www.multiasking.com/blog/xml2js-sax-js-non-whitespace-before-first-tag/
      var xml = res.text.replace("\ufeff", "");
      parseString(xml, function (err, ouput) {
        var parse = ouput.Response.Message[0];
        parse.should.startWith('Light rail ');
      });
      done();
    });
  });
  it('should decode a full address', function(done){
    r(app)
    .post('/msg')
    .send({Body:"111 Granby street Norfolk VA 23508"})
    .set('Accept', 'application/xml')
    .expect(200)
    .end(function(err, res){
      if (err) throw err;
      should.not.exist(err);

      // cleans utf8 parsing for xml in form
      // see http://www.multiasking.com/blog/xml2js-sax-js-non-whitespace-before-first-tag/
      var xml = res.text.replace("\ufeff", "");
      parseString(xml, function (err, ouput) {
        var parse = ouput.Response.Message[0];
        parse.should.startWith('Light rail ');
      });
      done();
    });
  });

  it('respond to term "help" with a useful message', function(done){
    r(app)
    .post('/msg')
    .send({Body:"help"})
    .set('Accept', 'application/xml')
    .expect(200)
    .end(function(err, res){
      if (err) throw err;
      should.not.exist(err);

      // cleans utf8 parsing for xml in form
      var xml = res.text.replace("\ufeff", "");
      parseString(xml, function (err, ouput) {
        var parse = ouput.Response.Message[0];
        parse.should.startWith('Hi, thanks for texting');
      });
      done();
    });
  });

  it('Search for an invalid bus stop number should respond with error message', function(done){
    r(app)
    .post('/msg')
    .send({Body:"82311"})
    .set('Accept', 'application/xml')
    .expect(200)
    .end(function(err, res){
      if (err) throw err;
      should.not.exist(err);

      // cleans utf8 parsing for xml in form
      var xml = res.text.replace("\ufeff", "");
      parseString(xml, function (err, ouput) {
        var parse = ouput.Response.Message[0];
        parse.should.startWith('Hmm, ');
      });
      done();
    });
  });
});

// #TODO Tests for
// Bounding Boxes
// Asking for help
// Checking for input a known address
// Checking for input a unknown/invalid address
// Check for known stop number
// Check for unknown/invalid stop number
// Check for addresses in 7 cities with generic typical names
<commit_msg>fixed nesting of done operators<commit_after>var assert = require("assert");
var express = require("express");
var r = require("supertest");
var parseString = require('xml2js').parseString;
var should = require('should');
/**
* Test
*
* @param {Object} req - The request value.
**/
process.env.PORT = 4000;
var app = require("../app.js");

describe('General incoming request', function(){

  it('should respond with xml', function(done){
    r(app)
    .post('/msg')
    .send({Body:"8004"})
    .set('Accept', 'application/xml')
    .expect(200)
    .end(function(err, res){
      if (err) throw err;
      done();
    });
  });

  it('decode a typical stop, such as for a light rail stop named 8004', function(done){
    r(app)
    .post('/msg')
    .send({Body:"8004"})
    .set('Accept', 'application/xml')
    .expect(200)
    .end(function(err, res){
      if (err) throw err;
      should.not.exist(err);

      // cleans utf8 parsing for xml in form
      // see http://www.multiasking.com/blog/xml2js-sax-js-non-whitespace-before-first-tag/
      var xml = res.text.replace("\ufeff", "");
      parseString(xml, function (err, ouput) {
         var parse = ouput.Response.Message[0];
         parse.should.startWith('Light rail ');
         done();
      });
    });
  });

  it('should decode a bus stop for an address in norfolk ', function(done){
    r(app)
    .post('/msg')
    .send({Body:"111 Granby street "})
    .set('Accept', 'application/xml')
    .expect(200)
    .end(function(err, res){
      if (err) throw err;
      should.not.exist(err);

      // cleans utf8 parsing for xml in form
      // see http://www.multiasking.com/blog/xml2js-sax-js-non-whitespace-before-first-tag/
      var xml = res.text.replace("\ufeff", "");
      parseString(xml, function (err, ouput) {
        var parse = ouput.Response.Message[0];
        parse.should.startWith('Light rail ');
        done();
      });
    });
  });
  it('should decode a full address', function(done){
    r(app)
    .post('/msg')
    .send({Body:"111 Granby street Norfolk VA 23508"})
    .set('Accept', 'application/xml')
    .expect(200)
    .end(function(err, res){
      if (err) throw err;
      should.not.exist(err);

      // cleans utf8 parsing for xml in form
      // see http://www.multiasking.com/blog/xml2js-sax-js-non-whitespace-before-first-tag/
      var xml = res.text.replace("\ufeff", "");
      parseString(xml, function (err, ouput) {
        var parse = ouput.Response.Message[0];
        parse.should.startWith('Light rail ');
        done();
      });
    });
  });
});

// #TODO Tests for
// Bounding Boxes
// Asking for help
// Checking for input a known address
// Checking for input a unknown/invalid address
// Check for known stop number
// Check for unknown/invalid stop number
// Check for addresses in 7 cities with generic typical names
// Bus stop with no buses coming
<|endoftext|>


<commit_before>'use strict';

/**
 * Test ip.js.
 */

const
    ip = require('../ip'),
    expect = require('chai').expect;

function isIp(addr, arr) {
    let loc = ip.find(addr);
    expect(loc).to.be.a('array');
    expect(JSON.stringify(loc)).to.equal(JSON.stringify(arr), `${addr} is not ${arr}?`);
}

describe('#ip', () => {

    it('find 1.x.x.x', async () => {
        isIp('1.1.1.1', ['APNIC', 'APNIC', '', '']);
        isIp('1.2.3.4', ['APNIC', 'APNIC', '', '']);
        isIp('1.11.22.33', ['韩国', '韩国', '', '']);
        isIp('32.0.0.0', ['欧盟', '欧盟', '', '']);
        isIp('120.144.0.0', ['澳大利亚', '澳大利亚', '', '']);
        isIp('197.32.0.0', ['埃及', '埃及', '', '']);
        isIp('5.136.0.0', ['俄罗斯', '俄罗斯', '', '']);
    });

    it('find 10.x.x.x', async () => {
        isIp('10.0.1.1', ['局域网', '局域网', '', '']);
        isIp('10.1.1.1', ['局域网', '局域网', '', '']);
        isIp('10.10.10.10', ['局域网', '局域网', '', '']);
    });

    it('find 192.168.x.x', async () => {
        isIp('192.168.0.1', ['局域网', '局域网', '', '']);
        isIp('192.168.1.1', ['局域网', '局域网', '', '']);
        isIp('192.168.10.10', ['局域网', '局域网', '', '']);
    });

    it('find usa', async () => {
        isIp('11.0.0.0', ['美国', '美国', '', '']);
        isIp('28.0.0.0', ['美国', '美国', '', '']);
        isIp('71.0.0.0', ['美国', '美国', '', '']);
        isIp('44.0.0.0', ['美国', '美国', '', '']);
        isIp('74.222.96.0', ['美国', '美国', '', '']);
        isIp('135.186.0.0', ['美国', '美国', '', '']);
        isIp('159.118.0.0', ['美国', '美国', '', '']);
        isIp('209.240.128.0', ['美国', '美国', '', '']);
    });

    it('find beijing', async () => {
        isIp('110.96.0.0', ['中国', '北京', '北京', '']);
        isIp('36.192.0.255', ['中国', '北京', '北京', '']);
        isIp('117.112.0.2', ['中国', '北京', '北京', '']);
        isIp('171.84.10.10', ['中国', '北京', '北京', '']);
        isIp('1.88.0.0', ['中国', '北京', '北京', '']);
        isIp('118.144.0.0', ['中国', '北京', '北京', '']);
        isIp('111.196.10.0', ['中国', '北京', '北京', '']);
        isIp('118.188.0.0', ['中国', '北京', '北京', '']);
        isIp('42.83.128.0', ['中国', '北京', '北京', '']);
        isIp('119.10.0.0', ['中国', '北京', '北京', '']);
        isIp('219.158.128.0', ['中国', '北京', '北京', '']);
        isIp('210.12.64.0', ['中国', '北京', '北京', '']);
        isIp('202.96.0.0', ['中国', '北京', '北京', '']);
        isIp('203.92.160.0', ['中国', '北京', '北京', '']);
        isIp('180.149.128.0', ['中国', '北京', '北京', '']);
        isIp('202.165.208.0', ['中国', '北京', '北京', '']);
        isIp('203.86.64.0', ['中国', '北京', '北京', '']);
        isIp('121.52.208.0', ['中国', '北京', '北京', '']);
    });

    it('find other', async () => {
        isIp('123.64.1.1', ['中国', '广东', '广州', '']);
        isIp('111.128.0.1', ['中国', '福建', '南平', '']);
        isIp('101.104.0.23', ['中国', '广东', '广州', '']);
        isIp('106.52.0.33', ['中国', '中国', '', '']);
        isIp('115.120.0.103', ['中国', '陕西', '西安', '']);
        isIp('1.116.0.1', ['中国', '中国', '', '']);
        isIp('220.154.0.1', ['中国', '中国', '', '']);
        isIp('211.81.0.0', ['中国', '天津', '天津', '']);
        isIp('223.201.0.0', ['中国', '中国', '', '']);
        isIp('114.215.0.0', ['中国', '山东', '青岛', '']);
        isIp('123.99.128.0', ['中国', '中国', '', '']);
        isIp('101.251.128.0', ['中国', '中国', '', '']);
        isIp('42.240.128.0', ['中国', '中国', '', '']);
        isIp('223.252.128.0', ['中国', '河南', '', '']);
        isIp('124.21.64.0', ['中国', '中国', '', '']);
        isIp('111.223.248.0', ['中国', '中国', '', '']);
        isIp('114.212.0.1', ['中国', '江苏', '南京', '']);
        isIp('202.119.128.0', ['中国', '江苏', '南京', '']);
        isIp('49.64.0.10', ['中国', '江苏', '苏州', '']);
        isIp('180.160.0.1', ['中国', '上海', '上海', '']);
        isIp('27.115.0.11', ['中国', '上海', '上海', '']);
        isIp('49.246.224.11', ['中国', '台湾', '', '']);
        isIp('202.97.96.1', ['中国', '香港', '', '']);
    });

});
<commit_msg>add speed test<commit_after>'use strict';

/**
 * Test ip.js.
 */

const
    ip = require('../ip'),
    expect = require('chai').expect;

function isIp(addr, arr) {
    let loc = ip.find(addr);
    expect(loc).to.be.a('array');
    expect(JSON.stringify(loc)).to.equal(JSON.stringify(arr), `${addr} is not ${arr}?`);
}

describe('#ip', () => {

    it('find 1.x.x.x', async () => {
        isIp('1.1.1.1', ['APNIC', 'APNIC', '', '']);
        isIp('1.2.3.4', ['APNIC', 'APNIC', '', '']);
        isIp('1.11.22.33', ['韩国', '韩国', '', '']);
        isIp('32.0.0.0', ['欧盟', '欧盟', '', '']);
        isIp('120.144.0.0', ['澳大利亚', '澳大利亚', '', '']);
        isIp('197.32.0.0', ['埃及', '埃及', '', '']);
        isIp('5.136.0.0', ['俄罗斯', '俄罗斯', '', '']);
    });

    it('find 10.x.x.x', async () => {
        isIp('10.0.1.1', ['局域网', '局域网', '', '']);
        isIp('10.1.1.1', ['局域网', '局域网', '', '']);
        isIp('10.10.10.10', ['局域网', '局域网', '', '']);
    });

    it('find 192.168.x.x', async () => {
        isIp('192.168.0.1', ['局域网', '局域网', '', '']);
        isIp('192.168.1.1', ['局域网', '局域网', '', '']);
        isIp('192.168.10.10', ['局域网', '局域网', '', '']);
    });

    it('find usa', async () => {
        isIp('11.0.0.0', ['美国', '美国', '', '']);
        isIp('28.0.0.0', ['美国', '美国', '', '']);
        isIp('71.0.0.0', ['美国', '美国', '', '']);
        isIp('44.0.0.0', ['美国', '美国', '', '']);
        isIp('74.222.96.0', ['美国', '美国', '', '']);
        isIp('135.186.0.0', ['美国', '美国', '', '']);
        isIp('159.118.0.0', ['美国', '美国', '', '']);
        isIp('209.240.128.0', ['美国', '美国', '', '']);
    });

    it('find beijing', async () => {
        isIp('110.96.0.0', ['中国', '北京', '北京', '']);
        isIp('36.192.0.255', ['中国', '北京', '北京', '']);
        isIp('117.112.0.2', ['中国', '北京', '北京', '']);
        isIp('171.84.10.10', ['中国', '北京', '北京', '']);
        isIp('1.88.0.0', ['中国', '北京', '北京', '']);
        isIp('118.144.0.0', ['中国', '北京', '北京', '']);
        isIp('111.196.10.0', ['中国', '北京', '北京', '']);
        isIp('118.188.0.0', ['中国', '北京', '北京', '']);
        isIp('42.83.128.0', ['中国', '北京', '北京', '']);
        isIp('119.10.0.0', ['中国', '北京', '北京', '']);
        isIp('219.158.128.0', ['中国', '北京', '北京', '']);
        isIp('210.12.64.0', ['中国', '北京', '北京', '']);
        isIp('202.96.0.0', ['中国', '北京', '北京', '']);
        isIp('203.92.160.0', ['中国', '北京', '北京', '']);
        isIp('180.149.128.0', ['中国', '北京', '北京', '']);
        isIp('202.165.208.0', ['中国', '北京', '北京', '']);
        isIp('203.86.64.0', ['中国', '北京', '北京', '']);
        isIp('121.52.208.0', ['中国', '北京', '北京', '']);
    });

    it('find other', async () => {
        isIp('123.64.1.1', ['中国', '广东', '广州', '']);
        isIp('111.128.0.1', ['中国', '福建', '南平', '']);
        isIp('101.104.0.23', ['中国', '广东', '广州', '']);
        isIp('106.52.0.33', ['中国', '中国', '', '']);
        isIp('115.120.0.103', ['中国', '陕西', '西安', '']);
        isIp('1.116.0.1', ['中国', '中国', '', '']);
        isIp('220.154.0.1', ['中国', '中国', '', '']);
        isIp('211.81.0.0', ['中国', '天津', '天津', '']);
        isIp('223.201.0.0', ['中国', '中国', '', '']);
        isIp('114.215.0.0', ['中国', '山东', '青岛', '']);
        isIp('123.99.128.0', ['中国', '中国', '', '']);
        isIp('101.251.128.0', ['中国', '中国', '', '']);
        isIp('42.240.128.0', ['中国', '中国', '', '']);
        isIp('223.252.128.0', ['中国', '河南', '', '']);
        isIp('124.21.64.0', ['中国', '中国', '', '']);
        isIp('111.223.248.0', ['中国', '中国', '', '']);
        isIp('114.212.0.1', ['中国', '江苏', '南京', '']);
        isIp('202.119.128.0', ['中国', '江苏', '南京', '']);
        isIp('49.64.0.10', ['中国', '江苏', '苏州', '']);
        isIp('180.160.0.1', ['中国', '上海', '上海', '']);
        isIp('27.115.0.11', ['中国', '上海', '上海', '']);
        isIp('49.246.224.11', ['中国', '台湾', '', '']);
        isIp('202.97.96.1', ['中国', '香港', '', '']);
    });

    it('test speed', () => {
        // generate random ip:
        let
            addrs = [],
            randomIp = () => {
                return parseInt(Math.random() * 256) + '.' + parseInt(Math.random() * 256) + '.' + parseInt(Math.random() * 256) + '.' + parseInt(Math.random() * 256);
            };
        for (let i=0; i<10000; i++) {
            addrs.push(randomIp());
        }
        let start = Date.now();
        for (let addr of addrs) {
            ip.find(addr);
        }
        let last = Date.now() - start;
        console.log(`get 10000 ip address in ${last} ms.\n\n\n`);
    });
});
<|endoftext|>