<commit_before>/* vim:ts=4:sts=4:sw=4:
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Ajax.org Code Editor (ACE).
 *
 * The Initial Developer of the Original Code is
 * Ajax.org B.V.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *      Fabian Jakobs <fabian AT ajax DOT org>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

define(function(require, exports, module) {

var TokenIterator = require("ace/token_iterator").TokenIterator;

function BracketMatch() {

    this.findMatchingBracket = function(position) {
        if (position.column == 0) return null;

        var charBeforeCursor = this.getLine(position.row).charAt(position.column-1);
        if (charBeforeCursor == "") return null;

        var match = charBeforeCursor.match(/([\(\[\{])|([\)\]\}])/);
        if (!match) {
            return null;
        }

        if (match[1]) {
            return this.$findClosingBracket(match[1], position);
        } else {
            return this.$findOpeningBracket(match[2], position);
        }
    };

    this.$brackets = {
        ")": "(",
        "(": ")",
        "]": "[",
        "[": "]",
        "{": "}",
        "}": "{"
    };

    this.$findOpeningBracket = function(bracket, position) {
        var openBracket = this.$brackets[bracket];
        var depth = 1;

        var iterator = new TokenIterator(this, position.row, position.column);
        var token = iterator.getCurrentToken();
        if (!token) return null;

        // token.type contains a period-delimited list of token identifiers
        // (e.g.: "constant.numeric" or "paren.lparen").  Create a pattern that
        // matches any token containing the same identifiers or a subset.  In
        // addition, if token.type includes "rparen", then also match "lparen".
        // So if type.token is "paren.rparen", then typeRe will match "lparen.paren".
        var typeRe = new RegExp("(\\.?[" +
            token.type.replace(".", "|").replace("rparen", "lparen|rparen") + "])+");

        // Start searching in token, just before the character at position.column
        var valueIndex = position.column - iterator.getCurrentTokenColumn() - 2;
        var value = token.value;

        while (true) {

            while (valueIndex >= 0) {
                var char = value.charAt(valueIndex);
                if (char == openBracket) {
                    depth -= 1;
                    if (depth == 0) {
                        return {row: iterator.getCurrentTokenRow(),
                            column: valueIndex + iterator.getCurrentTokenColumn()};
                    }
                }
                else if (char == bracket) {
                    depth += 1;
                }
                valueIndex -= 1;
            }

            // Scan backward through the document, looking for the next token
            // whose type matches typeRe
            do {
                token = iterator.stepBackward();
            } while (token && !typeRe.test(token.type));

            if (token == null)
                break;

            value = token.value;
            valueIndex = value.length - 1;
        }

        return null;
    };

    this.$findClosingBracket = function(bracket, position) {
        var closingBracket = this.$brackets[bracket];
        var depth = 1;

        var iterator = new TokenIterator(this, position.row, position.column);
        var token = iterator.getCurrentToken();
        if (!token) return null;

        // token.type contains a period-delimited list of token identifiers
        // (e.g.: "constant.numeric" or "paren.lparen").  Create a pattern that
        // matches any token containing the same identifiers or a subset.  In
        // addition, if token.type includes "lparen", then also match "rparen".
        // So if type.token is "lparen.paren", then typeRe will match "paren.rparen".
        var typeRe = new RegExp("(\\.?[" +
            token.type.replace(".", "|").replace("lparen", "lparen|rparen") + "])+");

        // Start searching in token, after the character at position.column
        var valueIndex = position.column - iterator.getCurrentTokenColumn();

        while (true) {

            var value = token.value;
            var valueLength = value.length;
            while (valueIndex < valueLength) {
                var char = value.charAt(valueIndex);
                if (char == closingBracket) {
                    depth -= 1;
                    if (depth == 0) {
                        return {row: iterator.getCurrentTokenRow(),
                            column: valueIndex + iterator.getCurrentTokenColumn()};
                    }
                }
                else if (char == bracket) {
                    depth += 1;
                }
                valueIndex += 1;
            }

            // Scan forward through the document, looking for the next token
            // whose type matches typeRe
            do {
                token = iterator.stepForward();
            } while (token && !typeRe.test(token.type));

            if (token == null)
                break;

            valueIndex = 0;
        }

        return null;
    };
}
exports.BracketMatch = BracketMatch;

});
<commit_msg>Changed the typeRe regular expression to be more strict.<commit_after>/* vim:ts=4:sts=4:sw=4:
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Ajax.org Code Editor (ACE).
 *
 * The Initial Developer of the Original Code is
 * Ajax.org B.V.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *      Fabian Jakobs <fabian AT ajax DOT org>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

define(function(require, exports, module) {

var TokenIterator = require("ace/token_iterator").TokenIterator;

function BracketMatch() {

    this.findMatchingBracket = function(position) {
        if (position.column == 0) return null;

        var charBeforeCursor = this.getLine(position.row).charAt(position.column-1);
        if (charBeforeCursor == "") return null;

        var match = charBeforeCursor.match(/([\(\[\{])|([\)\]\}])/);
        if (!match) {
            return null;
        }

        if (match[1]) {
            return this.$findClosingBracket(match[1], position);
        } else {
            return this.$findOpeningBracket(match[2], position);
        }
    };

    this.$brackets = {
        ")": "(",
        "(": ")",
        "]": "[",
        "[": "]",
        "{": "}",
        "}": "{"
    };

    this.$findOpeningBracket = function(bracket, position) {
        var openBracket = this.$brackets[bracket];
        var depth = 1;

        var iterator = new TokenIterator(this, position.row, position.column);
        var token = iterator.getCurrentToken();
        if (!token) return null;

        // token.type contains a period-delimited list of token identifiers
        // (e.g.: "constant.numeric" or "paren.lparen").  Create a pattern that
        // matches any token containing the same identifiers or a subset.  In
        // addition, if token.type includes "rparen", then also match "lparen".
        // So if type.token is "paren.rparen", then typeRe will match "lparen.paren".
        var typeRe = new RegExp("(\\.?" +
            token.type.replace(".", "|").replace("rparen", "lparen|rparen") + ")+");

        // Start searching in token, just before the character at position.column
        var valueIndex = position.column - iterator.getCurrentTokenColumn() - 2;
        var value = token.value;

        while (true) {

            while (valueIndex >= 0) {
                var char = value.charAt(valueIndex);
                if (char == openBracket) {
                    depth -= 1;
                    if (depth == 0) {
                        return {row: iterator.getCurrentTokenRow(),
                            column: valueIndex + iterator.getCurrentTokenColumn()};
                    }
                }
                else if (char == bracket) {
                    depth += 1;
                }
                valueIndex -= 1;
            }

            // Scan backward through the document, looking for the next token
            // whose type matches typeRe
            do {
                token = iterator.stepBackward();
            } while (token && !typeRe.test(token.type));

            if (token == null)
                break;

            value = token.value;
            valueIndex = value.length - 1;
        }

        return null;
    };

    this.$findClosingBracket = function(bracket, position) {
        var closingBracket = this.$brackets[bracket];
        var depth = 1;

        var iterator = new TokenIterator(this, position.row, position.column);
        var token = iterator.getCurrentToken();
        if (!token) return null;

        // token.type contains a period-delimited list of token identifiers
        // (e.g.: "constant.numeric" or "paren.lparen").  Create a pattern that
        // matches any token containing the same identifiers or a subset.  In
        // addition, if token.type includes "lparen", then also match "rparen".
        // So if type.token is "lparen.paren", then typeRe will match "paren.rparen".
        var typeRe = new RegExp("(\\.?" +
            token.type.replace(".", "|").replace("lparen", "lparen|rparen") + ")+");

        // Start searching in token, after the character at position.column
        var valueIndex = position.column - iterator.getCurrentTokenColumn();

        while (true) {

            var value = token.value;
            var valueLength = value.length;
            while (valueIndex < valueLength) {
                var char = value.charAt(valueIndex);
                if (char == closingBracket) {
                    depth -= 1;
                    if (depth == 0) {
                        return {row: iterator.getCurrentTokenRow(),
                            column: valueIndex + iterator.getCurrentTokenColumn()};
                    }
                }
                else if (char == bracket) {
                    depth += 1;
                }
                valueIndex += 1;
            }

            // Scan forward through the document, looking for the next token
            // whose type matches typeRe
            do {
                token = iterator.stepForward();
            } while (token && !typeRe.test(token.type));

            if (token == null)
                break;

            valueIndex = 0;
        }

        return null;
    };
}
exports.BracketMatch = BracketMatch;

});
<|endoftext|>


<commit_before>var step = require('step');
var assert = require('assert');
var _ = require('underscore');
var NamedMapsCacheEntry = require('../cache/model/named_maps_entry');

var cors = require('../middleware/cors');
var userMiddleware = require('../middleware/user');
var allowQueryParams = require('../middleware/allow-query-params');
var vectorError = require('../middleware/vector-error');

function NamedMapsController(prepareContext, namedMapProviderCache, tileBackend, previewBackend,
                             surrogateKeysCache, tablesExtentApi, metadataBackend) {
    this.namedMapProviderCache = namedMapProviderCache;
    this.tileBackend = tileBackend;
    this.previewBackend = previewBackend;
    this.surrogateKeysCache = surrogateKeysCache;
    this.tablesExtentApi = tablesExtentApi;
    this.metadataBackend = metadataBackend;
    this.prepareContext = prepareContext;
}

module.exports = NamedMapsController;

NamedMapsController.prototype.register = function(app) {
    app.get(
        app.base_url_templated + '/:template_id/:layer/:z/:x/:y.(:format)',
        cors(),
        userMiddleware,
        this.prepareContext,
        this.getNamedMapProvider(),
        this.tile.bind(this),
        vectorError()
    );

    app.get(
        app.base_url_mapconfig + '/static/named/:template_id/:width/:height.:format',
        cors(),
        userMiddleware,
        allowQueryParams(['layer', 'zoom', 'lon', 'lat', 'bbox']),
        this.prepareContext,
        this.getNamedMapProvider(),
        this.prepareLayerFilterFromPreviewLayers(),
        this.getStaticImageOptions(),
        this.getImage(),
        this.incrementMapViews(),
        this.handleImage()
    );
};

NamedMapsController.prototype.getNamedMapProvider = function () {
    return function getNamedMapProviderMiddleware (req, res, next) {
        const { user } = res.locals;
        const { config, auth_token } = req.query;
        const { template_id } = req.params;

        this.namedMapProviderCache.get(user, template_id, config, auth_token, res.locals, (err, namedMapProvider) => {
            if (err) {
                return next(err);
            }

            res.locals.namedMapProvider = namedMapProvider;

            next();
        });
    }.bind(this);
};

NamedMapsController.prototype.prepareLayerFilterFromPreviewLayers = function () {
    return function prepareLayerFilterFromPreviewLayersMiddleware (req, res, next) {
        const { user, namedMapProvider } = res.locals;
        const { template_id } = req.params;
        const { config, auth_token } = req.query;

        namedMapProvider.getTemplate((err, template) => {
            if (err) {
                return next(err);
            }

            if (!template || !template.view || !template.view.preview_layers) {
               return next();
            }

            var previewLayers = template.view.preview_layers;
            var layerVisibilityFilter = [];

            template.layergroup.layers.forEach(function (layer, index) {
                if (previewLayers[''+index] !== false && previewLayers[layer.id] !== false) {
                    layerVisibilityFilter.push(''+index);
                }
            });

            if (!layerVisibilityFilter.length) {
                return next();
            }

            // overwrites 'all' default filter
            res.locals.layer = layerVisibilityFilter.join(',');

            // recreates the provider
            this.namedMapProviderCache.get(user, template_id, config, auth_token, res.locals, (err, provider) => {
                if (err) {
                    return next(err);
                }

                res.locals.namedMapProvider = provider;

                next();
            });
        });
    }.bind(this);
};

NamedMapsController.prototype.sendResponse = function(req, res, body, headers, namedMapProvider) {
    this.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(res.locals.user, namedMapProvider.getTemplateName()));
    res.set('Content-Type', headers['content-type'] || headers['Content-Type'] || 'image/png');
    res.set('Cache-Control', 'public,max-age=7200,must-revalidate');

    var self = this;

    step(
        function getAffectedTablesAndLastUpdatedTime() {
            namedMapProvider.getAffectedTablesAndLastUpdatedTime(this);
        },
        function sendResponse(err, result) {
            req.profiler.done('affectedTables');
            if (err) {
                global.logger.log('ERROR generating cache channel: ' + err);
            }
            if (!result || !!result.tables) {
                // we increase cache control as we can invalidate it
                res.set('Cache-Control', 'public,max-age=31536000');

                var lastModifiedDate;
                if (Number.isFinite(result.lastUpdatedTime)) {
                    lastModifiedDate = new Date(result.getLastUpdatedAt());
                } else {
                    lastModifiedDate = new Date();
                }
                res.set('Last-Modified', lastModifiedDate.toUTCString());

                res.set('X-Cache-Channel', result.getCacheChannel());
                if (result.tables.length > 0) {
                    self.surrogateKeysCache.tag(res, result);
                }
            }
            res.status(200);
            res.send(body);
        }
    );
};

NamedMapsController.prototype.tile = function(req, res, next) {
    const { namedMapProvider } = res.locals;

    this.tileBackend.getTile(namedMapProvider, req.params, (err, tile, headers, stats) => {
        req.profiler.add(stats);

        if (err) {
            err.label = 'NAMED_MAP_TILE';
            return next(err);
        }

        this.sendResponse(req, res, tile, headers, namedMapProvider);
    });
};

var DEFAULT_ZOOM_CENTER = {
    zoom: 1,
    center: {
        lng: 0,
        lat: 0
    }
};

function numMapper(n) {
    return +n;
}

NamedMapsController.prototype.getStaticImageOptions = function () {
    return function getStaticImageOptionsMiddleware(req, res, next) {
        var self = this;

        const { user, namedMapProvider, zoom, lon, lat, bbox } = res.locals;

        if ([zoom, lon, lat].map(numMapper).every(Number.isFinite)) {
            res.locals.imageOpts = {
                zoom: zoom,
                center: {
                    lng: lon,
                    lat: lat
                }
            };

            return next();
        }

        if (bbox) {
            var _bbox = bbox.split(',').map(numMapper);
            if (_bbox.length === 4 && _bbox.every(Number.isFinite)) {
                res.locals.imageOpts = {
                    bounds: {
                        west: _bbox[0],
                        south: _bbox[1],
                        east: _bbox[2],
                        north: _bbox[3]
                    }
                };

                return next();
            }
        }

        step(
            function getTemplate() {
                namedMapProvider.getTemplate(this);
            },
            function handleTemplateView(err, template) {
                assert.ifError(err);

                if (template.view) {
                    var zoomCenter = templateZoomCenter(template.view);
                    if (zoomCenter) {
                        if (Number.isFinite(+zoom)) {
                            zoomCenter.zoom = +zoom;
                        }
                        return zoomCenter;
                    }

                    var bounds = templateBounds(template.view);
                    if (bounds) {
                        return bounds;
                    }
                }

                return false;
            },
            function estimateBoundsIfNoImageOpts(err, imageOpts) {
                if (imageOpts) {
                    return imageOpts;
                }

                var _next = this;
                namedMapProvider.getAffectedTablesAndLastUpdatedTime(function(err, affectedTablesAndLastUpdate) {
                    if (err) {
                        return _next(null);
                    }

                    var affectedTables = affectedTablesAndLastUpdate.tables || [];

                    if (affectedTables.length === 0) {
                        return _next(null);
                    }

                    self.tablesExtentApi.getBounds(user, affectedTables, function (err, result) {
                        return _next(null, result);
                    });
                });

            },
            function returnCallback(err, imageOpts) {
                res.locals.imageOpts = imageOpts || DEFAULT_ZOOM_CENTER;
                return next();
            }
        );
    }.bind(this);
};

NamedMapsController.prototype.getImage = function () {
    return function getImageMiddleware (req, res, next) {
        const { imageOpts, namedMapProvider } = res.locals;
        const { zoom, center, bounds } = imageOpts;

        let { width, height } = req.params;

        width = +width;
        height = +height;

        const format = req.params.format === 'jpg' ? 'jpeg' : 'png';
        // We force always the tile to be generated using PNG because
        // is the only format we support by now
        res.locals.format = 'png';
        res.locals.layer = res.locals.layer || 'all';

        if (!_.isUndefined(zoom) && center) {
            return this.previewBackend.getImage(namedMapProvider, format, width, height, zoom, center,
                (err, image, headers, stats) => {
                if (err) {
                    return next(err);
                }

                res.locals.image = image;
                res.locals.headers = headers;
                res.locals.stats = stats;

                next();
            });
        }

        this.previewBackend.getImage(namedMapProvider, format, width, height, bounds, (err, image, headers, stats) => {
            if (err) {
                return next(err);
            }

            res.locals.image = image;
            res.locals.headers = headers;
            res.locals.stats = stats;

            next();
        });
    }.bind(this);
};

const incrementMapViewsError = ctx => `ERROR: failed to increment mapview count for user '${ctx.user}': ${ctx.err}`;

NamedMapsController.prototype.incrementMapViews = function () {
    return function incrementMapViewsMiddleware(req, res, next) {
        const { user, namedMapProvider } = res.locals;

        namedMapProvider.getMapConfig((err, mapConfig) => {
            if (err) {
                global.logger.log(incrementMapViewsError({ user, err }));
                return next();
            }

            const statTag = mapConfig.obj().stat_tag;

            this.metadataBackend.incMapviewCount(user, statTag, (err) => {
                if (err) {
                    global.logger.log(incrementMapViewsError({ user, err }));
                }

                next();
            });
        });
    }.bind(this);
};

NamedMapsController.prototype.handleImage = function () {
    return function handleImageMiddleware (req, res) {
        const { namedMapProvider, image, headers, stats = {}, format } = res.locals;

        req.profiler.done('render-' + format);
        req.profiler.add(stats);

        this.sendResponse(req, res, image, headers, namedMapProvider);
    }.bind(this);
};

function templateZoomCenter(view) {
    if (!_.isUndefined(view.zoom) && view.center) {
        return {
            zoom: view.zoom,
            center: view.center
        };
    }
    return false;
}

function templateBounds(view) {
    if (view.bounds) {
        var hasAllBounds = _.every(['west', 'south', 'east', 'north'], function(prop) {
            return Number.isFinite(view.bounds[prop]);
        });
        if (hasAllBounds) {
            return {
                bounds: {
                    west: view.bounds.west,
                    south: view.bounds.south,
                    east: view.bounds.east,
                    north: view.bounds.north
                }
            };
        } else {
            return false;
        }
    }
    return false;
}
<commit_msg>Keep error label<commit_after>var step = require('step');
var assert = require('assert');
var _ = require('underscore');
var NamedMapsCacheEntry = require('../cache/model/named_maps_entry');

var cors = require('../middleware/cors');
var userMiddleware = require('../middleware/user');
var allowQueryParams = require('../middleware/allow-query-params');
var vectorError = require('../middleware/vector-error');

function NamedMapsController(prepareContext, namedMapProviderCache, tileBackend, previewBackend,
                             surrogateKeysCache, tablesExtentApi, metadataBackend) {
    this.namedMapProviderCache = namedMapProviderCache;
    this.tileBackend = tileBackend;
    this.previewBackend = previewBackend;
    this.surrogateKeysCache = surrogateKeysCache;
    this.tablesExtentApi = tablesExtentApi;
    this.metadataBackend = metadataBackend;
    this.prepareContext = prepareContext;
}

module.exports = NamedMapsController;

NamedMapsController.prototype.register = function(app) {
    app.get(
        app.base_url_templated + '/:template_id/:layer/:z/:x/:y.(:format)',
        cors(),
        userMiddleware,
        this.prepareContext,
        this.getNamedMapProvider(),
        this.tile.bind(this),
        vectorError()
    );

    app.get(
        app.base_url_mapconfig + '/static/named/:template_id/:width/:height.:format',
        cors(),
        userMiddleware,
        allowQueryParams(['layer', 'zoom', 'lon', 'lat', 'bbox']),
        this.prepareContext,
        this.getNamedMapProvider('STATIC_VIZ_MAP'),
        this.prepareLayerFilterFromPreviewLayers('STATIC_VIZ_MAP'),
        this.getStaticImageOptions(),
        this.getImage('STATIC_VIZ_MAP'),
        this.incrementMapViews(),
        this.handleImage()
    );
};

NamedMapsController.prototype.getNamedMapProvider = function (label) {
    return function getNamedMapProviderMiddleware (req, res, next) {
        const { user } = res.locals;
        const { config, auth_token } = req.query;
        const { template_id } = req.params;

        this.namedMapProviderCache.get(user, template_id, config, auth_token, res.locals, (err, namedMapProvider) => {
            if (err) {
                err.label = label;
                return next(err);
            }

            res.locals.namedMapProvider = namedMapProvider;

            next();
        });
    }.bind(this);
};

NamedMapsController.prototype.prepareLayerFilterFromPreviewLayers = function (label) {
    return function prepareLayerFilterFromPreviewLayersMiddleware (req, res, next) {
        const { user, namedMapProvider } = res.locals;
        const { template_id } = req.params;
        const { config, auth_token } = req.query;

        namedMapProvider.getTemplate((err, template) => {
            if (err) {
                err.label = label;
                return next(err);
            }

            if (!template || !template.view || !template.view.preview_layers) {
               return next();
            }

            var previewLayers = template.view.preview_layers;
            var layerVisibilityFilter = [];

            template.layergroup.layers.forEach(function (layer, index) {
                if (previewLayers[''+index] !== false && previewLayers[layer.id] !== false) {
                    layerVisibilityFilter.push(''+index);
                }
            });

            if (!layerVisibilityFilter.length) {
                return next();
            }

            // overwrites 'all' default filter
            res.locals.layer = layerVisibilityFilter.join(',');

            // recreates the provider
            this.namedMapProviderCache.get(user, template_id, config, auth_token, res.locals, (err, provider) => {
                if (err) {
                    err.label = label;
                    return next(err);
                }

                res.locals.namedMapProvider = provider;

                next();
            });
        });
    }.bind(this);
};

NamedMapsController.prototype.sendResponse = function(req, res, body, headers, namedMapProvider) {
    this.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(res.locals.user, namedMapProvider.getTemplateName()));
    res.set('Content-Type', headers['content-type'] || headers['Content-Type'] || 'image/png');
    res.set('Cache-Control', 'public,max-age=7200,must-revalidate');

    var self = this;

    step(
        function getAffectedTablesAndLastUpdatedTime() {
            namedMapProvider.getAffectedTablesAndLastUpdatedTime(this);
        },
        function sendResponse(err, result) {
            req.profiler.done('affectedTables');
            if (err) {
                global.logger.log('ERROR generating cache channel: ' + err);
            }
            if (!result || !!result.tables) {
                // we increase cache control as we can invalidate it
                res.set('Cache-Control', 'public,max-age=31536000');

                var lastModifiedDate;
                if (Number.isFinite(result.lastUpdatedTime)) {
                    lastModifiedDate = new Date(result.getLastUpdatedAt());
                } else {
                    lastModifiedDate = new Date();
                }
                res.set('Last-Modified', lastModifiedDate.toUTCString());

                res.set('X-Cache-Channel', result.getCacheChannel());
                if (result.tables.length > 0) {
                    self.surrogateKeysCache.tag(res, result);
                }
            }
            res.status(200);
            res.send(body);
        }
    );
};

NamedMapsController.prototype.tile = function(req, res, next) {
    const { namedMapProvider } = res.locals;

    this.tileBackend.getTile(namedMapProvider, req.params, (err, tile, headers, stats) => {
        req.profiler.add(stats);

        if (err) {
            err.label = 'NAMED_MAP_TILE';
            return next(err);
        }

        this.sendResponse(req, res, tile, headers, namedMapProvider);
    });
};

var DEFAULT_ZOOM_CENTER = {
    zoom: 1,
    center: {
        lng: 0,
        lat: 0
    }
};

function numMapper(n) {
    return +n;
}

NamedMapsController.prototype.getStaticImageOptions = function () {
    return function getStaticImageOptionsMiddleware(req, res, next) {
        var self = this;

        const { user, namedMapProvider, zoom, lon, lat, bbox } = res.locals;

        if ([zoom, lon, lat].map(numMapper).every(Number.isFinite)) {
            res.locals.imageOpts = {
                zoom: zoom,
                center: {
                    lng: lon,
                    lat: lat
                }
            };

            return next();
        }

        if (bbox) {
            var _bbox = bbox.split(',').map(numMapper);
            if (_bbox.length === 4 && _bbox.every(Number.isFinite)) {
                res.locals.imageOpts = {
                    bounds: {
                        west: _bbox[0],
                        south: _bbox[1],
                        east: _bbox[2],
                        north: _bbox[3]
                    }
                };

                return next();
            }
        }

        step(
            function getTemplate() {
                namedMapProvider.getTemplate(this);
            },
            function handleTemplateView(err, template) {
                assert.ifError(err);

                if (template.view) {
                    var zoomCenter = templateZoomCenter(template.view);
                    if (zoomCenter) {
                        if (Number.isFinite(+zoom)) {
                            zoomCenter.zoom = +zoom;
                        }
                        return zoomCenter;
                    }

                    var bounds = templateBounds(template.view);
                    if (bounds) {
                        return bounds;
                    }
                }

                return false;
            },
            function estimateBoundsIfNoImageOpts(err, imageOpts) {
                if (imageOpts) {
                    return imageOpts;
                }

                var _next = this;
                namedMapProvider.getAffectedTablesAndLastUpdatedTime(function(err, affectedTablesAndLastUpdate) {
                    if (err) {
                        return _next(null);
                    }

                    var affectedTables = affectedTablesAndLastUpdate.tables || [];

                    if (affectedTables.length === 0) {
                        return _next(null);
                    }

                    self.tablesExtentApi.getBounds(user, affectedTables, function (err, result) {
                        return _next(null, result);
                    });
                });

            },
            function returnCallback(err, imageOpts) {
                res.locals.imageOpts = imageOpts || DEFAULT_ZOOM_CENTER;
                return next();
            }
        );
    }.bind(this);
};

NamedMapsController.prototype.getImage = function (label) {
    return function getImageMiddleware (req, res, next) {
        const { imageOpts, namedMapProvider } = res.locals;
        const { zoom, center, bounds } = imageOpts;

        let { width, height } = req.params;

        width = +width;
        height = +height;

        const format = req.params.format === 'jpg' ? 'jpeg' : 'png';
        // We force always the tile to be generated using PNG because
        // is the only format we support by now
        res.locals.format = 'png';
        res.locals.layer = res.locals.layer || 'all';

        if (!_.isUndefined(zoom) && center) {
            return this.previewBackend.getImage(namedMapProvider, format, width, height, zoom, center,
                (err, image, headers, stats) => {
                if (err) {
                    err.label = label;
                    return next(err);
                }

                res.locals.image = image;
                res.locals.headers = headers;
                res.locals.stats = stats;

                next();
            });
        }

        this.previewBackend.getImage(namedMapProvider, format, width, height, bounds, (err, image, headers, stats) => {
            if (err) {
                err.label = label;
                return next(err);
            }

            res.locals.image = image;
            res.locals.headers = headers;
            res.locals.stats = stats;

            next();
        });
    }.bind(this);
};

const incrementMapViewsError = ctx => `ERROR: failed to increment mapview count for user '${ctx.user}': ${ctx.err}`;

NamedMapsController.prototype.incrementMapViews = function () {
    return function incrementMapViewsMiddleware(req, res, next) {
        const { user, namedMapProvider } = res.locals;

        namedMapProvider.getMapConfig((err, mapConfig) => {
            if (err) {
                global.logger.log(incrementMapViewsError({ user, err }));
                return next();
            }

            const statTag = mapConfig.obj().stat_tag;

            this.metadataBackend.incMapviewCount(user, statTag, (err) => {
                if (err) {
                    global.logger.log(incrementMapViewsError({ user, err }));
                }

                next();
            });
        });
    }.bind(this);
};

NamedMapsController.prototype.handleImage = function () {
    return function handleImageMiddleware (req, res) {
        const { namedMapProvider, image, headers, stats = {}, format } = res.locals;

        req.profiler.done('render-' + format);
        req.profiler.add(stats);

        this.sendResponse(req, res, image, headers, namedMapProvider);
    }.bind(this);
};

function templateZoomCenter(view) {
    if (!_.isUndefined(view.zoom) && view.center) {
        return {
            zoom: view.zoom,
            center: view.center
        };
    }
    return false;
}

function templateBounds(view) {
    if (view.bounds) {
        var hasAllBounds = _.every(['west', 'south', 'east', 'north'], function(prop) {
            return Number.isFinite(view.bounds[prop]);
        });
        if (hasAllBounds) {
            return {
                bounds: {
                    west: view.bounds.west,
                    south: view.bounds.south,
                    east: view.bounds.east,
                    north: view.bounds.north
                }
            };
        } else {
            return false;
        }
    }
    return false;
}
<|endoftext|>


<commit_before>'use strict';
// Copyright (C) 2015 SignalFx, Inc. All rights reserved.

var request = require('request'); // Use for create post request
var winston = require('winston'); // Logging library
var Promise = require('promise');

var conf = require('../conf');

// Application version and name for User-Agent
var name = 'signalfx-nodejs-client';
var version = require('../../../package.json').version;

/**
 * SignalFx API client.
 * This class presents a programmatic interface to SignalFx's metadata and
 * ingest APIs. At the time being, only ingest is supported; more will come
 * later.
 *
 * @constructor
 * @param apiToken
 * @param options - {
 *                    enableAmazonUniqueId: boolean, // "false by default"
 *                    dimensions:"object", // dimensions for each datapoint and event
 *                    ingestEndpoint:"string",
 *                    timeout:"number",
 *                    batchSize:"number",
 *                    userAgents:"array",
 *                    proxy:"string" //http://<USER>:<PASSWORD>@<HOST>:<PORT>
 *                  }
 */
