<commit_before>(function(root, factory) {
    'use strict';
    // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers.

    /* istanbul ignore next */
    if (typeof define === 'function' && define.amd) {
        define('stacktrace-gps', ['source-map', 'stackframe'], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory(require('source-map/lib/source-map-consumer'), require('stackframe'));
    } else {
        root.StackTraceGPS = factory(root.SourceMap || root.sourceMap, root.StackFrame);
    }
}(this, function(SourceMap, StackFrame) {
    'use strict';

    /**
     * Make a X-Domain request to url and callback.
     *
     * @param {String} url
     * @returns {Promise} with response text if fulfilled
     */
    function _xdr(url) {
        return new Promise(function(resolve, reject) {
            var req = new XMLHttpRequest();
            req.open('get', url);
            req.onerror = reject;
            req.onreadystatechange = function onreadystatechange() {
                if (req.readyState === 4) {
                    if (req.status >= 200 && req.status < 300) {
                        resolve(req.responseText);
                    } else {
                        reject(new Error('HTTP status: ' + req.status + ' retrieving ' + url));
                    }
                }
            };
            req.send();
        });

    }

    /**
     * Convert a Base64-encoded string into its original representation.
     * Used for inline sourcemaps.
     *
     * @param {String} b64str Base-64 encoded string
     * @returns {String} original representation of the base64-encoded string.
     */
    function _atob(b64str) {
        if (typeof window !== 'undefined' && window.atob) {
            return window.atob(b64str);
        } else {
            throw new Error('You must supply a polyfill for window.atob in this environment');
        }
    }

    function _parseJson(string) {
        if (typeof JSON !== 'undefined' && JSON.parse) {
            return JSON.parse(string);
        } else {
            throw new Error('You must supply a polyfill for JSON.parse in this environment');
        }
    }

    function _findFunctionName(source, lineNumber/*, columnNumber*/) {
        // function {name}({args}) m[1]=name m[2]=args
        var reFunctionDeclaration = /function\s+([^(]*?)\s*\(([^)]*)\)/;
        // {name} = function ({args}) TODO args capture
        var reFunctionExpression = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/;
        // {name} = eval()
        var reFunctionEvaluation = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*(?:eval|new Function)\b/;
        var lines = source.split('\n');

        // Walk backwards in the source lines until we find the line which matches one of the patterns above
        var code = '';
        var maxLines = Math.min(lineNumber, 20);
        var m;
        for (var i = 0; i < maxLines; ++i) {
            // lineNo is 1-based, source[] is 0-based
            var line = lines[lineNumber - i - 1];
            var commentPos = line.indexOf('//');
            if (commentPos >= 0) {
                line = line.substr(0, commentPos);
            }

            if (line) {
                code = line + code;
                m = reFunctionExpression.exec(code);
                if (m && m[1]) {
                    return m[1];
                }
                m = reFunctionDeclaration.exec(code);
                if (m && m[1]) {
                    return m[1];
                }
                m = reFunctionEvaluation.exec(code);
                if (m && m[1]) {
                    return m[1];
                }
            }
        }
        return undefined;
    }

    function _ensureSupportedEnvironment() {
        if (typeof Object.defineProperty !== 'function' || typeof Object.create !== 'function') {
            throw new Error('Unable to consume source maps in older browsers');
        }
    }

    function _ensureStackFrameIsLegit(stackframe) {
        if (typeof stackframe !== 'object') {
            throw new TypeError('Given StackFrame is not an object');
        } else if (typeof stackframe.fileName !== 'string') {
            throw new TypeError('Given file name is not a String');
        } else if (typeof stackframe.lineNumber !== 'number' ||
            stackframe.lineNumber % 1 !== 0 ||
            stackframe.lineNumber < 1) {
            throw new TypeError('Given line number must be a positive integer');
        } else if (typeof stackframe.columnNumber !== 'number' ||
            stackframe.columnNumber % 1 !== 0 ||
            stackframe.columnNumber < 0) {
            throw new TypeError('Given column number must be a non-negative integer');
        }
        return true;
    }

    function _findSourceMappingURL(source) {
        var m = /\/\/[#@] ?sourceMappingURL=([^\s'"]+)\s*$/.exec(source);
        if (m && m[1]) {
            return m[1];
        } else {
            throw new Error('sourceMappingURL not found');
        }
    }

    function _extractLocationInfoFromSourceMap(stackframe, rawSourceMap, sourceCache) {
        return new Promise(function(resolve, reject) {
            var mapConsumer = new SourceMap.SourceMapConsumer(rawSourceMap);

            var loc = mapConsumer.originalPositionFor({
                line: stackframe.lineNumber,
                column: stackframe.columnNumber
            });

            if (loc.source) {
                var mappedSource = mapConsumer.sourceContentFor(loc.source);
                if (mappedSource) {
                    sourceCache[loc.source] = mappedSource;
                }
                resolve(
                    new StackFrame(
                        loc.name || stackframe.functionName,
                        stackframe.args,
                        loc.source,
                        loc.line,
                        loc.column));
            } else {
                reject(new Error('Could not get original source for given stackframe and source map'));
            }
        });
    }

    /**
     * @constructor
     * @param {Object} opts
     *      opts.sourceCache = {url: "Source String"} => preload source cache
     *      opts.offline = True to prevent network requests.
     *              Best effort without sources or source maps.
     *      opts.ajax = Promise returning function to make X-Domain requests
     */
    return function StackTraceGPS(opts) {
        if (!(this instanceof StackTraceGPS)) {
            return new StackTraceGPS(opts);
        }
        opts = opts || {};

        this.sourceCache = opts.sourceCache || {};

        this.ajax = opts.ajax || _xdr;

        this._atob = opts.atob || _atob;

        this._get = function _get(location) {
            return new Promise(function(resolve, reject) {
                var isDataUrl = location.substr(0, 5) === 'data:';
                if (this.sourceCache[location]) {
                    resolve(this.sourceCache[location]);
                } else if (opts.offline && !isDataUrl) {
                    reject(new Error('Cannot make network requests in offline mode'));
                } else {
                    if (isDataUrl) {
                        // data URLs can have parameters.
                        // see http://tools.ietf.org/html/rfc2397
                        var supportedEncodingRegexp =
                            /^data:application\/json;([\w=:"-]+;)*base64,/;
                        var match = location.match(supportedEncodingRegexp);
                        if (match) {
                            var sourceMapStart = match[0].length;
                            var encodedSource = location.substr(sourceMapStart);
                            var source = this._atob(encodedSource);
                            this.sourceCache[location] = source;
                            resolve(source);
                        } else {
                            reject(new Error('The encoding of the inline sourcemap is not supported'));
                        }
                    } else {
                        var xhrPromise = this.ajax(location, {method: 'get'});
                        // Cache the Promise to prevent duplicate in-flight requests
                        this.sourceCache[location] = xhrPromise;
                        xhrPromise.then(resolve, reject);
                    }
                }
            }.bind(this));
        };

        /**
         * Given a StackFrame, enhance function name and use source maps for a
         * better StackFrame.
         *
         * @param {StackFrame} stackframe object
         * @returns {Promise} that resolves with with source-mapped StackFrame
         */
        this.pinpoint = function StackTraceGPS$$pinpoint(stackframe) {
            return new Promise(function(resolve, reject) {
                this.getMappedLocation(stackframe).then(function(mappedStackFrame) {
                    function resolveMappedStackFrame() {
                        resolve(mappedStackFrame);
                    }

                    this.findFunctionName(mappedStackFrame)
                        .then(resolve, resolveMappedStackFrame)
                        ['catch'](resolveMappedStackFrame);
                }.bind(this), reject);
            }.bind(this));
        };

        /**
         * Given a StackFrame, guess function name from location information.
         *
         * @param {StackFrame} stackframe
         * @returns {Promise} that resolves with enhanced StackFrame.
         */
        this.findFunctionName = function StackTraceGPS$$findFunctionName(stackframe) {
            return new Promise(function(resolve, reject) {
                _ensureStackFrameIsLegit(stackframe);
                this._get(stackframe.fileName).then(function getSourceCallback(source) {
                    var lineNumber = stackframe.lineNumber;
                    var columnNumber = stackframe.columnNumber;
                    var guessedFunctionName = _findFunctionName(source, lineNumber, columnNumber);
                    // Only replace functionName if we found something
                    if (guessedFunctionName) {
                        resolve(new StackFrame(guessedFunctionName,
                            stackframe.args,
                            stackframe.fileName,
                            lineNumber,
                            columnNumber));
                    } else {
                        resolve(stackframe);
                    }
                }, reject)['catch'](reject);
            }.bind(this));
        };

        /**
         * Given a StackFrame, seek source-mapped location and return new enhanced StackFrame.
         *
         * @param {StackFrame} stackframe
         * @returns {Promise} that resolves with enhanced StackFrame.
         */
        this.getMappedLocation = function StackTraceGPS$$getMappedLocation(stackframe) {
            return new Promise(function(resolve, reject) {
                _ensureSupportedEnvironment();
                _ensureStackFrameIsLegit(stackframe);

                var sourceCache = this.sourceCache;
                var fileName = stackframe.fileName;
                this._get(fileName).then(function(source) {
                    var sourceMappingURL = _findSourceMappingURL(source);
                    var isDataUrl = sourceMappingURL.substr(0, 5) === 'data:';
                    var base = fileName.substring(0, fileName.lastIndexOf('/') + 1);

                    if (sourceMappingURL[0] !== '/' && !isDataUrl && !(/^https?:\/\/|^\/\//i).test(sourceMappingURL)) {
                        sourceMappingURL = base + sourceMappingURL;
                    }

                    return this._get(sourceMappingURL).then(function(sourceMap) {
                        if (typeof sourceMap === 'string') {
                            sourceMap = _parseJson(sourceMap.replace(/^\)\]\}'/, ''));
                        }
                        if (typeof sourceMap.sourceRoot === 'undefined') {
                            sourceMap.sourceRoot = base;
                        }

                        return _extractLocationInfoFromSourceMap(stackframe, sourceMap, sourceCache)
                            .then(resolve)['catch'](function() {
                            resolve(stackframe);
                        });
                    }, reject)['catch'](reject);
                }.bind(this), reject)['catch'](reject);
            }.bind(this));
        };
    };
}));
<commit_msg>Update format to support easily addition of regex.<commit_after>(function(root, factory) {
    'use strict';
    // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers.

    /* istanbul ignore next */
    if (typeof define === 'function' && define.amd) {
        define('stacktrace-gps', ['source-map', 'stackframe'], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory(require('source-map/lib/source-map-consumer'), require('stackframe'));
    } else {
        root.StackTraceGPS = factory(root.SourceMap || root.sourceMap, root.StackFrame);
    }
}(this, function(SourceMap, StackFrame) {
    'use strict';

    /**
     * Make a X-Domain request to url and callback.
     *
     * @param {String} url
     * @returns {Promise} with response text if fulfilled
     */
    function _xdr(url) {
        return new Promise(function(resolve, reject) {
            var req = new XMLHttpRequest();
            req.open('get', url);
            req.onerror = reject;
            req.onreadystatechange = function onreadystatechange() {
                if (req.readyState === 4) {
                    if (req.status >= 200 && req.status < 300) {
                        resolve(req.responseText);
                    } else {
                        reject(new Error('HTTP status: ' + req.status + ' retrieving ' + url));
                    }
                }
            };
            req.send();
        });

    }

    /**
     * Convert a Base64-encoded string into its original representation.
     * Used for inline sourcemaps.
     *
     * @param {String} b64str Base-64 encoded string
     * @returns {String} original representation of the base64-encoded string.
     */
    function _atob(b64str) {
        if (typeof window !== 'undefined' && window.atob) {
            return window.atob(b64str);
        } else {
            throw new Error('You must supply a polyfill for window.atob in this environment');
        }
    }

    function _parseJson(string) {
        if (typeof JSON !== 'undefined' && JSON.parse) {
            return JSON.parse(string);
        } else {
            throw new Error('You must supply a polyfill for JSON.parse in this environment');
        }
    }

    function _findFunctionName(source, lineNumber/*, columnNumber*/) {
        var syntaxes = [
          // {name} = function ({args}) TODO args capture
          /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/,
          // function {name}({args}) m[1]=name m[2]=args
          /function\s+([^(]*?)\s*\(([^)]*)\)/,
          // {name} = eval()
          /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*(?:eval|new Function)\b/,
        ]
        var lines = source.split('\n');

        // Walk backwards in the source lines until we find the line which matches one of the patterns above
        var code = '';
        var maxLines = Math.min(lineNumber, 20);
        var m;
        for (var i = 0; i < maxLines; ++i) {
            // lineNo is 1-based, source[] is 0-based
            var line = lines[lineNumber - i - 1];
            var commentPos = line.indexOf('//');
            if (commentPos >= 0) {
                line = line.substr(0, commentPos);
            }

            if (line) {
                code = line + code;
                var len = syntaxes.length;
                for (var i = 0; i < len; i++) {
                  var m = syntaxes[i].exec(code);
                  if (m && m[1]) {
                      return m[1];
                  }
                }
            }
        }
        return undefined;
    }

    function _ensureSupportedEnvironment() {
        if (typeof Object.defineProperty !== 'function' || typeof Object.create !== 'function') {
            throw new Error('Unable to consume source maps in older browsers');
        }
    }

    function _ensureStackFrameIsLegit(stackframe) {
        if (typeof stackframe !== 'object') {
            throw new TypeError('Given StackFrame is not an object');
        } else if (typeof stackframe.fileName !== 'string') {
            throw new TypeError('Given file name is not a String');
        } else if (typeof stackframe.lineNumber !== 'number' ||
            stackframe.lineNumber % 1 !== 0 ||
            stackframe.lineNumber < 1) {
            throw new TypeError('Given line number must be a positive integer');
        } else if (typeof stackframe.columnNumber !== 'number' ||
            stackframe.columnNumber % 1 !== 0 ||
            stackframe.columnNumber < 0) {
            throw new TypeError('Given column number must be a non-negative integer');
        }
        return true;
    }

    function _findSourceMappingURL(source) {
        var m = /\/\/[#@] ?sourceMappingURL=([^\s'"]+)\s*$/.exec(source);
        if (m && m[1]) {
            return m[1];
        } else {
            throw new Error('sourceMappingURL not found');
        }
    }

    function _extractLocationInfoFromSourceMap(stackframe, rawSourceMap, sourceCache) {
        return new Promise(function(resolve, reject) {
            var mapConsumer = new SourceMap.SourceMapConsumer(rawSourceMap);

            var loc = mapConsumer.originalPositionFor({
                line: stackframe.lineNumber,
                column: stackframe.columnNumber
            });

            if (loc.source) {
                var mappedSource = mapConsumer.sourceContentFor(loc.source);
                if (mappedSource) {
                    sourceCache[loc.source] = mappedSource;
                }
                resolve(
                    new StackFrame(
                        loc.name || stackframe.functionName,
                        stackframe.args,
                        loc.source,
                        loc.line,
                        loc.column));
            } else {
                reject(new Error('Could not get original source for given stackframe and source map'));
            }
        });
    }

    /**
     * @constructor
     * @param {Object} opts
     *      opts.sourceCache = {url: "Source String"} => preload source cache
     *      opts.offline = True to prevent network requests.
     *              Best effort without sources or source maps.
     *      opts.ajax = Promise returning function to make X-Domain requests
     */
    return function StackTraceGPS(opts) {
        if (!(this instanceof StackTraceGPS)) {
            return new StackTraceGPS(opts);
        }
        opts = opts || {};

        this.sourceCache = opts.sourceCache || {};

        this.ajax = opts.ajax || _xdr;

        this._atob = opts.atob || _atob;

        this._get = function _get(location) {
            return new Promise(function(resolve, reject) {
                var isDataUrl = location.substr(0, 5) === 'data:';
                if (this.sourceCache[location]) {
                    resolve(this.sourceCache[location]);
                } else if (opts.offline && !isDataUrl) {
                    reject(new Error('Cannot make network requests in offline mode'));
                } else {
                    if (isDataUrl) {
                        // data URLs can have parameters.
                        // see http://tools.ietf.org/html/rfc2397
                        var supportedEncodingRegexp =
                            /^data:application\/json;([\w=:"-]+;)*base64,/;
                        var match = location.match(supportedEncodingRegexp);
                        if (match) {
                            var sourceMapStart = match[0].length;
                            var encodedSource = location.substr(sourceMapStart);
                            var source = this._atob(encodedSource);
                            this.sourceCache[location] = source;
                            resolve(source);
                        } else {
                            reject(new Error('The encoding of the inline sourcemap is not supported'));
                        }
                    } else {
                        var xhrPromise = this.ajax(location, {method: 'get'});
                        // Cache the Promise to prevent duplicate in-flight requests
                        this.sourceCache[location] = xhrPromise;
                        xhrPromise.then(resolve, reject);
                    }
                }
            }.bind(this));
        };

        /**
         * Given a StackFrame, enhance function name and use source maps for a
         * better StackFrame.
         *
         * @param {StackFrame} stackframe object
         * @returns {Promise} that resolves with with source-mapped StackFrame
         */
        this.pinpoint = function StackTraceGPS$$pinpoint(stackframe) {
            return new Promise(function(resolve, reject) {
                this.getMappedLocation(stackframe).then(function(mappedStackFrame) {
                    function resolveMappedStackFrame() {
                        resolve(mappedStackFrame);
                    }

                    this.findFunctionName(mappedStackFrame)
                        .then(resolve, resolveMappedStackFrame)
                        ['catch'](resolveMappedStackFrame);
                }.bind(this), reject);
            }.bind(this));
        };

        /**
         * Given a StackFrame, guess function name from location information.
         *
         * @param {StackFrame} stackframe
         * @returns {Promise} that resolves with enhanced StackFrame.
         */
        this.findFunctionName = function StackTraceGPS$$findFunctionName(stackframe) {
            return new Promise(function(resolve, reject) {
                _ensureStackFrameIsLegit(stackframe);
                this._get(stackframe.fileName).then(function getSourceCallback(source) {
                    var lineNumber = stackframe.lineNumber;
                    var columnNumber = stackframe.columnNumber;
                    var guessedFunctionName = _findFunctionName(source, lineNumber, columnNumber);
                    // Only replace functionName if we found something
                    if (guessedFunctionName) {
                        resolve(new StackFrame(guessedFunctionName,
                            stackframe.args,
                            stackframe.fileName,
                            lineNumber,
                            columnNumber));
                    } else {
                        resolve(stackframe);
                    }
                }, reject)['catch'](reject);
            }.bind(this));
        };

        /**
         * Given a StackFrame, seek source-mapped location and return new enhanced StackFrame.
         *
         * @param {StackFrame} stackframe
         * @returns {Promise} that resolves with enhanced StackFrame.
         */
        this.getMappedLocation = function StackTraceGPS$$getMappedLocation(stackframe) {
            return new Promise(function(resolve, reject) {
                _ensureSupportedEnvironment();
                _ensureStackFrameIsLegit(stackframe);

                var sourceCache = this.sourceCache;
                var fileName = stackframe.fileName;
                this._get(fileName).then(function(source) {
                    var sourceMappingURL = _findSourceMappingURL(source);
                    var isDataUrl = sourceMappingURL.substr(0, 5) === 'data:';
                    var base = fileName.substring(0, fileName.lastIndexOf('/') + 1);

                    if (sourceMappingURL[0] !== '/' && !isDataUrl && !(/^https?:\/\/|^\/\//i).test(sourceMappingURL)) {
                        sourceMappingURL = base + sourceMappingURL;
                    }

                    return this._get(sourceMappingURL).then(function(sourceMap) {
                        if (typeof sourceMap === 'string') {
                            sourceMap = _parseJson(sourceMap.replace(/^\)\]\}'/, ''));
                        }
                        if (typeof sourceMap.sourceRoot === 'undefined') {
                            sourceMap.sourceRoot = base;
                        }

                        return _extractLocationInfoFromSourceMap(stackframe, sourceMap, sourceCache)
                            .then(resolve)['catch'](function() {
                            resolve(stackframe);
                        });
                    }, reject)['catch'](reject);
                }.bind(this), reject)['catch'](reject);
            }.bind(this));
        };
    };
}));
<|endoftext|>


<commit_before>var common = require('../lib/http-proxy/common'),
    url = require('url'),
    expect = require('expect.js');

describe('lib/http-proxy/common.js', function () {
  describe('#setupOutgoing', function () {
    it('should setup the correct headers', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing,
      {
        agent     : '?',
        target: {
          host      : 'hey',
          hostname  : 'how',
          socketPath: 'are',
          port      : 'you',
        },
        headers: {'fizz': 'bang', 'overwritten':true},
        localAddress: 'local.address',
      },
      {
        method    : 'i',
        url      : 'am',
        headers   : {'pro':'xy','overwritten':false}
      });

      expect(outgoing.host).to.eql('hey');
      expect(outgoing.hostname).to.eql('how');
      expect(outgoing.socketPath).to.eql('are');
      expect(outgoing.port).to.eql('you');
      expect(outgoing.agent).to.eql('?');

      expect(outgoing.method).to.eql('i');
      expect(outgoing.path).to.eql('am');

      expect(outgoing.headers.pro).to.eql('xy');
      expect(outgoing.headers.fizz).to.eql('bang');
      expect(outgoing.headers.overwritten).to.eql(true);
      expect(outgoing.localAddress).to.eql('local.address');
    });

    it('should not override agentless upgrade header', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing,
        {
          agent: undefined,
          target: {
            host      : 'hey',
            hostname  : 'how',
            socketPath: 'are',
            port      : 'you',
          },
          headers: {'connection': 'upgrade'},
        },
        {
          method    : 'i',
          url      : 'am',
          headers   : {'pro':'xy','overwritten':false}
        });
      expect(outgoing.headers.connection).to.eql('upgrade');
    });

    it('should not override agentless connection: contains upgrade', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing,
        {
          agent: undefined,
          target: {
            host      : 'hey',
            hostname  : 'how',
            socketPath: 'are',
            port      : 'you',
          },
          headers: {'connection': 'keep-alive, upgrade'}, // this is what Firefox sets
        },
        {
          method    : 'i',
          url      : 'am',
          headers   : {'pro':'xy','overwritten':false}
        });
      expect(outgoing.headers.connection).to.eql('keep-alive, upgrade');
    });

    it('should override agentless connection: contains improper upgrade', function () {
      // sanity check on upgrade regex
      var outgoing = {};
      common.setupOutgoing(outgoing,
        {
          agent: undefined,
          target: {
            host      : 'hey',
            hostname  : 'how',
            socketPath: 'are',
            port      : 'you',
          },
          headers: {'connection': 'keep-alive, not upgrade'},
        },
        {
          method    : 'i',
          url      : 'am',
          headers   : {'pro':'xy','overwritten':false}
        });
      expect(outgoing.headers.connection).to.eql('close');
    });

    it('should override agentless non-upgrade header to close', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing,
        {
          agent: undefined,
          target: {
            host      : 'hey',
            hostname  : 'how',
            socketPath: 'are',
            port      : 'you',
          },
          headers: {'connection': 'xyz'},
        },
        {
          method    : 'i',
          url      : 'am',
          headers   : {'pro':'xy','overwritten':false}
        });
      expect(outgoing.headers.connection).to.eql('close');
    });

    it('should set the agent to false if none is given', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing, {target:
        'http://localhost'
      }, { url: '/' });
      expect(outgoing.agent).to.eql(false);
    });

    it('set the port according to the protocol', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing,
      {
        agent     : '?',
        target: {
          host      : 'how',
          hostname  : 'are',
          socketPath: 'you',
          protocol: 'https:'
        }
      },
      {
        method    : 'i',
        url      : 'am',
        headers   : {pro:'xy'}
      });

      expect(outgoing.host).to.eql('how');
      expect(outgoing.hostname).to.eql('are');
      expect(outgoing.socketPath).to.eql('you');
      expect(outgoing.agent).to.eql('?');

      expect(outgoing.method).to.eql('i');
      expect(outgoing.path).to.eql('am');
      expect(outgoing.headers.pro).to.eql('xy');

      expect(outgoing.port).to.eql(443);
    });

    it('should keep the original target path in the outgoing path', function(){
      var outgoing = {};
      common.setupOutgoing(outgoing, {target:
        { path: 'some-path' }
      }, { url : 'am' });

      expect(outgoing.path).to.eql('some-path/am');
    });

    it('should keep the original forward path in the outgoing path', function(){
      var outgoing = {};
      common.setupOutgoing(outgoing, {
        target: {},
        forward: {
          path: 'some-path'
        }
      }, {
        url : 'am'
      }, 'forward');

      expect(outgoing.path).to.eql('some-path/am');
    });

    it('should not prepend the target path to the outgoing path with prependPath = false', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing, {
        target: { path: 'hellothere' },
        prependPath: false
      }, { url: 'hi' });

      expect(outgoing.path).to.eql('hi');
    })

    it('should properly join paths', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing, {
        target: { path: '/forward' },
      }, { url: '/static/path' });

      expect(outgoing.path).to.eql('/forward/static/path');
    })

    it('should not modify the query string', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing, {
        target: { path: '/forward' },
      }, { url: '/?foo=bar//&target=http://foobar.com/?a=1%26b=2&other=2' });

      expect(outgoing.path).to.eql('/forward/?foo=bar//&target=http://foobar.com/?a=1%26b=2&other=2');
    })

    //
    // This is the proper failing test case for the common.join problem
    //
    it('should correctly format the a toProxy URL', function () {
      var outgoing = {};
      var google = 'https://google.com'
      common.setupOutgoing(outgoing, {
        target: url.parse('http://sometarget.com:80'),
        toProxy: true,
      }, { url: google });

      expect(outgoing.path).to.eql('/' + google);
    })
  });

  describe('#setupSocket', function () {
    it('should setup a socket', function () {
      var socketConfig = {
        timeout: null,
        nodelay: false,
        keepalive: false
      },
      stubSocket = {
        setTimeout: function (num) {
          socketConfig.timeout = num;
        },
        setNoDelay: function (bol) {
          socketConfig.nodelay = bol;
        },
        setKeepAlive: function (bol) {
          socketConfig.keepalive = bol;
        }
      }
      returnValue = common.setupSocket(stubSocket);

      expect(socketConfig.timeout).to.eql(0);
      expect(socketConfig.nodelay).to.eql(true);
      expect(socketConfig.keepalive).to.eql(true);
    });
  });
});
<commit_msg>[test] add tests for the changeOrigin cases in properly setting the host header<commit_after>var common = require('../lib/http-proxy/common'),
    url = require('url'),
    expect = require('expect.js');