function SignalFxClient(apiToken, options) {
  var _this = this;

  this.apiToken = apiToken;
  var params = options || {};

  this.ingestEndpoint = params.ingestEndpoint || conf.DEFAULT_INGEST_ENDPOINT;
  this.timeout = params.timeout || conf.DEFAULT_TIMEOUT;
  this.batchSize = Math.max(1, (params.batchSize ? params.batchSize : conf.DEFAULT_BATCH_SIZE));
  this.userAgents = params.userAgents || null;
  this.globalDimensions = params.dimensions || {};
  this.enableAmazonUniqueId = params.enableAmazonUniqueId || false;
  this.proxy = params.proxy || null;

  this.rawData = [];
  this.rawEvents = [];
  this.queue = [];

  this.loadAWSUniqueId = new Promise(function (resolve) {
    if (!_this.enableAmazonUniqueId) {
      resolve();
      return;
    } else {
      if (_this.AWSUniqueId !== undefined && _this.AWSUniqueId !== '') {
        resolve();
        return;
      }
    }

    _this._retrieveAWSUniqueId(function (isSuccess, AWSUniqueId) {
      if (isSuccess) {
        _this.AWSUniqueId = AWSUniqueId;
        _this.globalDimensions[_this.AWSUniqueId_DIMENTION_NAME] = AWSUniqueId;
      } else {
        _this.enableAmazonUniqueId = false;
        _this.AWSUniqueId = '';
        delete _this.globalDimensions[_this.AWSUniqueId_DIMENTION_NAME];
      }
      resolve();
    });
  });
}

SignalFxClient.prototype.AWSUniqueId_DIMENTION_NAME = 'AWSUniqueId';
SignalFxClient.prototype.HEADER_API_TOKEN_KEY = 'X-SF-Token';
SignalFxClient.prototype.HEADER_USER_AGENT_KEY = 'User-Agent';
SignalFxClient.prototype.HEADER_CONTENT_TYPE = 'Content-Type';
SignalFxClient.prototype.INGEST_ENDPOINT_SUFFIX = 'v2/datapoint';
SignalFxClient.prototype.EVENT_ENDPOINT_SUFFIX = 'v2/event';

SignalFxClient.prototype.EVENT_CATEGORIES = {
  USER_DEFINED: 'USER_DEFINED',
  ALERT: 'ALERT',
  AUDIT: 'AUDIT',
  JOB: 'JOB',
  COLLECTD: 'COLLECTD',
  SERVICE_DISCOVERY: 'SERVICE_DISCOVERY',
  EXCEPTION: 'EXCEPTION'
};

/**
 * Send the given metrics to SignalFx.
 *
 * @param data - param object with following fields:
 *    'cumulative_counters' (list): a list of dictionaries representing the cumulative counters to report.
 *    'gauges' (list): a list of dictionaries representing the gauges to report.
 *    'counters' (list): a list of dictionaries representing the counters to report.
 */