describe('lib/http-proxy/common.js', function () {
  describe('#setupOutgoing', function () {
    it('should setup the correct headers', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing,
      {
        agent     : '?',
        target: {
          host      : 'hey',
          hostname  : 'how',
          socketPath: 'are',
          port      : 'you',
        },
        headers: {'fizz': 'bang', 'overwritten':true},
        localAddress: 'local.address',
      },
      {
        method    : 'i',
        url      : 'am',
        headers   : {'pro':'xy','overwritten':false}
      });

      expect(outgoing.host).to.eql('hey');
      expect(outgoing.hostname).to.eql('how');
      expect(outgoing.socketPath).to.eql('are');
      expect(outgoing.port).to.eql('you');
      expect(outgoing.agent).to.eql('?');

      expect(outgoing.method).to.eql('i');
      expect(outgoing.path).to.eql('am');

      expect(outgoing.headers.pro).to.eql('xy');
      expect(outgoing.headers.fizz).to.eql('bang');
      expect(outgoing.headers.overwritten).to.eql(true);
      expect(outgoing.localAddress).to.eql('local.address');
    });

    it('should not override agentless upgrade header', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing,
        {
          agent: undefined,
          target: {
            host      : 'hey',
            hostname  : 'how',
            socketPath: 'are',
            port      : 'you',
          },
          headers: {'connection': 'upgrade'},
        },
        {
          method    : 'i',
          url      : 'am',
          headers   : {'pro':'xy','overwritten':false}
        });
      expect(outgoing.headers.connection).to.eql('upgrade');
    });

    it('should not override agentless connection: contains upgrade', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing,
        {
          agent: undefined,
          target: {
            host      : 'hey',
            hostname  : 'how',
            socketPath: 'are',
            port      : 'you',
          },
          headers: {'connection': 'keep-alive, upgrade'}, // this is what Firefox sets
        },
        {
          method    : 'i',
          url      : 'am',
          headers   : {'pro':'xy','overwritten':false}
        });
      expect(outgoing.headers.connection).to.eql('keep-alive, upgrade');
    });

    it('should override agentless connection: contains improper upgrade', function () {
      // sanity check on upgrade regex
      var outgoing = {};
      common.setupOutgoing(outgoing,
        {
          agent: undefined,
          target: {
            host      : 'hey',
            hostname  : 'how',
            socketPath: 'are',
            port      : 'you',
          },
          headers: {'connection': 'keep-alive, not upgrade'},
        },
        {
          method    : 'i',
          url      : 'am',
          headers   : {'pro':'xy','overwritten':false}
        });
      expect(outgoing.headers.connection).to.eql('close');
    });

    it('should override agentless non-upgrade header to close', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing,
        {
          agent: undefined,
          target: {
            host      : 'hey',
            hostname  : 'how',
            socketPath: 'are',
            port      : 'you',
          },
          headers: {'connection': 'xyz'},
        },
        {
          method    : 'i',
          url      : 'am',
          headers   : {'pro':'xy','overwritten':false}
        });
      expect(outgoing.headers.connection).to.eql('close');
    });

    it('should set the agent to false if none is given', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing, {target:
        'http://localhost'
      }, { url: '/' });
      expect(outgoing.agent).to.eql(false);
    });

    it('set the port according to the protocol', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing,
      {
        agent     : '?',
        target: {
          host      : 'how',
          hostname  : 'are',
          socketPath: 'you',
          protocol: 'https:'
        }
      },
      {
        method    : 'i',
        url      : 'am',
        headers   : {pro:'xy'}
      });

      expect(outgoing.host).to.eql('how');
      expect(outgoing.hostname).to.eql('are');
      expect(outgoing.socketPath).to.eql('you');
      expect(outgoing.agent).to.eql('?');

      expect(outgoing.method).to.eql('i');
      expect(outgoing.path).to.eql('am');
      expect(outgoing.headers.pro).to.eql('xy');

      expect(outgoing.port).to.eql(443);
    });

    it('should keep the original target path in the outgoing path', function(){
      var outgoing = {};
      common.setupOutgoing(outgoing, {target:
        { path: 'some-path' }
      }, { url : 'am' });

      expect(outgoing.path).to.eql('some-path/am');
    });

    it('should keep the original forward path in the outgoing path', function(){
      var outgoing = {};
      common.setupOutgoing(outgoing, {
        target: {},
        forward: {
          path: 'some-path'
        }
      }, {
        url : 'am'
      }, 'forward');

      expect(outgoing.path).to.eql('some-path/am');
    });

    it('should not prepend the target path to the outgoing path with prependPath = false', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing, {
        target: { path: 'hellothere' },
        prependPath: false
      }, { url: 'hi' });

      expect(outgoing.path).to.eql('hi');
    })

    it('should properly join paths', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing, {
        target: { path: '/forward' },
      }, { url: '/static/path' });

      expect(outgoing.path).to.eql('/forward/static/path');
    })

    it('should not modify the query string', function () {
      var outgoing = {};
      common.setupOutgoing(outgoing, {
        target: { path: '/forward' },
      }, { url: '/?foo=bar//&target=http://foobar.com/?a=1%26b=2&other=2' });

      expect(outgoing.path).to.eql('/forward/?foo=bar//&target=http://foobar.com/?a=1%26b=2&other=2');
    })

    //
    // This is the proper failing test case for the common.join problem
    //
    it('should correctly format the toProxy URL', function () {
      var outgoing = {};
      var google = 'https://google.com'
      common.setupOutgoing(outgoing, {
        target: url.parse('http://sometarget.com:80'),
        toProxy: true,
      }, { url: google });

      expect(outgoing.path).to.eql('/' + google);
    });

    describe('when using changeOrigin', function () {
      it('should correctly set the port to the host when it is a non-standard port using url.parse', function () {
        var outgoing = {};
        var myEndpoint = 'https://myCouch.com:6984';
        common.setupOutgoing(outgoing, {
          target: url.parse(myEndpoint),
          changeOrigin: true
        }, { url: '/' });

        expect(outgoing.headers.host).to.eql('mycouch.com:6984');
      });
      it('should correctly set the port to the host when it is a non-standard port when setting host and port manually (which ignores port)', function () {
        var outgoing = {};
        common.setupOutgoing(outgoing, {
          target: {
            protocol: 'https:',
            host: 'mycouch.com',
            port: 6984
          },
          changeOrigin: true
        }, { url: '/' });
        expect(outgoing.headers.host).to.eql('mycouch.com:6984');
      })
    });

  });

  describe('#setupSocket', function () {
    it('should setup a socket', function () {
      var socketConfig = {
        timeout: null,
        nodelay: false,
        keepalive: false
      },
      stubSocket = {
        setTimeout: function (num) {
          socketConfig.timeout = num;
        },
        setNoDelay: function (bol) {
          socketConfig.nodelay = bol;
        },
        setKeepAlive: function (bol) {
          socketConfig.keepalive = bol;
        }
      }
      returnValue = common.setupSocket(stubSocket);

      expect(socketConfig.timeout).to.eql(0);
      expect(socketConfig.nodelay).to.eql(true);
      expect(socketConfig.keepalive).to.eql(true);
    });
  });
});
<|endoftext|>