SignalFxClient.prototype.send = function (data) {
  var _this = this;
  this.rawData.push(data);
  return this.loadAWSUniqueId
    .then(function () {
      _this.processingData();
      return _this.startAsyncSend();
    });
};

SignalFxClient.prototype.processingData = function () {
  var _this = this;

  function processDataPoints(metricType, dataPoints) {
    if (!dataPoints) {
      return;
    }

    if (!Array.isArray(dataPoints)) {
      throw new TypeError('DataPoints not of type list: ' + dataPoints);
    }

    dataPoints.forEach(function (dp) {
      var datapoint = dp;
      if (!datapoint.dimensions) {
        datapoint.dimensions = {};
      }
      for (var key in _this.globalDimensions) {
        if (_this.globalDimensions.hasOwnProperty(key)) {
          datapoint.dimensions[key] = _this.globalDimensions[key];
        }
      }

      _this._addToQueue(metricType, datapoint);
    });
  }

  while (this.rawData.length) {
    var data = this.rawData.pop();
    if (data) {
      processDataPoints('cumulative_counter', data.cumulative_counters);
      processDataPoints('gauge', data.gauges);
      processDataPoints('counter', data.counters);
    }
  }
};

SignalFxClient.prototype._addToQueue = function (metricType, datapoint) {
  throw new Error('Subclasses should implement this!');
};

SignalFxClient.prototype._batchData = function (datapointsList) {
  throw new Error('Subclasses should implement this!');
};

/**
 * Send an event to SignalFx
 *
 * @param event - param object with following fields:
 *    category (string) - the category of event. Choose one from EVENT_CATEGORIES list
 *    eventType (string) - the event type (name of the event time series).
 *    dimensions  (dict) - a map of event dimensions, empty dictionary by default
 *    properties  (dict) - a map of extra properties on that event, empty dictionary by default
 *    timestamp (int64) - a timestamp, by default is current time
 *
 * Only eventType field is required
 */
SignalFxClient.prototype.sendEvent = function (event) {
  var _this = this;
  if (!event || !event.eventType) {
    throw new Error('Type of event should not be empty!');
  }
  // Check than passed event category is supported
  if (!this.EVENT_CATEGORIES[event.category]) {
    throw new Error('Unsupported event category: ' + event.category);
  }

  var data = {
    category: event.category || _this.EVENT_CATEGORIES.USER_DEFINED,
    eventType: event.eventType,
    dimensions: event.dimensions || {},
    properties: event.properties || {},
    timestamp: event.timestamp || (new Date()).getTime()
  };

  this.rawEvents.push(data);
  return this.loadAWSUniqueId.then(function () {
    return _this.startAsyncEventSend();
  });
};

SignalFxClient.prototype._retrieveAWSUniqueId = function (callback) {
  var getOptions = {
    url: conf.AWS_UNIQUE_ID_URL,
    timeout: 1000,
    method: 'GET',
    proxy: this.proxy
  };

  request(getOptions, function (error, response, body) {
    if (error || response.statusCode !== 200) {
      callback(false, '');
      winston.error('Failed to retrieve AWS unique ID: ', response ? response.statusCode : '', body);
    } else {
      try {
        var jsonBody = JSON.parse(body);
        if (jsonBody && jsonBody.instanceId && jsonBody.region && jsonBody.accountId) {
          var AWSUniqueId = jsonBody.instanceId + '_' + jsonBody.region + '_' + jsonBody.accountId;
          callback(true, AWSUniqueId);
        } else {
          callback(false, '');
        }
      } catch (e) {
        callback(false, '');
      }
    }
  });
};

SignalFxClient.prototype.getHeaderContentType = function () {
  throw new Error('Subclasses should implement this!');
};

SignalFxClient.prototype._buildEvent = function (event) {
  throw new Error('Subclasses should implement this!');
};

SignalFxClient.prototype._encodeEvent = function (event) {
  throw new Error('Subclasses should implement this!');
};

SignalFxClient.prototype.startAsyncSend = function () {
  var _this = this;
  // Send post request in separate thread
  var datapointsList = [];
  while (_this.queue.length !== 0 && datapointsList.length < _this.batchSize) {
    datapointsList.push(_this.queue.shift());
  }

  if (datapointsList.length > 0) {
    var dataToSend = _this._batchData(datapointsList);
    if (dataToSend && dataToSend.length > 0) {
      var url = _this.ingestEndpoint + '/' + _this.INGEST_ENDPOINT_SUFFIX;
      return _this.post(dataToSend, url, _this.getHeaderContentType());
    }
  }
  return new Promise(function (resolve) {
    resolve(null);
  });
};

SignalFxClient.prototype.startAsyncEventSend = function () {
  var _this = this;
  while (this.rawEvents.length) {
    var data = this.rawEvents.pop();
    for (var key in this.globalDimensions) {
      if (this.globalDimensions.hasOwnProperty(key)) {
        data.dimensions[key] = this.globalDimensions[key];
      }
    }

    try {
      var eventToSend = _this._buildEvent(data);
      if (eventToSend) {
        var url = this.ingestEndpoint + '/' + this.EVENT_ENDPOINT_SUFFIX;
        return this.post(_this._encodeEvent(eventToSend), url, _this.getHeaderContentType());
      }
    } catch (error) {
      winston.error('Can\'t process event: ', error);
    }
  }
};

SignalFxClient.prototype.post = function (data, postUrl, contentType) {
  var _this = this;

  var headers = {};
  headers[_this.HEADER_USER_AGENT_KEY] = name + '/' + version;
  //Adding custom user agents passed by client modules
  if (_this.userAgents) {
    headers[_this.HEADER_USER_AGENT_KEY] += ',' + _this.userAgents;
  }
  headers[_this.HEADER_API_TOKEN_KEY] = _this.apiToken;
  headers[_this.HEADER_CONTENT_TYPE] = contentType;

  // An object of options to indicate where to post to
  var postOptions = {
    url: postUrl,
    timeout: _this.timeout,
    headers: headers,
    body: data,
    method: 'POST',
    proxy: _this.proxy
  };

  return new Promise(function (resolve) {
    request(postOptions, function (error, response, body) {
      if (error || response.statusCode !== 200) {
        winston.error('Failed to send datapoint: ', response ? response.statusCode : '', body, error);
        resolve(error);
      }
      resolve(body);
    });
  });
};

exports.SignalFxClient = SignalFxClient;
<commit_msg>reject instead of resolve<commit_after>'use strict';
// Copyright (C) 2015 SignalFx, Inc. All rights reserved.

var request = require('request'); // Use for create post request
var winston = require('winston'); // Logging library
var Promise = require('promise');

var conf = require('../conf');

// Application version and name for User-Agent
var name = 'signalfx-nodejs-client';
var version = require('../../../package.json').version;

/**
 * SignalFx API client.
 * This class presents a programmatic interface to SignalFx's metadata and
 * ingest APIs. At the time being, only ingest is supported; more will come
 * later.
 *
 * @constructor
 * @param apiToken
 * @param options - {
 *                    enableAmazonUniqueId: boolean, // "false by default"
 *                    dimensions:"object", // dimensions for each datapoint and event
 *                    ingestEndpoint:"string",
 *                    timeout:"number",
 *                    batchSize:"number",
 *                    userAgents:"array",
 *                    proxy:"string" //http://<USER>:<PASSWORD>@<HOST>:<PORT>
 *                  }
 */
function SignalFxClient(apiToken, options) {
  var _this = this;

  this.apiToken = apiToken;
  var params = options || {};

  this.ingestEndpoint = params.ingestEndpoint || conf.DEFAULT_INGEST_ENDPOINT;
  this.timeout = params.timeout || conf.DEFAULT_TIMEOUT;
  this.batchSize = Math.max(1, (params.batchSize ? params.batchSize : conf.DEFAULT_BATCH_SIZE));
  this.userAgents = params.userAgents || null;
  this.globalDimensions = params.dimensions || {};
  this.enableAmazonUniqueId = params.enableAmazonUniqueId || false;
  this.proxy = params.proxy || null;

  this.rawData = [];
  this.rawEvents = [];
  this.queue = [];

  this.loadAWSUniqueId = new Promise(function (resolve) {
    if (!_this.enableAmazonUniqueId) {
      resolve();
      return;
    } else {
      if (_this.AWSUniqueId !== undefined && _this.AWSUniqueId !== '') {
        resolve();
        return;
      }
    }

    _this._retrieveAWSUniqueId(function (isSuccess, AWSUniqueId) {
      if (isSuccess) {
        _this.AWSUniqueId = AWSUniqueId;
        _this.globalDimensions[_this.AWSUniqueId_DIMENTION_NAME] = AWSUniqueId;
      } else {
        _this.enableAmazonUniqueId = false;
        _this.AWSUniqueId = '';
        delete _this.globalDimensions[_this.AWSUniqueId_DIMENTION_NAME];
      }
      resolve();
    });
  });
}

SignalFxClient.prototype.AWSUniqueId_DIMENTION_NAME = 'AWSUniqueId';
SignalFxClient.prototype.HEADER_API_TOKEN_KEY = 'X-SF-Token';
SignalFxClient.prototype.HEADER_USER_AGENT_KEY = 'User-Agent';
SignalFxClient.prototype.HEADER_CONTENT_TYPE = 'Content-Type';
SignalFxClient.prototype.INGEST_ENDPOINT_SUFFIX = 'v2/datapoint';
SignalFxClient.prototype.EVENT_ENDPOINT_SUFFIX = 'v2/event';

SignalFxClient.prototype.EVENT_CATEGORIES = {
  USER_DEFINED: 'USER_DEFINED',
  ALERT: 'ALERT',
  AUDIT: 'AUDIT',
  JOB: 'JOB',
  COLLECTD: 'COLLECTD',
  SERVICE_DISCOVERY: 'SERVICE_DISCOVERY',
  EXCEPTION: 'EXCEPTION'
};

/**
 * Send the given metrics to SignalFx.
 *
 * @param data - param object with following fields:
 *    'cumulative_counters' (list): a list of dictionaries representing the cumulative counters to report.
 *    'gauges' (list): a list of dictionaries representing the gauges to report.
 *    'counters' (list): a list of dictionaries representing the counters to report.
 */
SignalFxClient.prototype.send = function (data) {
  var _this = this;
  this.rawData.push(data);
  return this.loadAWSUniqueId
    .then(function () {
      _this.processingData();
      return _this.startAsyncSend();
    });
};