<commit_before>var resource = require('./resource');
var error = require('./error');

var route = {
    '/auth': resource.account.auth,
    '/register': resource.account.register,
    '/user': resource.user,
    '/user/contact': resource.contact,
    '/user/blacklist': resource.blacklist,
    '/user/message': resource.message
}

var router = module.exports = function (res, method, session, params, callback) {

    if (route[res] && route[res][method])
        route[res][method](session, params, callback);
    else callback(model.errors.e404);
};<commit_msg>Modify router of application service<commit_after>var resource = require('./resource');
var error = require('./error');

var route = {
    '/auth': resource.account.auth,
    '/register': resource.account.register,
    '/user': resource.user,
    '/user/contact': resource.contact,
    '/user/blacklist': resource.blacklist,
    '/user/message': resource.message
}

var router = module.exports = function (res, method, session, params, callback) {

    if (method == 'delete') method = 'del';

    if (route[res] && route[res][method])
        route[res][method](session, params, callback);
    else callback(model.errors.e404);
};<|endoftext|>


<commit_before>'use strict';

var RtlDetect = require('../../' + 'lib/rtl-detect');

var assert = require('chai').assert;

describe('rtl-detect', function() {

    describe('public', function() {

        it('isRtlLang()', function () {
            var strIn, out;

            out = RtlDetect.isRtlLang(strIn);
            assert.isUndefined(out);

            strIn = null;
            out = RtlDetect.isRtlLang(strIn);
            assert.isUndefined(out);

            strIn = '';
            out = RtlDetect.isRtlLang(strIn);
            assert.isUndefined(out);

            strIn = ' ';
            out = RtlDetect.isRtlLang(strIn);
            assert.isUndefined(out);

            strIn = '1234';
            out = RtlDetect.isRtlLang(strIn);
            assert.isUndefined(out)

            strIn = 'en';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, false);

            strIn = 'EN';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, false);

            strIn = 'en-US';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, false);

            strIn = 'en_US';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, false);

            strIn = 'en-us';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, false);

            strIn = 'ar';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, true);

            strIn = 'AR';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, true);

            strIn = 'ar-jo';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, true);

            strIn = 'ar-JO';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, true);

            strIn = 'ar_JO';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, true);

        });

        it('getLangDir()', function () {
            var strIn, out;

            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = null;
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = '';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = ' ';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = '1234';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = 'en';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = 'EN';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = 'en-US';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = 'en_US';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = 'en-us';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = 'ar';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'rtl');

            strIn = 'AR';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'rtl');

            strIn = 'ar-jo';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'rtl');

            strIn = 'ar-JO';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'rtl');

            strIn = 'ar_JO';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'rtl');
        });

    });

});
<commit_msg>Update rtl-detect-public_test.js<commit_after>//Copyright 2015, Yahoo! Inc.
'use strict';