SignalFxClient.prototype.processingData = function () {
  var _this = this;

  function processDataPoints(metricType, dataPoints) {
    if (!dataPoints) {
      return;
    }

    if (!Array.isArray(dataPoints)) {
      throw new TypeError('DataPoints not of type list: ' + dataPoints);
    }

    dataPoints.forEach(function (dp) {
      var datapoint = dp;
      if (!datapoint.dimensions) {
        datapoint.dimensions = {};
      }
      for (var key in _this.globalDimensions) {
        if (_this.globalDimensions.hasOwnProperty(key)) {
          datapoint.dimensions[key] = _this.globalDimensions[key];
        }
      }

      _this._addToQueue(metricType, datapoint);
    });
  }

  while (this.rawData.length) {
    var data = this.rawData.pop();
    if (data) {
      processDataPoints('cumulative_counter', data.cumulative_counters);
      processDataPoints('gauge', data.gauges);
      processDataPoints('counter', data.counters);
    }
  }
};

SignalFxClient.prototype._addToQueue = function (metricType, datapoint) {
  throw new Error('Subclasses should implement this!');
};

SignalFxClient.prototype._batchData = function (datapointsList) {
  throw new Error('Subclasses should implement this!');
};

/**
 * Send an event to SignalFx
 *
 * @param event - param object with following fields:
 *    category (string) - the category of event. Choose one from EVENT_CATEGORIES list
 *    eventType (string) - the event type (name of the event time series).
 *    dimensions  (dict) - a map of event dimensions, empty dictionary by default
 *    properties  (dict) - a map of extra properties on that event, empty dictionary by default
 *    timestamp (int64) - a timestamp, by default is current time
 *
 * Only eventType field is required
 */
SignalFxClient.prototype.sendEvent = function (event) {
  var _this = this;
  if (!event || !event.eventType) {
    throw new Error('Type of event should not be empty!');
  }
  // Check than passed event category is supported
  if (!this.EVENT_CATEGORIES[event.category]) {
    throw new Error('Unsupported event category: ' + event.category);
  }

  var data = {
    category: event.category || _this.EVENT_CATEGORIES.USER_DEFINED,
    eventType: event.eventType,
    dimensions: event.dimensions || {},
    properties: event.properties || {},
    timestamp: event.timestamp || (new Date()).getTime()
  };

  this.rawEvents.push(data);
  return this.loadAWSUniqueId.then(function () {
    return _this.startAsyncEventSend();
  });
};

SignalFxClient.prototype._retrieveAWSUniqueId = function (callback) {
  var getOptions = {
    url: conf.AWS_UNIQUE_ID_URL,
    timeout: 1000,
    method: 'GET',
    proxy: this.proxy
  };

  request(getOptions, function (error, response, body) {
    if (error || response.statusCode !== 200) {
      callback(false, '');
      winston.error('Failed to retrieve AWS unique ID: ', response ? response.statusCode : '', body);
    } else {
      try {
        var jsonBody = JSON.parse(body);
        if (jsonBody && jsonBody.instanceId && jsonBody.region && jsonBody.accountId) {
          var AWSUniqueId = jsonBody.instanceId + '_' + jsonBody.region + '_' + jsonBody.accountId;
          callback(true, AWSUniqueId);
        } else {
          callback(false, '');
        }
      } catch (e) {
        callback(false, '');
      }
    }
  });
};

SignalFxClient.prototype.getHeaderContentType = function () {
  throw new Error('Subclasses should implement this!');
};

SignalFxClient.prototype._buildEvent = function (event) {
  throw new Error('Subclasses should implement this!');
};

SignalFxClient.prototype._encodeEvent = function (event) {
  throw new Error('Subclasses should implement this!');
};

SignalFxClient.prototype.startAsyncSend = function () {
  var _this = this;
  // Send post request in separate thread
  var datapointsList = [];
  while (_this.queue.length !== 0 && datapointsList.length < _this.batchSize) {
    datapointsList.push(_this.queue.shift());
  }

  if (datapointsList.length > 0) {
    var dataToSend = _this._batchData(datapointsList);
    if (dataToSend && dataToSend.length > 0) {
      var url = _this.ingestEndpoint + '/' + _this.INGEST_ENDPOINT_SUFFIX;
      return _this.post(dataToSend, url, _this.getHeaderContentType());
    }
  }
  return new Promise(function (resolve) {
    resolve(null);
  });
};

SignalFxClient.prototype.startAsyncEventSend = function () {
  var _this = this;
  while (this.rawEvents.length) {
    var data = this.rawEvents.pop();
    for (var key in this.globalDimensions) {
      if (this.globalDimensions.hasOwnProperty(key)) {
        data.dimensions[key] = this.globalDimensions[key];
      }
    }

    try {
      var eventToSend = _this._buildEvent(data);
      if (eventToSend) {
        var url = this.ingestEndpoint + '/' + this.EVENT_ENDPOINT_SUFFIX;
        return this.post(_this._encodeEvent(eventToSend), url, _this.getHeaderContentType());
      }
    } catch (error) {
      winston.error('Can\'t process event: ', error);
    }
  }
};

SignalFxClient.prototype.post = function (data, postUrl, contentType) {
  var _this = this;

  var headers = {};
  headers[_this.HEADER_USER_AGENT_KEY] = name + '/' + version;
  //Adding custom user agents passed by client modules
  if (_this.userAgents) {
    headers[_this.HEADER_USER_AGENT_KEY] += ',' + _this.userAgents;
  }
  headers[_this.HEADER_API_TOKEN_KEY] = _this.apiToken;
  headers[_this.HEADER_CONTENT_TYPE] = contentType;

  // An object of options to indicate where to post to
  var postOptions = {
    url: postUrl,
    timeout: _this.timeout,
    headers: headers,
    body: data,
    method: 'POST',
    proxy: _this.proxy
  };

  return new Promise(function (resolve, reject) {
    request(postOptions, function (error, response, body) {
      if (error || response.statusCode !== 200) {
        winston.error('Failed to send datapoint: ', response ? response.statusCode : '', body, error);
        reject(error);
      }
      resolve(body);
    });
  });
};

exports.SignalFxClient = SignalFxClient;
<|endoftext|>


<commit_before>/**
* Copyright (c) Microsoft.  All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

var fs = require('fs');
var path = require('path');
var azure = require('azure');
var through = require('through');
var util = require('util');
var validation = require('../../../util/validation');

var profile = require('../../../util/profile');
var utils = require('../../../util/utils');

var $ = utils.getLocaleString;

/**
* Validate that a given location is valid for group creation
* and prompts for location if not valid or not given.
*
* @param {string} location location requested
* @param {object} log object to print messages to user
* @param {object} interaction the interactor object to prompt with
* @param {function} callback callback received either error or final location
*/
exports.validateLocation = function validateLocation(location, log, interaction, callback) {
  // TODO: We currently don't have a way to query the server for
  // valid group locations, so hard coded to what the test cluster
  // allows.

  var validLocations = ['South Central US', 'West Europe'];

  if (location && validLocations.some(function (l) { return utils.ignoreCaseEquals(l, location); })) {
    return callback(null, location);
  }

  if (location) {
    log.info(util.format($('The location %s is not valid'), location));
  }

  log.info($('Choose location: '));
  interaction.choose(validLocations, function (err, locationIndex) {
    callback(err, validLocations[locationIndex]);
  });
};

exports.getGroup = function getGroup(client, name, callback) {
  client.resourceGroups.get(name, function (err, group) {
    if (err) {
      // 404 means doesn't exist
      if (err.statusCode === 404) {
        callback(null, null);
      } else {
      // some other error, let it out
        callback(err);
      }
    } else {
      // No error, group exists
      callback(null, group.resourceGroup);
    }
  });
};

exports.getResource = function getResource(client, resourceGroup, identity, callback) {
  client.resources.get(resourceGroup, identity, function (err, resource) {
    if (err) {
      // 404 means doesn't exist
      if (err.statusCode === 404) {
        callback(null, null);
      } else {
      // some other error, let it out
        callback(err);
      }
    } else {
      // No error, resource exists
      callback(null, resource.resource);
    }
  });
};

var deploymentTemplateStorageContainerName = 'deployment-templates';

exports.createDeployment = function (cli, resourceGroup, name, options, _) {
  var subscription = profile.current.getSubscription(options.subscription);
  var client = subscription.createResourceClient('createResourceManagementClient');

  if (!name) {
    // if the user doesn't specify a deployment name, use the current time, like "20131223140835"
    name = new Date().toISOString().split('.')[0]
      .replace(/-/g, '')
      .replace(/T/g, '')
      .replace(/:/g, '');
  }
  var templateParameters = createDeploymentParameters(cli, subscription, resourceGroup, options, _);

  var deploymentObject = cli.interaction.withProgress($('Creating a deployment'),
    function (log, _) {
      client.deployments.validate(resourceGroup, name, templateParameters, _);
      return client.deployments.createOrUpdate(resourceGroup, name, templateParameters, _);
    }, _);

  cli.output.info(util.format($('Created template deployment "%s"'), name));
  return deploymentObject;
};

exports.validateTemplate = function (cli, resourceGroup, options, _) {
  var subscription = profile.current.getSubscription(options.subscription);
  var client = subscription.createResourceClient('createResourceManagementClient');

  var templateParameters = createDeploymentParameters(cli, subscription, resourceGroup, options, _);

  return cli.interaction.withProgress($('Validating the template'),
    function (log, _) {
      return client.deployments.validate(resourceGroup, 'fakedDeploymentName', templateParameters, _);
    }, _);
};

exports.getTemplateUri = function (subscription, environment, templateFile, galleryTemplateName, storageAccountName, _) {
  var templateFileUri;

  if (templateFile) {
    if (validation.isURL(templateFile)) {
      templateFileUri = templateFile;
    } else {
      var storageClient = utils._createStorageClient(subscription);
      var keys = storageClient.storageAccounts.getKeys(storageAccountName, _);
      var storageAccount = storageClient.storageAccounts.get(storageAccountName, _).storageAccount;

      var blobService = azure.createBlobService(storageAccountName, keys.primaryKey, storageAccount.properties.endpoints[0]);

      try {
        blobService.createContainer(deploymentTemplateStorageContainerName, {
          publicAccessLevel: azure.Constants.BlobConstants.BlobContainerPublicAccessType.BLOB
        }, _);
      } catch (e) {
        if (e.statusCode !== 409) {
          // Don't throw if container already exists
          throw e;
        }
      }

      var blobName = process.env.CSM_TEST_BLOB_NAME || new Date().toISOString().split('.')[0]
            .replace(/-/g, '')
            .replace(/_/g, '')
            .replace(/:/g, '');

      blobService
          .createBlockBlobFromFile(deploymentTemplateStorageContainerName,
            blobName, templateFile,_);

      templateFileUri = blobService.getBlobUrl(deploymentTemplateStorageContainerName, blobName, {
        AccessPolicy: {
          Permissions: azure.Constants.BlobConstants.SharedAccessPermissions.READ,
          Expiry: azure.date.hoursFromNow(24)
        }
      });
    }
  } else if (galleryTemplateName) {
    templateFileUri = exports.getGalleryTemplateFile(environment, galleryTemplateName, _);
  }

  return templateFileUri;
};