var RtlDetect = require('../../' + 'lib/rtl-detect');

var assert = require('chai').assert;

describe('rtl-detect', function() {

    describe('public', function() {

        it('isRtlLang()', function () {
            var strIn, out;

            out = RtlDetect.isRtlLang(strIn);
            assert.isUndefined(out);

            strIn = null;
            out = RtlDetect.isRtlLang(strIn);
            assert.isUndefined(out);

            strIn = '';
            out = RtlDetect.isRtlLang(strIn);
            assert.isUndefined(out);

            strIn = ' ';
            out = RtlDetect.isRtlLang(strIn);
            assert.isUndefined(out);

            strIn = '1234';
            out = RtlDetect.isRtlLang(strIn);
            assert.isUndefined(out)

            strIn = 'en';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, false);

            strIn = 'EN';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, false);

            strIn = 'en-US';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, false);

            strIn = 'en_US';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, false);

            strIn = 'en-us';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, false);

            strIn = 'ar';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, true);

            strIn = 'AR';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, true);

            strIn = 'ar-jo';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, true);

            strIn = 'ar-JO';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, true);

            strIn = 'ar_JO';
            out = RtlDetect.isRtlLang(strIn);
            assert.strictEqual(out, true);

        });

        it('getLangDir()', function () {
            var strIn, out;

            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = null;
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = '';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = ' ';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = '1234';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = 'en';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = 'EN';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = 'en-US';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = 'en_US';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = 'en-us';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'ltr');

            strIn = 'ar';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'rtl');

            strIn = 'AR';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'rtl');

            strIn = 'ar-jo';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'rtl');

            strIn = 'ar-JO';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'rtl');

            strIn = 'ar_JO';
            out = RtlDetect.getLangDir(strIn);
            assert.strictEqual(out, 'rtl');
        });

    });

});
<|endoftext|>


<commit_before>import angular from "angular";
import { attach, delay } from "../../../utils.js";
import { DomCache } from "../../../cstm-ng-utils.js";
import dropzoneModule from "../../file-dropzone.js";

const serviceDependencies = ["$scope", "$element"];
function genComponentConf() {
  let template = `
      <won-file-dropzone on-image-picked="::self.updateFiles(image)" accepts="::self.detail.accepts">
      </won-file-dropzone>

 0">
        Chosen Files:

 0">

            {{ file.name }}

          <svg
            class="filep__preview__item__remove"
            ng-click="self.removeFile(file)">
            <use xlink:href="#ico36_close" href="#ico36_close"></use>
          </svg>

          <svg ng-if="!self.isImage(file)"
            class="filep__preview__item__typeicon">
            <use xlink:href="#ico36_uc_transport_demand" href="#ico36_uc_transport_demand"></use>
          </svg>

    `;

  class Controller {
    constructor() {
      attach(this, serviceDependencies, arguments);
      this.domCache = new DomCache(this.$element);

      window.filep4dbg = this;

      this.addedFiles = this.initialValue;

      delay(0).then(() => this.showInitialFiles());
    }

    /**
     * Checks validity and uses callback method
     */
    update(data) {
      if (data) {
        this.onUpdate({ value: data });
      } else {
        this.onUpdate({ value: undefined });
      }
    }

    showInitialFiles() {
      this.addedFiles = this.initialValue;
      this.$scope.$apply();
    }

    updateFiles(file) {
      console.log("called updateFiles: ", file);
      if (!this.addedFiles) {
        this.addedFiles = [];
      }
      this.addedFiles.push(file);
      this.update(this.addedFiles);
      this.$scope.$apply();
    }

    removeFile(file) {
      console.log("Removing file: " + file);
      //TODO: IMPL ME
    }

    isImage(file) {
      return file && /^image\//.test(file.type);
    }
  }
  Controller.$inject = serviceDependencies;

  return {
    restrict: "E",
    controller: Controller,
    controllerAs: "self",
    bindToController: true, //scope-bindings -> ctrl
    scope: {
      onUpdate: "&",
      initialValue: "=",
      detail: "=",
    },
    template: template,
  };
}

export default angular
  .module("won.owner.components.filePicker", [dropzoneModule])
  .directive("wonFilePicker", genComponentConf).name;
<commit_msg>ability to removeFiles from file-picker<commit_after>import angular from "angular";
import { attach, delay } from "../../../utils.js";
import { DomCache } from "../../../cstm-ng-utils.js";
import dropzoneModule from "../../file-dropzone.js";

const serviceDependencies = ["$scope", "$element"];
function genComponentConf() {
  let template = `
      <won-file-dropzone on-image-picked="::self.updateFiles(image)" accepts="::self.detail.accepts">
      </won-file-dropzone>

 0">
        Chosen Files:

 0">

            {{ file.name }}

          <svg
            class="filep__preview__item__remove"
            ng-click="self.removeFile(file)">
            <use xlink:href="#ico36_close" href="#ico36_close"></use>
          </svg>

          <svg ng-if="!self.isImage(file)"
            class="filep__preview__item__typeicon">
            <use xlink:href="#ico36_uc_transport_demand" href="#ico36_uc_transport_demand"></use>
          </svg>

    `;

  class Controller {
    constructor() {
      attach(this, serviceDependencies, arguments);
      this.domCache = new DomCache(this.$element);

      window.filep4dbg = this;

      this.addedFiles = this.initialValue;

      delay(0).then(() => this.showInitialFiles());
    }

    /**
     * Checks validity and uses callback method
     */
    update(data) {
      if (data && data.length > 0) {
        this.onUpdate({ value: data });
      } else {
        this.onUpdate({ value: undefined });
      }
    }

    showInitialFiles() {
      this.addedFiles = this.initialValue;
      this.$scope.$apply();
    }

    updateFiles(file) {
      console.log("called updateFiles: ", file);
      if (!this.addedFiles) {
        this.addedFiles = [];
      }
      this.addedFiles.push(file);
      this.update(this.addedFiles);
      this.$scope.$apply();
    }

    removeFile(fileToRemove) {
      console.log("Removing file: " + fileToRemove);
      if (!this.addedFiles) {
        this.addedFiles = [];
      }
      this.addedFiles = this.addedFiles.filter(file => fileToRemove !== file);
      this.update(this.addedFiles);
      this.$scope.$apply();
    }

    isImage(file) {
      return file && /^image\//.test(file.type);
    }
  }
  Controller.$inject = serviceDependencies;

  return {
    restrict: "E",
    controller: Controller,
    controllerAs: "self",
    bindToController: true, //scope-bindings -> ctrl
    scope: {
      onUpdate: "&",
      initialValue: "=",
      detail: "=",
    },
    template: template,
  };
}

export default angular
  .module("won.owner.components.filePicker", [dropzoneModule])
  .directive("wonFilePicker", genComponentConf).name;
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var chai = require('chai');
var expect = chai.expect;

var Database = require('../../lib/database');
var FakeAdapter = require('../fakes/adapter');

require('../helpers/model');

var db,
  adapter,
  Article,
  User,
  article;