exports.getGalleryTemplateFile = function (environment, galleryTemplateName, _) {
  var galleryClient = createGalleryClient(environment);
  var galleryItem = galleryClient.items.get(galleryTemplateName, _).item;
  return exports.getTemplateDownloadUrl(galleryItem);
};

function createGalleryClient(environment) {
  return utils.createClient('createGalleryClient', new azure.AnonymousCloudCredentials(),
    profile.current.getEnvironment(environment).publicGalleryEndpointUrl);
}

exports.getTemplateDownloadUrl = function getTemplateDownloadUrl(templateData) {
  var key = templateData.definitionTemplates.defaultDeploymentTemplateId;
  var urls = Object.keys(templateData.definitionTemplates.deploymentTemplateFileUrls)
    .filter(function (url) { return utils.ignoreCaseEquals(key, url); });

  if (urls.length === 0) {
    throw new Error($('Error in template, the key %s is not found in deploymentTemplateFileUrls'));
  }

  return templateData.definitionTemplates.deploymentTemplateFileUrls[urls[0]];
};

exports.normalizeDownloadFileName = function normalizeDownloadFileName(name, file, quiet, confirmer, callback) {
  name = name + '.json';
  var downloadFile = path.resolve(file || name);

  function ensureDirExists(dirname) {
    if (!dirname) {
      return;
    }

    if (utils.pathExistsSync(dirname)) {
      if (!fs.statSync(dirname).isDirectory()) {
        throw new Error(util.format($('Path %s already exists and is not a directory.'), dirname));
      }
      return;
    }

    ensureDirExists(path.dirname(dirname));

    fs.mkdirSync(dirname);
  }

  function normalizeFile() {
    try {
      ensureDirExists(path.dirname(downloadFile));
      if (utils.pathExistsSync(downloadFile) && !quiet) {
        confirmer(
          util.format($('The file %s already exists. Overwrite? [y/n]: '), downloadFile),
            function (confirmed) {
              if (confirmed) {
                callback(null, downloadFile);
              } else {
                callback(null, null);
              }
            }
        );
      } else {
        callback(null, downloadFile);
      }
    } catch(ex) {
      callback(ex);
    }
  }

  function normalizeDirectory() {
    downloadFile = path.join(downloadFile, name);
    normalizeFile();
  }

  if (utils.pathExistsSync(downloadFile) && fs.statSync(downloadFile).isDirectory()) {
    normalizeDirectory();
  } else {
    normalizeFile();
  }
};

function createDeploymentParameters(cli, subscription, resourceGroup, options, _) {
  if (options.galleryTemplate && options.fileTemplate) {
    throw new Error($('Either --gallery-template or --file-template need to be specified. Not both.'));
  } else if (!options.galleryTemplate && !options.fileTemplate) {
    throw new Error($('Either --gallery-template or --file-template need to be specified.'));
  }

  if (options.parameters && options.parametersFile) {
    throw new Error($('Either --parameters or --parameters-file need to be specified. Not both.'));
  }

  var deploymentParameters;
  if (options.parametersFile) {
    var jsonFile = fs.readFileSync(options.parametersFile, 'utf8');
    deploymentParameters = JSON.parse(utils.stripBOM(jsonFile));
    if (!deploymentParameters.properties || !deploymentParameters.properties.parameters) {
      throw new Error($('Incorrectly formated parameters file.'));
    }

    deploymentParameters = deploymentParameters.properties.parameters;
  } else if (options.parameters) {
    deploymentParameters = JSON.parse(options.parameters);
  }

  var templateParameters;
  cli.interaction.withProgress($('Initializing template configurations and parameters'),
    function (log, _) {

      var templateUri = exports.getTemplateUri(subscription,
        options.env,
        options.fileTemplate,
        options.galleryTemplate,
        options.storageAccount, _);

      templateParameters = {
        mode: 'Incremental',
        templateLink: {
          uri: templateUri
        }
      };

      if (deploymentParameters) {
        templateParameters.parameters = deploymentParameters;

        if (options.parametersHash) {
          templateParameters.parameters.contentHash = {
            value: options.parametersHash
          };

          if (options.parametersHashAlgorithm) {
            templateParameters.parameters.contentHash.algorithm = options.parametersHashAlgorithm;
          }
        }

        if (options.parametersVersion) {
          templateParameters.parameters.contentVersion = options.parametersVersion;
        }
      }

      if (options.templateHash) {
        templateParameters.templateLink = {
          contentHash: {
            value: options.templateHash
          }
        };

        if (options.templateHashAlgorithm) {
          templateParameters.templateLink.contentHash.algorithm = options.templateHashAlgorithm;
        }
      }

      if (options.templateVersion) {
        if (!options.templateVersion) {
          templateParameters = {};
        }

        templateParameters.templateLink.contentVersion = options.templateVersion;
      }
    }, _);
  return templateParameters;
}

var eventRetentionPeriodMS = 89 * 24 * 60 * 60 * 1000; // 89 days in milliseconds

function getDeploymentLogs(subscription, correlationId) {
  var output = through();
  var client = subscription.createResourceClient('createEventsClient');
  client.eventData.listEventsForCorrelationId({
    correlationId: correlationId,
    startTime: new Date(Date.now() - eventRetentionPeriodMS),
    endTime: new Date()
  }, function (err, response) {
    if (err) {
      return output.emit('error', err);
    }
    response.eventDataCollection.value.forEach(function (e) {
      output.queue(e);
    });
    output.end();
  });
  return output;
}

exports.getAllEvents = function (subscription, groupName) {
  var output = through();
  var client = subscription.createResourceClient('createEventsClient');

  client.eventData.listEventsForResourceGroup({
    resourceGroupName: groupName,
    startTime: new Date(Date.now() - eventRetentionPeriodMS),
    endTime: new Date()
  }, function (err, response) {
    if (err) {
      return output.emit('error', err);
    }

    response.eventDataCollection.value.forEach(function (e) {
      output.queue(e);
    });
    output.end();
  });
  return output;
};

exports.getDeploymentLog = function (subscription, name, deploymentName) {
  var output = through();
  var client = subscription.createResourceClient('createResourceManagementClient');

  client.deployments.get(name, deploymentName, function (err, result) {
    if (err) {
      output.emit('error', err);
    }
    getDeploymentLogs(subscription, result.deployment.properties.correlationId).pipe(output);
  });
  return output;
};

exports.getLastDeploymentLog = function (subscription, name) {
  var output = through();
  var client = subscription.createResourceClient('createResourceManagementClient');

  client.deployments.list(name, { top: 1 }, function (err, response) {
    if (err) { return output.emit('error', err); }
    if (response.deployments.length === 0) {
      output.emit('error', new Error($('Deployment not found')));
    }
    getDeploymentLogs(subscription, response.deployments[0].properties.correlationId).pipe(output);
  });
  return output;
};

<commit_msg>Updating environment variable<commit_after>/**
* Copyright (c) Microsoft.  All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

var fs = require('fs');
var path = require('path');
var azure = require('azure');
var through = require('through');
var util = require('util');
var validation = require('../../../util/validation');

var profile = require('../../../util/profile');
var utils = require('../../../util/utils');

var $ = utils.getLocaleString;

/**
* Validate that a given location is valid for group creation
* and prompts for location if not valid or not given.
*
* @param {string} location location requested
* @param {object} log object to print messages to user
* @param {object} interaction the interactor object to prompt with
* @param {function} callback callback received either error or final location
*/
exports.validateLocation = function validateLocation(location, log, interaction, callback) {
  // TODO: We currently don't have a way to query the server for
  // valid group locations, so hard coded to what the test cluster
  // allows.

  var validLocations = ['South Central US', 'West Europe'];

  if (location && validLocations.some(function (l) { return utils.ignoreCaseEquals(l, location); })) {
    return callback(null, location);
  }

  if (location) {
    log.info(util.format($('The location %s is not valid'), location));
  }

  log.info($('Choose location: '));
  interaction.choose(validLocations, function (err, locationIndex) {
    callback(err, validLocations[locationIndex]);
  });
};

exports.getGroup = function getGroup(client, name, callback) {
  client.resourceGroups.get(name, function (err, group) {
    if (err) {
      // 404 means doesn't exist
      if (err.statusCode === 404) {
        callback(null, null);
      } else {
      // some other error, let it out
        callback(err);
      }
    } else {
      // No error, group exists
      callback(null, group.resourceGroup);
    }
  });
};

exports.getResource = function getResource(client, resourceGroup, identity, callback) {
  client.resources.get(resourceGroup, identity, function (err, resource) {
    if (err) {
      // 404 means doesn't exist
      if (err.statusCode === 404) {
        callback(null, null);
      } else {
      // some other error, let it out
        callback(err);
      }
    } else {
      // No error, resource exists
      callback(null, resource.resource);
    }
  });
};

var deploymentTemplateStorageContainerName = 'deployment-templates';

exports.createDeployment = function (cli, resourceGroup, name, options, _) {
  var subscription = profile.current.getSubscription(options.subscription);
  var client = subscription.createResourceClient('createResourceManagementClient');

  if (!name) {
    // if the user doesn't specify a deployment name, use the current time, like "20131223140835"
    name = new Date().toISOString().split('.')[0]
      .replace(/-/g, '')
      .replace(/T/g, '')
      .replace(/:/g, '');
  }
  var templateParameters = createDeploymentParameters(cli, subscription, resourceGroup, options, _);

  var deploymentObject = cli.interaction.withProgress($('Creating a deployment'),
    function (log, _) {
      client.deployments.validate(resourceGroup, name, templateParameters, _);
      return client.deployments.createOrUpdate(resourceGroup, name, templateParameters, _);
    }, _);

  cli.output.info(util.format($('Created template deployment "%s"'), name));
  return deploymentObject;
};

exports.validateTemplate = function (cli, resourceGroup, options, _) {
  var subscription = profile.current.getSubscription(options.subscription);
  var client = subscription.createResourceClient('createResourceManagementClient');

  var templateParameters = createDeploymentParameters(cli, subscription, resourceGroup, options, _);

  return cli.interaction.withProgress($('Validating the template'),
    function (log, _) {
      return client.deployments.validate(resourceGroup, 'fakedDeploymentName', templateParameters, _);
    }, _);
};