describe('Model.belongsTo', function() {
  beforeEach(function() {
    adapter = FakeAdapter.create({});
    db = Database.create({ adapter: adapter });

    var belongsTo = db.belongsTo;
    var attr = db.attr;

    Article = db.model('article').reopen({
      title: attr(),
      author: belongsTo('user'),
      authorKey: attr('author_id') // writable access to foreign key attr
    });
    User = db.model('user').reopen({
      username: attr()
    });
  });

  beforeEach(function() {
    article = Article.fresh({ id: 932, title: 'Azul News', authorKey: 623 });
  });

  beforeEach(function() {
    adapter.intercept(/select.*from "users"/i, {
      fields: ['id', 'username'],
      rows: [{ id: 623, username: 'wbyoung' }]
    });
    adapter.intercept(/select.*from "articles"/i, {
      fields: ['id', 'title'],
      rows: [{ id: 448, title: 'Journal', 'author_id': 623 }]
    });
    adapter.intercept(/insert into "users"/i, {
      fields: ['id'],
      rows: [{ id: 838 }]
    });
  });

  describe('definition', function() {
    it('does not need to provide name', function() {
      Article.reopen({
        user: db.Model.belongsTo()
      });
      expect(article.userRelation._relatedModel).to.eql(db.model('user'));
    });
  });

  it('has related methods', function() {
    expect(Article.__class__.prototype).to.have.ownProperty('author');
    expect(Article.__class__.prototype).to.have.ownProperty('authorId');
    expect(article).to.respondTo('fetchAuthor');
    expect(article).to.respondTo('setAuthor');
  });

  describe('pre-fetch', function() {
    it('executes multiple queries', function(done) {
      Article.objects.with('author').fetch().then(function() {
        expect(adapter.executedSQL()).to.eql([
          ['SELECT * FROM "articles"', []],
          ['SELECT * FROM "users" WHERE "users"."id" = ? LIMIT 1', [623]]
        ]);
      })
      .done(done, done);
    });

    it('caches related objects', function(done) {
      Article.objects.with('author').fetch().get('0').then(function(foundArticle) {
        expect(foundArticle.id).to.eql(448);
        expect(foundArticle.authorId).to.eql(623);
        expect(foundArticle.author).to.eql(
          User.fresh({ id: 623, username: 'wbyoung' })
        );
      })
      .done(done, done);
    });

    it('works with models each having multiple related objects', function(done) {
      var articlesRegex = /select.*from "articles".*order by "id"/i;
      var usersRegex =
        /select.*from "users" where "users"."id" in \(\?, \?, \?\) limit 3/i;
      adapter.intercept(articlesRegex, {
        fields: ['id', 'title', 'author_id'],
        rows: [
          { id: 3, title: 'Announcing Azul', 'author_id': 1 },
          { id: 4, title: 'Tasty Kale Salad', 'author_id': 2 },
          { id: 5, title: 'Node.js ORM', 'author_id': 1 },
          { id: 6, title: 'The Bipartisan System', 'author_id': 4 },
          { id: 8, title: 'Awesome Margaritas', 'author_id': 2 },
          { id: 9, title: 'Delicious Pancakes', 'author_id': 2 }
        ]
      });
      adapter.intercept(usersRegex, {
        fields: ['id', 'username'],
        rows: [
          { id: 1, username: 'wbyoung' },
          { id: 4, username: 'sam' },
          { id: 2, username: 'kate' }
        ]
      });

      Article.objects.with('author').orderBy('id').fetch().then(function(articles) {
        expect(_(articles).map('title').value()).to.eql([
          'Announcing Azul', 'Tasty Kale Salad', 'Node.js ORM',
          'The Bipartisan System', 'Awesome Margaritas', 'Delicious Pancakes'
        ]);
        expect(_(articles).map('author').map('username').value()).to.eql([
          'wbyoung', 'kate', 'wbyoung', 'sam', 'kate', 'kate'
        ]);
      })
      .done(done, done);
    });

    it('works when the related value is sometimes absent', function(done) {
      var articlesRegex = /select.*from "articles".*order by "id"/i;
      var usersRegex =
        /select.*from "users" where "users"."id" in \(\?, \?\) limit 2/i;
      adapter.intercept(articlesRegex, {
        fields: ['id', 'title', 'author_id'],
        rows: [
          { id: 3, title: 'Announcing Azul', 'author_id': 874 },
          { id: 4, title: 'Tasty Kale Salad', 'author_id': null },
          { id: 6, title: 'The Bipartisan System', 'author_id': 4 },
        ]
      });
      adapter.intercept(usersRegex, {
        fields: ['id', 'username'],
        rows: [{ id: 874, username: 'wbyoung' }, { id: 4, username: 'kate' }]
      });

      Article.objects.with('author').orderBy('id').fetch().then(function(articles) {
        expect(_(articles).map('title').value()).to.eql([
          'Announcing Azul', 'Tasty Kale Salad', 'The Bipartisan System'
        ]);
        expect(_.map(articles, 'author')).to.eql([
          User.fresh({ id: 874, username: 'wbyoung' }), null,
          User.fresh({ id: 4, username: 'kate' })
        ]);
      })
      .done(done, done);
    });

    it('gives an error when it cannot find the related object', function(done) {
      adapter.intercept(/select.*from "users"/i, {
        fields: ['id', 'username'],
        rows: []
      });
      Article.objects.with('author').fetch()
      .throw(new Error('Expected fetch to fail.'))
      .catch(function(e) {
        expect(e.message).to.match(/found no.*User.*author_id.*623/i);
      })
      .done(done, done);
    });

    it('works via `fetchOne`', function(done) {
      Article.objects.where({ id: 1 }).with('author').fetchOne()
      .then(function(fetchedArticle) {
        expect(fetchedArticle.author).to.eql(
          User.fresh({ id: 623, username: 'wbyoung' })
        );
      })
      .done(done, done);
    });

    it('works via `find`', function(done) {
      Article.objects.with('author').find(1).then(function(fetchedArticle) {
        expect(fetchedArticle.author).to.eql(
          User.fresh({ id: 623, username: 'wbyoung' })
        );
      })
      .done(done, done);
    });
  });

  describe('relation', function() {

    it('fetches related object', function(done) {
      article.fetchAuthor().then(function(user) {
        expect(user.attrs).to.eql({ id: 623, username: 'wbyoung' });
        expect(adapter.executedSQL()).to.eql([
          ['SELECT * FROM "users" WHERE "users"."id" = ? LIMIT 1', [623]]
        ]);
      })
      .done(done, done);
    });

    it('gives an error when it cannot fetch the related object', function(done) {
      adapter.intercept(/select.*from "users"/i, {
        fields: ['id', 'username'],
        rows: []
      });
      article.fetchAuthor()
      .throw(new Error('Expected fetch to fail.'))
      .catch(function(e) {
        expect(e.message).to.match(/found no.*User.*author_id.*623/i);
      })
      .done(done, done);
    });

    it('does not fetch when the foreign key is not defined', function(done) {
      var unauthoredArticle = Article.fresh({ id: 932, title: 'Azul News' });
      unauthoredArticle.fetchAuthor().then(function(user) {
        expect(user).to.not.exist;
        expect(unauthoredArticle.author).to.not.exist;
        expect(adapter.executedSQL()).to.eql([]);
      })
      .done(done, done);
    });

    it('throws when attempting to access un-loaded item', function() {
      expect(function() {
        article.author;
      }).to.throw(/author.*not yet.*loaded/i);
    });

    it('allows access loaded item', function(done) {
      article.fetchAuthor().then(function() {
        expect(article.author.attrs).to.eql({ id: 623, username: 'wbyoung' });
      })
      .done(done, done);
    });
  });

  describe('helpers', function() {
    it('provides a getter method for the foreign key', function() {
      expect(article.authorId).to.eql(623);
    });

    it('does not provide a setter method for the foreign key', function() {
      expect(function() {
        article.authorId = 25;
      }).to.throw(/cannot set.*authorId/i);
    });

    it('allows create', function() {
      var user = article.createAuthor({ username: 'jill' });
      expect(article.author).to.equal(user);
      expect(user).to.to.be.an.instanceOf(User.__class__);
    });

    it('allows store with existing object', function(done) {
      article.author = User.fresh({ id: 3, username: 'jack' });
      article.save().then(function() {
        expect(adapter.executedSQL()).to.eql([
          ['UPDATE "articles" SET "title" = ?, "author_id" = ? ' +
           'WHERE "id" = ?', ['Azul News', 3, 932]]
        ]);
      })
      .done(done, done);
    });

    it('allows store with unsaved object', function(done) {
      var user = User.create({ username: 'jack' });
      article.author = user;
      article.save().then(function() {
        expect(adapter.executedSQL()).to.eql([
          ['INSERT INTO "users" ("username") VALUES (?) ' +
           'RETURNING "id"', ['jack']],
          ['UPDATE "articles" SET "title" = ?, "author_id" = ? ' +
           'WHERE "id" = ?', ['Azul News', 838, 932]]
        ]);
      })
      .done(done, done);
    });
  });
});
<commit_msg>Testing that fetched object is cached in a belongs to.<commit_after>'use strict';

var _ = require('lodash');
var chai = require('chai');
var expect = chai.expect;

var Database = require('../../lib/database');
var FakeAdapter = require('../fakes/adapter');

require('../helpers/model');

var db,
  adapter,
  Article,
  User,
  article;