exports.getTemplateUri = function (subscription, environment, templateFile, galleryTemplateName, storageAccountName, _) {
  var templateFileUri;

  if (templateFile) {
    if (validation.isURL(templateFile)) {
      templateFileUri = templateFile;
    } else {
      var storageClient = utils._createStorageClient(subscription);
      var keys = storageClient.storageAccounts.getKeys(storageAccountName, _);
      var storageAccount = storageClient.storageAccounts.get(storageAccountName, _).storageAccount;

      var blobService = azure.createBlobService(storageAccountName, keys.primaryKey, storageAccount.properties.endpoints[0]);

      try {
        blobService.createContainer(deploymentTemplateStorageContainerName, {
          publicAccessLevel: azure.Constants.BlobConstants.BlobContainerPublicAccessType.BLOB
        }, _);
      } catch (e) {
        if (e.statusCode !== 409) {
          // Don't throw if container already exists
          throw e;
        }
      }

      var blobName = process.env.ARM_TEST_BLOB_NAME || new Date().toISOString().split('.')[0]
            .replace(/-/g, '')
            .replace(/_/g, '')
            .replace(/:/g, '');

      blobService
          .createBlockBlobFromFile(deploymentTemplateStorageContainerName,
            blobName, templateFile,_);

      templateFileUri = blobService.getBlobUrl(deploymentTemplateStorageContainerName, blobName, {
        AccessPolicy: {
          Permissions: azure.Constants.BlobConstants.SharedAccessPermissions.READ,
          Expiry: azure.date.hoursFromNow(24)
        }
      });
    }
  } else if (galleryTemplateName) {
    templateFileUri = exports.getGalleryTemplateFile(environment, galleryTemplateName, _);
  }

  return templateFileUri;
};

exports.getGalleryTemplateFile = function (environment, galleryTemplateName, _) {
  var galleryClient = createGalleryClient(environment);
  var galleryItem = galleryClient.items.get(galleryTemplateName, _).item;
  return exports.getTemplateDownloadUrl(galleryItem);
};

function createGalleryClient(environment) {
  return utils.createClient('createGalleryClient', new azure.AnonymousCloudCredentials(),
    profile.current.getEnvironment(environment).publicGalleryEndpointUrl);
}

exports.getTemplateDownloadUrl = function getTemplateDownloadUrl(templateData) {
  var key = templateData.definitionTemplates.defaultDeploymentTemplateId;
  var urls = Object.keys(templateData.definitionTemplates.deploymentTemplateFileUrls)
    .filter(function (url) { return utils.ignoreCaseEquals(key, url); });

  if (urls.length === 0) {
    throw new Error($('Error in template, the key %s is not found in deploymentTemplateFileUrls'));
  }

  return templateData.definitionTemplates.deploymentTemplateFileUrls[urls[0]];
};

exports.normalizeDownloadFileName = function normalizeDownloadFileName(name, file, quiet, confirmer, callback) {
  name = name + '.json';
  var downloadFile = path.resolve(file || name);

  function ensureDirExists(dirname) {
    if (!dirname) {
      return;
    }

    if (utils.pathExistsSync(dirname)) {
      if (!fs.statSync(dirname).isDirectory()) {
        throw new Error(util.format($('Path %s already exists and is not a directory.'), dirname));
      }
      return;
    }

    ensureDirExists(path.dirname(dirname));

    fs.mkdirSync(dirname);
  }

  function normalizeFile() {
    try {
      ensureDirExists(path.dirname(downloadFile));
      if (utils.pathExistsSync(downloadFile) && !quiet) {
        confirmer(
          util.format($('The file %s already exists. Overwrite? [y/n]: '), downloadFile),
            function (confirmed) {
              if (confirmed) {
                callback(null, downloadFile);
              } else {
                callback(null, null);
              }
            }
        );
      } else {
        callback(null, downloadFile);
      }
    } catch(ex) {
      callback(ex);
    }
  }

  function normalizeDirectory() {
    downloadFile = path.join(downloadFile, name);
    normalizeFile();
  }

  if (utils.pathExistsSync(downloadFile) && fs.statSync(downloadFile).isDirectory()) {
    normalizeDirectory();
  } else {
    normalizeFile();
  }
};

function createDeploymentParameters(cli, subscription, resourceGroup, options, _) {
  if (options.galleryTemplate && options.fileTemplate) {
    throw new Error($('Either --gallery-template or --file-template need to be specified. Not both.'));
  } else if (!options.galleryTemplate && !options.fileTemplate) {
    throw new Error($('Either --gallery-template or --file-template need to be specified.'));
  }

  if (options.parameters && options.parametersFile) {
    throw new Error($('Either --parameters or --parameters-file need to be specified. Not both.'));
  }

  var deploymentParameters;
  if (options.parametersFile) {
    var jsonFile = fs.readFileSync(options.parametersFile, 'utf8');
    deploymentParameters = JSON.parse(utils.stripBOM(jsonFile));
    if (!deploymentParameters.properties || !deploymentParameters.properties.parameters) {
      throw new Error($('Incorrectly formated parameters file.'));
    }

    deploymentParameters = deploymentParameters.properties.parameters;
  } else if (options.parameters) {
    deploymentParameters = JSON.parse(options.parameters);
  }

  var templateParameters;
  cli.interaction.withProgress($('Initializing template configurations and parameters'),
    function (log, _) {

      var templateUri = exports.getTemplateUri(subscription,
        options.env,
        options.fileTemplate,
        options.galleryTemplate,
        options.storageAccount, _);

      templateParameters = {
        mode: 'Incremental',
        templateLink: {
          uri: templateUri
        }
      };

      if (deploymentParameters) {
        templateParameters.parameters = deploymentParameters;

        if (options.parametersHash) {
          templateParameters.parameters.contentHash = {
            value: options.parametersHash
          };

          if (options.parametersHashAlgorithm) {
            templateParameters.parameters.contentHash.algorithm = options.parametersHashAlgorithm;
          }
        }

        if (options.parametersVersion) {
          templateParameters.parameters.contentVersion = options.parametersVersion;
        }
      }

      if (options.templateHash) {
        templateParameters.templateLink = {
          contentHash: {
            value: options.templateHash
          }
        };

        if (options.templateHashAlgorithm) {
          templateParameters.templateLink.contentHash.algorithm = options.templateHashAlgorithm;
        }
      }

      if (options.templateVersion) {
        if (!options.templateVersion) {
          templateParameters = {};
        }

        templateParameters.templateLink.contentVersion = options.templateVersion;
      }
    }, _);
  return templateParameters;
}

var eventRetentionPeriodMS = 89 * 24 * 60 * 60 * 1000; // 89 days in milliseconds

function getDeploymentLogs(subscription, correlationId) {
  var output = through();
  var client = subscription.createResourceClient('createEventsClient');
  client.eventData.listEventsForCorrelationId({
    correlationId: correlationId,
    startTime: new Date(Date.now() - eventRetentionPeriodMS),
    endTime: new Date()
  }, function (err, response) {
    if (err) {
      return output.emit('error', err);
    }
    response.eventDataCollection.value.forEach(function (e) {
      output.queue(e);
    });
    output.end();
  });
  return output;
}

exports.getAllEvents = function (subscription, groupName) {
  var output = through();
  var client = subscription.createResourceClient('createEventsClient');

  client.eventData.listEventsForResourceGroup({
    resourceGroupName: groupName,
    startTime: new Date(Date.now() - eventRetentionPeriodMS),
    endTime: new Date()
  }, function (err, response) {
    if (err) {
      return output.emit('error', err);
    }

    response.eventDataCollection.value.forEach(function (e) {
      output.queue(e);
    });
    output.end();
  });
  return output;
};

exports.getDeploymentLog = function (subscription, name, deploymentName) {
  var output = through();
  var client = subscription.createResourceClient('createResourceManagementClient');

  client.deployments.get(name, deploymentName, function (err, result) {
    if (err) {
      output.emit('error', err);
    }
    getDeploymentLogs(subscription, result.deployment.properties.correlationId).pipe(output);
  });
  return output;
};

exports.getLastDeploymentLog = function (subscription, name) {
  var output = through();
  var client = subscription.createResourceClient('createResourceManagementClient');

  client.deployments.list(name, { top: 1 }, function (err, response) {
    if (err) { return output.emit('error', err); }
    if (response.deployments.length === 0) {
      output.emit('error', new Error($('Deployment not found')));
    }
    getDeploymentLogs(subscription, response.deployments[0].properties.correlationId).pipe(output);
  });
  return output;
};

<|endoftext|>


<commit_before>'use strict';
/* global CodeMirror $ */

var React = require('react/addons');
var TagTranslator = require('../helpers/tag-translator');
var ChoicesDropdown = require('../helpers/choices-dropdown');
var _ = require('underscore');

/*
   Editor for tagged text. Renders text like "hello {{firstName}}"
   with replacement labels rendered in a pill box. Designed to load
   quickly when many separate instances of it are on the same
   page.

   Uses CodeMirror to edit text. To save memory the CodeMirror node is
   instantiated when the user moves the mouse into the edit area.
   Initially a read-only view using a simple div is shown.

   IMPLEMENTATION NOTE:

   To display the tags inside CodeMirror we are using CM's
   specialCharPlaceholder feature, to replace special characters with
   custom DOM nodes. This feature is designed for single character
   replacements, not tags like 'firstName'.  So we replace each tag
   with an unused character from the Unicode private use area, and
   tell CM to replace that with a DOM node display the tag label with
   the pill box effect.

   Is this evil? Perhaps a little, but delete, undo, redo, cut, copy
   and paste of the tag pill boxes just work because CM treats them as
   atomic single characters, and it's not much code on our part.
 */