describe('Model.belongsTo', function() {
  beforeEach(function() {
    adapter = FakeAdapter.create({});
    db = Database.create({ adapter: adapter });

    var belongsTo = db.belongsTo;
    var attr = db.attr;

    Article = db.model('article').reopen({
      title: attr(),
      author: belongsTo('user'),
      authorKey: attr('author_id') // writable access to foreign key attr
    });
    User = db.model('user').reopen({
      username: attr()
    });
  });

  beforeEach(function() {
    article = Article.fresh({ id: 932, title: 'Azul News', authorKey: 623 });
  });

  beforeEach(function() {
    adapter.intercept(/select.*from "users"/i, {
      fields: ['id', 'username'],
      rows: [{ id: 623, username: 'wbyoung' }]
    });
    adapter.intercept(/select.*from "articles"/i, {
      fields: ['id', 'title'],
      rows: [{ id: 448, title: 'Journal', 'author_id': 623 }]
    });
    adapter.intercept(/insert into "users"/i, {
      fields: ['id'],
      rows: [{ id: 838 }]
    });
  });

  describe('definition', function() {
    it('does not need to provide name', function() {
      Article.reopen({
        user: db.Model.belongsTo()
      });
      expect(article.userRelation._relatedModel).to.eql(db.model('user'));
    });
  });

  it('has related methods', function() {
    expect(Article.__class__.prototype).to.have.ownProperty('author');
    expect(Article.__class__.prototype).to.have.ownProperty('authorId');
    expect(article).to.respondTo('fetchAuthor');
    expect(article).to.respondTo('setAuthor');
  });

  describe('pre-fetch', function() {
    it('executes multiple queries', function(done) {
      Article.objects.with('author').fetch().then(function() {
        expect(adapter.executedSQL()).to.eql([
          ['SELECT * FROM "articles"', []],
          ['SELECT * FROM "users" WHERE "users"."id" = ? LIMIT 1', [623]]
        ]);
      })
      .done(done, done);
    });

    it('caches related objects', function(done) {
      Article.objects.with('author').fetch().get('0').then(function(foundArticle) {
        expect(foundArticle.id).to.eql(448);
        expect(foundArticle.authorId).to.eql(623);
        expect(foundArticle.author).to.eql(
          User.fresh({ id: 623, username: 'wbyoung' })
        );
      })
      .done(done, done);
    });

    it('works with models each having multiple related objects', function(done) {
      var articlesRegex = /select.*from "articles".*order by "id"/i;
      var usersRegex =
        /select.*from "users" where "users"."id" in \(\?, \?, \?\) limit 3/i;
      adapter.intercept(articlesRegex, {
        fields: ['id', 'title', 'author_id'],
        rows: [
          { id: 3, title: 'Announcing Azul', 'author_id': 1 },
          { id: 4, title: 'Tasty Kale Salad', 'author_id': 2 },
          { id: 5, title: 'Node.js ORM', 'author_id': 1 },
          { id: 6, title: 'The Bipartisan System', 'author_id': 4 },
          { id: 8, title: 'Awesome Margaritas', 'author_id': 2 },
          { id: 9, title: 'Delicious Pancakes', 'author_id': 2 }
        ]
      });
      adapter.intercept(usersRegex, {
        fields: ['id', 'username'],
        rows: [
          { id: 1, username: 'wbyoung' },
          { id: 4, username: 'sam' },
          { id: 2, username: 'kate' }
        ]
      });

      Article.objects.with('author').orderBy('id').fetch().then(function(articles) {
        expect(_(articles).map('title').value()).to.eql([
          'Announcing Azul', 'Tasty Kale Salad', 'Node.js ORM',
          'The Bipartisan System', 'Awesome Margaritas', 'Delicious Pancakes'
        ]);
        expect(_(articles).map('author').map('username').value()).to.eql([
          'wbyoung', 'kate', 'wbyoung', 'sam', 'kate', 'kate'
        ]);
      })
      .done(done, done);
    });

    it('works when the related value is sometimes absent', function(done) {
      var articlesRegex = /select.*from "articles".*order by "id"/i;
      var usersRegex =
        /select.*from "users" where "users"."id" in \(\?, \?\) limit 2/i;
      adapter.intercept(articlesRegex, {
        fields: ['id', 'title', 'author_id'],
        rows: [
          { id: 3, title: 'Announcing Azul', 'author_id': 874 },
          { id: 4, title: 'Tasty Kale Salad', 'author_id': null },
          { id: 6, title: 'The Bipartisan System', 'author_id': 4 },
        ]
      });
      adapter.intercept(usersRegex, {
        fields: ['id', 'username'],
        rows: [{ id: 874, username: 'wbyoung' }, { id: 4, username: 'kate' }]
      });

      Article.objects.with('author').orderBy('id').fetch().then(function(articles) {
        expect(_(articles).map('title').value()).to.eql([
          'Announcing Azul', 'Tasty Kale Salad', 'The Bipartisan System'
        ]);
        expect(_.map(articles, 'author')).to.eql([
          User.fresh({ id: 874, username: 'wbyoung' }), null,
          User.fresh({ id: 4, username: 'kate' })
        ]);
      })
      .done(done, done);
    });

    it('gives an error when it cannot find the related object', function(done) {
      adapter.intercept(/select.*from "users"/i, {
        fields: ['id', 'username'],
        rows: []
      });
      Article.objects.with('author').fetch()
      .throw(new Error('Expected fetch to fail.'))
      .catch(function(e) {
        expect(e.message).to.match(/found no.*User.*author_id.*623/i);
      })
      .done(done, done);
    });

    it('works via `fetchOne`', function(done) {
      Article.objects.where({ id: 1 }).with('author').fetchOne()
      .then(function(fetchedArticle) {
        expect(fetchedArticle.author).to.eql(
          User.fresh({ id: 623, username: 'wbyoung' })
        );
      })
      .done(done, done);
    });

    it('works via `find`', function(done) {
      Article.objects.with('author').find(1).then(function(fetchedArticle) {
        expect(fetchedArticle.author).to.eql(
          User.fresh({ id: 623, username: 'wbyoung' })
        );
      })
      .done(done, done);
    });
  });

  describe('relation', function() {

    it('fetches related object', function(done) {
      article.fetchAuthor().then(function(user) {
        expect(user.attrs).to.eql({ id: 623, username: 'wbyoung' });
        expect(adapter.executedSQL()).to.eql([
          ['SELECT * FROM "users" WHERE "users"."id" = ? LIMIT 1', [623]]
        ]);
      })
      .done(done, done);
    });

    it('caches the fetched object (one query for two fetches)', function(done) {
      article.fetchAuthor()
      .then(function() { return article.fetchAuthor(); })
      .then(function(user) {
        expect(user.attrs).to.eql({ id: 623, username: 'wbyoung' });
        expect(adapter.executedSQL()).to.eql([
          ['SELECT * FROM "users" WHERE "users"."id" = ? LIMIT 1', [623]]
        ]);
      })
      .done(done, done);
    });

    it('gives an error when it cannot fetch the related object', function(done) {
      adapter.intercept(/select.*from "users"/i, {
        fields: ['id', 'username'],
        rows: []
      });
      article.fetchAuthor()
      .throw(new Error('Expected fetch to fail.'))
      .catch(function(e) {
        expect(e.message).to.match(/found no.*User.*author_id.*623/i);
      })
      .done(done, done);
    });

    it('does not fetch when the foreign key is not defined', function(done) {
      var unauthoredArticle = Article.fresh({ id: 932, title: 'Azul News' });
      unauthoredArticle.fetchAuthor().then(function(user) {
        expect(user).to.not.exist;
        expect(unauthoredArticle.author).to.not.exist;
        expect(adapter.executedSQL()).to.eql([]);
      })
      .done(done, done);
    });

    it('throws when attempting to access un-loaded item', function() {
      expect(function() {
        article.author;
      }).to.throw(/author.*not yet.*loaded/i);
    });

    it('allows access loaded item', function(done) {
      article.fetchAuthor().then(function() {
        expect(article.author.attrs).to.eql({ id: 623, username: 'wbyoung' });
      })
      .done(done, done);
    });
  });

  describe('helpers', function() {
    it('provides a getter method for the foreign key', function() {
      expect(article.authorId).to.eql(623);
    });

    it('does not provide a setter method for the foreign key', function() {
      expect(function() {
        article.authorId = 25;
      }).to.throw(/cannot set.*authorId/i);
    });

    it('allows create', function() {
      var user = article.createAuthor({ username: 'jill' });
      expect(article.author).to.equal(user);
      expect(user).to.to.be.an.instanceOf(User.__class__);
    });

    it('allows store with existing object', function(done) {
      article.author = User.fresh({ id: 3, username: 'jack' });
      article.save().then(function() {
        expect(adapter.executedSQL()).to.eql([
          ['UPDATE "articles" SET "title" = ?, "author_id" = ? ' +
           'WHERE "id" = ?', ['Azul News', 3, 932]]
        ]);
      })
      .done(done, done);
    });

    it('allows store with unsaved object', function(done) {
      var user = User.create({ username: 'jack' });
      article.author = user;
      article.save().then(function() {
        expect(adapter.executedSQL()).to.eql([
          ['INSERT INTO "users" ("username") VALUES (?) ' +
           'RETURNING "id"', ['jack']],
          ['UPDATE "articles" SET "title" = ?, "author_id" = ? ' +
           'WHERE "id" = ?', ['Azul News', 838, 932]]
        ]);
      })
      .done(done, done);
    });
  });
});
<|endoftext|>