module.exports = React.createClass({
  displayName: 'PrettyText',

  mixins: [require('../../mixins/field')],

  componentDidMount: function() {
    this.createEditor();
  },

  shouldComponentUpdate: function(nextProps, nextState) {
    return (nextState.value !== this.state.value) ||
           (nextState.codeMirrorMode && !this.state.codeMirrorMode) ||
           !_.isEqual(nextProps.field.replaceChoices, this.props.replaceChoices);
  },

  componentDidUpdate: function(prevProps, prevState) {
    if (prevState.codeMirrorMode !== this.state.codeMirrorMode) {
      // Changed from code mirror mode to read only mode or vice versa,
      // so setup the other editor.
      this.createEditor();
    }

    // If they just typed in a tag like {{firstName}} we have to replace it
    if (this.state.codeMirrorMode && this.codeMirror.getValue().match(/\{\{.+\}\}/)) {
      // avoid recursive update cycle
      this.updatingCodeMirror = true;

      // get new encoded value for CodeMirror
      var cmValue = this.codeMirror.getValue();
      var decodedValue = this.translator.decodeValue(cmValue);
      var encodedValue = this.translator.encodeValue(decodedValue);

      // Grab the cursor so we can reset it.
      // The new length of the CM value will be shorter after replacing a tag like {{firstName}}
      // with a single special char, so adjust cursor position accordingly.
      var cursor = this.codeMirror.getCursor();
      cursor.ch -= cmValue.length - encodedValue.length;

      this.codeMirror.setValue(encodedValue);
      this.codeMirror.setCursor(cursor);
    }
  },

  componentWillUnmount: function() {
    this.removeCodeMirrorEditor();
  },

  getInitialState: function() {
    // TODO: using instance vars like this is probaby bad. Find another approach.
    this.tagToLabelMap = this.buildTagToLabelMap();
    this.translator = TagTranslator(this.tagToLabelMap, this.props.config.humanize);

    return {
      value: this.props.field.value,
      codeMirrorMode: false
    };
  },

  componentWillReceiveProps: function(nextProps) {
    if (this.state.value !== nextProps.field.value && nextProps.field.value) {
      this.setState({ value: nextProps.field.value });
    }
  },

  buildTagToLabelMap: function () {
    var map = {};
    this.props.field.replaceChoices.map(function (choice) {
      map[choice.value] = choice.label;
    });
    return map;
  },

  handleChoiceSelection: function (key) {
    var char = this.translator.encodeTag(key);

    // put the cursor at the end of the inserted tag.
    this.codeMirror.replaceSelection(char, 'end');
    this.codeMirror.focus();
  },

  render: function() {
    return this.renderWithConfig();
  },

  renderDefault: function () {
    var config = this.props.config;
    var field = this.props.field;
    var props = { field: field, plain: this.props.plain };
    var tabIndex = field.tabIndex;
    var choices = this.tagToLabelMap;
    var textBox = this.createTextBoxNode();

    // Render read-only version. We are using pure HTML via dangerouslySetInnerHTML, to avoid
    // the cost of the react nodes. This is probably a premature optimization.
    var element = (

          {textBox}

        <ChoicesDropdown ref="choicesDropdown" choices={choices} handleSelection={this.handleChoiceSelection} />

    );

    return config.createElement('field', props, element);
  },

  createTextBoxNode: function () {
    if (this.state.codeMirrorMode) {
      return
;
    } else {
      var html = this.translator.toHtml(this.state.value);
      return
;
    }
  },

  createEditor: function () {
    if (this.state.codeMirrorMode) {
      this.createCodeMirrorEditor();
    } else {
      this.createReadonlyEditor();
    }
  },

  createCodeMirrorEditor: function () {
    var cmValue = this.translator.encodeValue(this.state.value);

    var options = {
      tabindex: this.props.tabIndex,
      value: cmValue,
      specialChars: this.translator.specialCharsRegexp,
      specialCharPlaceholder: this.createTagNode
    };

    var textBox = this.refs.textBox.getDOMNode();
    textBox.innerHTML = ''; // release any previous read-only content so it can be GC'ed

    this.codeMirror = CodeMirror(textBox, options);
    this.codeMirror.on('change', this.onCodeMirrorChange);
  },

  onCodeMirrorChange: function () {
    if (this.updatingCodeMirror) {
      // avoid recursive update cycle, and mark the code mirror manual update as done
      this.updatingCodeMirror = false;
      return;
    }

    var newValue = this.translator.decodeValue(this.codeMirror.getValue());
    this.onChangeValue(newValue);
    this.setState({value: newValue});
  },

  createReadonlyEditor: function () {
    this.removeCodeMirrorEditor();
    var textBoxNode = this.refs.textBox.getDOMNode();
    textBoxNode.innerHTML = this.translator.toHtml(this.state.value);
  },

  removeCodeMirrorEditor: function () {
    var textBoxNode = this.refs.textBox.getDOMNode();
    var cmNode = textBoxNode.firstChild;
    textBoxNode.removeChild(cmNode);
    this.codeMirror = null;
  },

  switchToCodeMirror: function () {
    if (!this.state.codeMirrorMode) {
      this.setState({codeMirrorMode: true});
    }
  },

  displayChoicesDropdown: function () {
    // TODO: hack to toggle bootstrap dropdown. Won't need once we get rid of BS in choices dropdown.
    $(this.refs.choicesDropdown.refs.dropdownToggle.getDOMNode()).dropdown('toggle');
  },

  // Create pill box style for display inside CM. For example
  // '\ue000' becomes 'First Name'
  createTagNode: function (char) {
    var node = document.createElement('span');
    var label = this.translator.decodeChar(char);
    React.render(
      {label},
      node
    );
    return node;
  }
});
<commit_msg>PrettyText2: Use config.replaceChoices, fix shouldComponentUpdate re replaceChoices<commit_after>'use strict';
/* global CodeMirror $ */

var React = require('react/addons');
var TagTranslator = require('../helpers/tag-translator');
var ChoicesDropdown = require('../helpers/choices-dropdown');
var _ = require('underscore');

/*
   Editor for tagged text. Renders text like "hello {{firstName}}"
   with replacement labels rendered in a pill box. Designed to load
   quickly when many separate instances of it are on the same
   page.

   Uses CodeMirror to edit text. To save memory the CodeMirror node is
   instantiated when the user moves the mouse into the edit area.
   Initially a read-only view using a simple div is shown.

   IMPLEMENTATION NOTE:

   To display the tags inside CodeMirror we are using CM's
   specialCharPlaceholder feature, to replace special characters with
   custom DOM nodes. This feature is designed for single character
   replacements, not tags like 'firstName'.  So we replace each tag
   with an unused character from the Unicode private use area, and
   tell CM to replace that with a DOM node display the tag label with
   the pill box effect.

   Is this evil? Perhaps a little, but delete, undo, redo, cut, copy
   and paste of the tag pill boxes just work because CM treats them as
   atomic single characters, and it's not much code on our part.
 */
module.exports = React.createClass({
  displayName: 'PrettyText',

  mixins: [require('../../mixins/field')],

  componentDidMount: function() {
    this.createEditor();
  },

  shouldComponentUpdate: function(nextProps, nextState) {
    return (nextState.value !== this.state.value) ||
           (nextState.codeMirrorMode && !this.state.codeMirrorMode) ||
           !_.isEqual(nextProps.field.replaceChoices, this.props.field.replaceChoices);
  },

  componentDidUpdate: function(prevProps, prevState) {
    if (prevState.codeMirrorMode !== this.state.codeMirrorMode) {
      // Changed from code mirror mode to read only mode or vice versa,
      // so setup the other editor.
      this.createEditor();
    }

    // If they just typed in a tag like {{firstName}} we have to replace it
    if (this.state.codeMirrorMode && this.codeMirror.getValue().match(/\{\{.+\}\}/)) {
      // avoid recursive update cycle
      this.updatingCodeMirror = true;

      // get new encoded value for CodeMirror
      var cmValue = this.codeMirror.getValue();
      var decodedValue = this.translator.decodeValue(cmValue);
      var encodedValue = this.translator.encodeValue(decodedValue);

      // Grab the cursor so we can reset it.
      // The new length of the CM value will be shorter after replacing a tag like {{firstName}}
      // with a single special char, so adjust cursor position accordingly.
      var cursor = this.codeMirror.getCursor();
      cursor.ch -= cmValue.length - encodedValue.length;

      this.codeMirror.setValue(encodedValue);
      this.codeMirror.setCursor(cursor);
    }
  },

  componentWillUnmount: function() {
    this.removeCodeMirrorEditor();
  },

  getInitialState: function() {
    // TODO: using instance vars like this is probaby bad. Find another approach.
    this.tagToLabelMap = this.buildTagToLabelMap();
    this.translator = TagTranslator(this.tagToLabelMap, this.props.config.humanize);

    return {
      value: this.props.field.value,
      codeMirrorMode: false
    };
  },

  componentWillReceiveProps: function(nextProps) {
    if (this.state.value !== nextProps.field.value && nextProps.field.value) {
      this.setState({ value: nextProps.field.value });
    }
  },

  buildTagToLabelMap: function () {
    var map = {};
    this.props.config.fieldReplaceChoices(this.props.field).map(function (choice) {
      map[choice.value] = choice.label;
    });
    return map;
  },

  handleChoiceSelection: function (key) {
    var char = this.translator.encodeTag(key);

    // put the cursor at the end of the inserted tag.
    this.codeMirror.replaceSelection(char, 'end');
    this.codeMirror.focus();
  },

  render: function() {
    return this.renderWithConfig();
  },

  renderDefault: function () {
    var config = this.props.config;
    var field = this.props.field;
    var props = { field: field, plain: this.props.plain };
    var tabIndex = field.tabIndex;
    var choices = this.tagToLabelMap;
    var textBox = this.createTextBoxNode();

    // Render read-only version. We are using pure HTML via dangerouslySetInnerHTML, to avoid
    // the cost of the react nodes. This is probably a premature optimization.
    var element = (

          {textBox}

        <ChoicesDropdown ref="choicesDropdown" choices={choices} handleSelection={this.handleChoiceSelection} />

    );

    return config.createElement('field', props, element);
  },

  createTextBoxNode: function () {
    if (this.state.codeMirrorMode) {
      return
;
    } else {
      var html = this.translator.toHtml(this.state.value);
      return
;
    }
  },

  createEditor: function () {
    if (this.state.codeMirrorMode) {
      this.createCodeMirrorEditor();
    } else {
      this.createReadonlyEditor();
    }
  },

  createCodeMirrorEditor: function () {
    var cmValue = this.translator.encodeValue(this.state.value);

    var options = {
      tabindex: this.props.tabIndex,
      value: cmValue,
      specialChars: this.translator.specialCharsRegexp,
      specialCharPlaceholder: this.createTagNode
    };

    var textBox = this.refs.textBox.getDOMNode();
    textBox.innerHTML = ''; // release any previous read-only content so it can be GC'ed

    this.codeMirror = CodeMirror(textBox, options);
    this.codeMirror.on('change', this.onCodeMirrorChange);
  },

  onCodeMirrorChange: function () {
    if (this.updatingCodeMirror) {
      // avoid recursive update cycle, and mark the code mirror manual update as done
      this.updatingCodeMirror = false;
      return;
    }

    var newValue = this.translator.decodeValue(this.codeMirror.getValue());
    this.onChangeValue(newValue);
    this.setState({value: newValue});
  },

  createReadonlyEditor: function () {
    this.removeCodeMirrorEditor();
    var textBoxNode = this.refs.textBox.getDOMNode();
    textBoxNode.innerHTML = this.translator.toHtml(this.state.value);
  },

  removeCodeMirrorEditor: function () {
    var textBoxNode = this.refs.textBox.getDOMNode();
    var cmNode = textBoxNode.firstChild;
    textBoxNode.removeChild(cmNode);
    this.codeMirror = null;
  },

  switchToCodeMirror: function () {
    if (!this.state.codeMirrorMode) {
      this.setState({codeMirrorMode: true});
    }
  },

  displayChoicesDropdown: function () {
    // TODO: hack to toggle bootstrap dropdown. Won't need once we get rid of BS in choices dropdown.
    $(this.refs.choicesDropdown.refs.dropdownToggle.getDOMNode()).dropdown('toggle');
  },

  // Create pill box style for display inside CM. For example
  // '\ue000' becomes 'First Name'
  createTagNode: function (char) {
    var node = document.createElement('span');
    var label = this.translator.decodeChar(char);
    React.render(
      {label},
      node
    );
    return node;
  }
});
<|endoftext|>