<commit_before>/*jslint node: true */
'use strict';

/*
 * ==================================================
 * Parcels
 * ==================================================
 *
 * Serves parcel data from the geo database.
 */

var pg = require('pg');

var client = null;

function bboxToPolygon(bbox) {
  var polygon = 'POLYGON((' +
                bbox[0] + ' ' + bbox[1] + ', ' +
                bbox[0] + ' ' + bbox[3] + ', ' +
                bbox[2] + ' ' + bbox[3] + ', ' +
                bbox[2] + ' ' + bbox[1] + ', ' +
                bbox[0] + ' ' + bbox[1] + '))';
  return polygon;
}

/*
 * Make values returned by the database neater.
 */
function clean(val) {
  if(val !== null) {
    return val.trim();
  }
  return "";
}

/*
 * app: express server
 */
function setup(app, settings) {
  var connectionString =
    'tcp://' + settings.psqlUser + ':' + settings.psqlPass +
    '@' + settings.psqlHost + '/' + settings.psqlName;

  if (client === null) {
    client = new pg.Client(connectionString);
    client.connect();
  }

  // Get parcels
  // Filter to include only parcels inside a bounding box or only parcels that
  // intersect a point.
  // We do not allow filtering by both, and we require one of the filters.
  // GET http://localhost:3000/api/parcels?bbox=-{SW_LON},{SW_LAT},{NE_LON},{NE_LAT}
  // GET http://localhost:3000/api/parcels?bbox=-83.0805,42.336,-83.08,42.34
  // GET http://localhost:3000/api/parcels?lon={LONGITUDE}&lat={LATITUDE}
  // GET http://localhost:3000/api/parcels?lon=-83.08076&lat=42.338
  app.get('/api/parcels', function(req, response) {
    var bbox = req.query.bbox;
    var lon = req.query.lon;
    var lat = req.query.lat;
    var query;
    var coordString;
    var coords;
    var output;
    var error;
    var i;

    // Require a filter
    if (bbox === undefined &&
        (lon === undefined || lat === undefined)) {
      response.send(413);
      return;
    }

    if (bbox !== undefined) {
      // Bounding box query

      // Don't allow both filters at once
      if (lon !== undefined || lat !== undefined) {
        response.send(400);
        return;
      }

      // Split bounding box into coordinates
      coordString = bbox.split(',');

      if (coordString.length !== 4) {
        response.send(400);
        return;
      }

      // Convert coordinates to numbers
      coords = [];
      for (i = 0; i < 4; i += 1) {
        coords[i] = parseFloat(coordString[i]);

        // Make sure the conversion worked
        if (isNaN(coords[i])) {
          response.send(400);
          return;
        }
      }

      query = client.query({
        text: 'SELECT parcelnumb, propaddres, proaddress, ST_AsGeoJSON(wkb_geometry) AS polygon, ST_AsGeoJSON(ST_Centroid(wkb_geometry)) AS centroid FROM qgis WHERE ST_Intersects(wkb_geometry, ST_SetSRID($1, 4326))',
        values: [bboxToPolygon(coords)],
        name: 'parcelBBoxQuery'
      });
    } else {
      // Point query

      // Convert coordinates to numbers
      lat = parseFloat(lat);
      lon = parseFloat(lon);

      if (isNaN(lat) || isNaN(lon)) {
        response.send(400);
        return;
      }

      query = client.query({
        text: 'SELECT parcelnumb, propaddres, proaddress, ST_AsGeoJSON(wkb_geometry) AS polygon, ST_AsGeoJSON(ST_Centroid(wkb_geometry)) AS centroid FROM qgis WHERE ST_Contains(wkb_geometry, ST_SetSRID($1, 4326))',
        values: ['POINT(' + lon + ' ' + lat + ')'],
        name: 'parcelPointQuery'
      });
    }

    output = [];
    query
    .on('row', function (row, result) {
      try {

        output.push({
          parcelId: clean(row.parcelnumb),
          address: clean(row.proaddress),
          polygon: JSON.parse(row.polygon),
          centroid: JSON.parse(row.centroid)
        });

      } catch (e) {
        console.log(row);
        error = e;
      }
    })
    .on('error', function (e) {
      console.log(e.message);
      error = e;
    })
    .on('end', function (result) {
      if (error) {
        console.log(error);
        response.send(500);
      } else {
        response.send(output);
      }
    });
  });
}

module.exports = {
  setup: setup
}
<commit_msg>Change the parcel API to use the new table structure<commit_after>/*jslint node: true */
'use strict';

/*
 * ==================================================
 * Parcels
 * ==================================================
 *
 * Serves parcel data from the geo database.
 */

var pg = require('pg');

var client = null;

function bboxToPolygon(bbox) {
  var polygon = 'POLYGON((' +
                bbox[0] + ' ' + bbox[1] + ', ' +
                bbox[0] + ' ' + bbox[3] + ', ' +
                bbox[2] + ' ' + bbox[3] + ', ' +
                bbox[2] + ' ' + bbox[1] + ', ' +
                bbox[0] + ' ' + bbox[1] + '))';
  return polygon;
}

/*
 * Make values returned by the database neater.
 */
function clean(val) {
  if(val
   !== null) {
    return val.trim();
  }
  return "";
}

/*
 * app: express server
 */
function setup(app, settings) {
  var connectionString =
    'tcp://' + settings.psqlUser + ':' + settings.psqlPass +
    '@' + settings.psqlHost + '/' + settings.psqlName;

  if (client === null) {
    client = new pg.Client(connectionString);
    client.connect();
  }

  // Get parcels
  // Filter to include only parcels inside a bounding box or only parcels that
  // intersect a point.
  // We do not allow filtering by both, and we require one of the filters.
  // GET http://localhost:3000/api/parcels?bbox=-{SW_LON},{SW_LAT},{NE_LON},{NE_LAT}
  // GET http://localhost:3000/api/parcels?bbox=-83.0805,42.336,-83.08,42.34
  // GET http://localhost:3000/api/parcels?lon={LONGITUDE}&lat={LATITUDE}
  // GET http://localhost:3000/api/parcels?lon=-83.08076&lat=42.338
  app.get('/api/parcels', function(req, response) {
    var bbox = req.query.bbox;
    var lon = req.query.lon;
    var lat = req.query.lat;
    var query;
    var coordString;
    var coords;
    var output;
    var error;
    var i;

    // Require a filter
    if (bbox === undefined &&
        (lon === undefined || lat === undefined)) {
      response.send(413);
      return;
    }

    if (bbox !== undefined) {
      // Bounding box query

      // Don't allow both filters at once
      if (lon !== undefined || lat !== undefined) {
        response.send(400);
        return;
      }

      // Split bounding box into coordinates
      coordString = bbox.split(',');

      if (coordString.length !== 4) {
        response.send(400);
        return;
      }

      // Convert coordinates to numbers
      coords = [];
      for (i = 0; i < 4; i += 1) {
        coords[i] = parseFloat(coordString[i]);

        // Make sure the conversion worked
        if (isNaN(coords[i])) {
          response.send(400);
          return;
        }
      }

      query = client.query({
        text: 'SELECT object_id, name1, name2, source, created, ST_AsGeoJSON(wkb_geometry) AS polygon, ST_AsGeoJSON(ST_Centroid(wkb_geometry)) AS centroid FROM objects WHERE ST_Intersects(wkb_geometry, ST_SetSRID($1, 4326))',
        values: [bboxToPolygon(coords)],
        name: 'parcelBBoxQuery'
      });
    } else {
      // Point query

      // Convert coordinates to numbers
      lat = parseFloat(lat);
      lon = parseFloat(lon);

      if (isNaN(lat) || isNaN(lon)) {
        response.send(400);
        return;
      }

      query = client.query({
        text: 'SELECT object_id, name1, name2, source, created, ST_AsGeoJSON(wkb_geometry) AS polygon, ST_AsGeoJSON(ST_Centroid(wkb_geometry)) AS centroid FROM objects WHERE ST_Contains(wkb_geometry, ST_SetSRID($1, 4326))',
        values: ['POINT(' + lon + ' ' + lat + ')'],
        name: 'parcelPointQuery'
      });
    }

    output = [];
    query
    .on('row', function (row, result) {
      try {

        output.push({
          parcelId: clean(row.object_id),
          address: clean(row.name1) + " " + clean(row.name2),
          polygon: JSON.parse(row.polygon),
          centroid: JSON.parse(row.centroid)
        });

      } catch (e) {
        console.log(row);
        error = e;
      }
    })
    .on('error', function (e) {
      console.log(e.message);
      error = e;
    })
    .on('end', function (result) {
      if (error) {
        console.log(error);
        response.send(500);
      } else {
        response.send(output);
      }
    });
  });
}

module.exports = {
  setup: setup
}
<|endoftext|>