"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GrafanaProxy = void 0;
var Config_1 = require("./Config");
var Crypt_1 = require("./Crypt");
var openflow_api_1 = require("@openiap/openflow-api");
var DBHelper_1 = require("./DBHelper");
var OAuthProvider_1 = require("./OAuthProvider");
var OAuthServer = require("oauth2-server");
var url = require("url");
var Auth_1 = require("./Auth");
var Logger_1 = require("./Logger");
var Request = OAuthServer.Request;
var Response = OAuthServer.Response;
var template = [
    '====BEGIN LICENSE====',
    '{{&licenseVersion}}',
    '{{&email}}',
    '{{&expirationDate}}',
    '{{&serial}}',
    '=====END LICENSE====='
].join('\n');
var GrafanaProxy = /** @class */ (function () {
    function GrafanaProxy() {
    }
    GrafanaProxy.parseJson = function (txt, reviver, context) {
        context = context || 20;
        try {
            return JSON.parse(txt, reviver);
        }
        catch (e) {
            if (typeof txt !== "string") {
                var isEmptyArray = Array.isArray(txt) && txt.length === 0;
                var errorMessage = "Cannot parse " +
                    (isEmptyArray ? "an empty array" : String(txt));
                throw new TypeError(errorMessage);
            }
            var syntaxErr = e.message.match(/^Unexpected token.*position\s+(\d+)/i);
            var errIdx = syntaxErr
                ? +syntaxErr[1]
                : e.message.match(/^Unexpected end of JSON.*/i)
                    ? txt.length - 1
                    : null;
            if (errIdx != null) {
                var start = errIdx <= context
                    ? 0
                    : errIdx - context;
                var end = errIdx + context >= txt.length
                    ? txt.length
                    : errIdx + context;
                e.message += " while parsing near \"" + (start === 0 ? "" : "...") + txt.slice(start, end) + (end === txt.length ? "" : "...") + "\"";
            }
            else {
                e.message += " while parsing \"" + txt.slice(0, context * 2) + "\"";
            }
            throw e;
        }
    };
    GrafanaProxy.configure = function (app) {
        return __awaiter(this, void 0, void 0, function () {
            var ot_grafana_search, ot_grafana_query;
            var _this = this;
            return __generator(this, function (_a) {
                try {
                    Logger_1.Logger.License.validate();
                }
                catch (error) {
                    Logger_1.Logger.instanse.info(error.message ? error.message : error);
                }
                if (!Logger_1.Logger.License.validlicense)
                    return [2 /*return*/];
                app.all("/grafana", function (req, res, next) {
                    _this.logRequest(req.body, "/");
                    _this.setCORSHeaders(req, res);
                    if (!Logger_1.Logger.License.validlicense) {
                        var result = { message: Logger_1.Logger.License.licenserror, statusCode: 500 };
                        console.error(Logger_1.Logger.License.licenserror); // Log error message in our server"s console
                        var json = JSON.stringify(result);
                        res.status(500).send(json); // All HTTP requests must have a response, so let's send back an error with its status code and message
                        return;
                    }
                    if (!Config_1.Config.db.isConnected) {
                        res.send({
                            status: "error",
                            display_status: "Error",
                            message: "OpenFlow not connected to DB"
                        });
                    }
                    else {
                        res.send({
                            status: "success",
                            display_status: "Success",
                            message: "MongoDB Connection test OK"
                        });
                    }
                    // next()
                });
                ot_grafana_search = Logger_1.Logger.otel.meter.createValueRecorder('openflow_http_grafana_search_request_duration', {
                    description: 'Duration of Grafana Search requests',
                    boundaries: Logger_1.Logger.otel.default_boundaries
                });
                ot_grafana_query = Logger_1.Logger.otel.meter.createValueRecorder('openflow_http_grafana_query_request_duration', {
                    description: 'Duration of Grafana Queries',
                    boundaries: Logger_1.Logger.otel.default_boundaries
                });
                app.all("/grafana/search", function (req, res, next) { return __awaiter(_this, void 0, void 0, function () {
                    var span, result, json, ot_end, route, jwt, openflowusername, openflowpassword, xgrafanauser, json, tuser, user, json, requestId, queryStates, query, result_1, output, substitutions, i1, i2, queryArgs, error_1, error_2;
                    return __generator(this, function (_a) {
                        switch (_a.label) {
                            case 0:
                                span = Logger_1.Logger.otel.startSpan("grafana.search");
                                _a.label = 1;
                            case 1:
                                _a.trys.push([1, 15, 16, 17]);
                                this.logRequest(req.body, "/search");
                                this.setCORSHeaders(req, res);
                                if (!Logger_1.Logger.License.validlicense) {
                                    result = { message: Logger_1.Logger.License.licenserror, statusCode: 500 };
                                    console.error(Logger_1.Logger.License.licenserror); // Log error message in our server"s console
                                    json = JSON.stringify(result);
                                    res.status(500).send(json); // All HTTP requests must have a response, so let's send back an error with its status code and message
                                    return [2 /*return*/];
                                }
                                ot_end = Logger_1.Logger.otel.startTimer();
                                route = url.parse(req.url).pathname;
                                jwt = null;
                                openflowusername = (req.body.options && req.body.options.openflowusername ? req.body.options.openflowusername : "");
                                if (openflow_api_1.NoderedUtil.IsNullEmpty(openflowusername))
                                    openflowusername = (req.body.securedata && req.body.securedata.openflowusername ? req.body.securedata.openflowusername : "");
                                openflowpassword = (req.body.securedata && req.body.securedata.openflowpassword ? req.body.securedata.openflowpassword : "");
                                if (!(req.headers && req.headers["authorization"])) return [3 /*break*/, 3];
                                return [4 /*yield*/, this.GetJWT(req, res, span)];
                            case 2:
                                jwt = _a.sent();
                                return [3 /*break*/, 9];
                            case 3:
                                if (!(!openflow_api_1.NoderedUtil.IsNullEmpty(openflowusername) && !openflow_api_1.NoderedUtil.IsNullEmpty(openflowpassword))) return [3 /*break*/, 7];
                                xgrafanauser = (req.headers ? req.headers["X-Grafana-User"] : "");
                                if (!openflow_api_1.NoderedUtil.IsNullEmpty(xgrafanauser) && xgrafanauser != openflowusername) {
                                    json = JSON.stringify({ message: "Please login again" });
                                    return [2 /*return*/, res.status(500).send(json)];
                                }
                                tuser = null;
                                user = Auth_1.Auth.getUser(openflowusername + ":" + openflowpassword, "grafana");
                                if (!openflow_api_1.NoderedUtil.IsNullUndefinded(user)) return [3 /*break*/, 6];
                                return [4 /*yield*/, Auth_1.Auth.ValidateByPassword(openflowusername, openflowpassword, span)];
                            case 4:
                                user = _a.sent();
                                if (!!openflow_api_1.NoderedUtil.IsNullUndefinded(user)) return [3 /*break*/, 6];
                                return [4 /*yield*/, Auth_1.Auth.AddUser(user, openflowusername + ":" + openflowpassword, "grafana")];
                            case 5:
                                _a.sent();
                                _a.label = 6;
                            case 6:
                                if (user != null)
                                    tuser = openflow_api_1.TokenUser.From(user);
                                if (tuser != null)
                                    jwt = Crypt_1.Crypt.createToken(tuser, Config_1.Config.shorttoken_expires_in);
                                return [3 /*break*/, 9];
                            case 7: return [4 /*yield*/, this.GetJWT(req, res, span)];
                            case 8:
                                jwt = _a.sent();
                                _a.label = 9;
                            case 9:
                                if (jwt == null) {
                                    json = JSON.stringify({ message: "Please login again" });
                                    return [2 /*return*/, res.status(500).send(json)];
                                }
                                requestId = ++this.requestIdCounter;
                                queryStates = [];
                                this.requestsPending[requestId] = queryStates;
                                _a.label = 10;
                            case 10:
                                _a.trys.push([10, 13, , 14]);
                                query = (req.body.target ? req.body.target : (Array.isArray(req.body.queries) ? req.body.queries[0] : req.body.queries));
                                if (!(req.body.target == "getcollections")) return [3 /*break*/, 12];
                                return [4 /*yield*/, Config_1.Config.db.ListCollections(jwt)];
                            case 11:
                                result_1 = _a.sent();
                                result_1 = result_1.filter(function (x) { return !x.name.endsWith("_hist"); });
                                result_1 = result_1.filter(function (x) { return x.name != "fs.chunks"; });
                                result_1 = result_1.filter(function (x) { return x.name != "fs.files"; });
                                result_1 = result_1.filter(function (x) { return x.name != "uploads.files"; });
                                result_1 = result_1.filter(function (x) { return x.name != "uploads.chunks"; });
                                delete this.requestsPending[requestId];
                                output = result_1.map(function (x) { return x.name; });
                                res.json(output);
                                return [2 /*return*/];
                            case 12:
                                substitutions = {
                                    "$from": new Date(req.body.from),
                                    "$to": new Date(req.body.to),
                                    "$dateBucketCount": this.getBucketCount(req.body.from, req.body.to, req.body.intervalMs)
                                };
                                if (req.body.optionalOptions && req.body.optionalOptions.range && req.body.optionalOptions.range.from) {
                                    substitutions = {
                                        "$from": new Date(req.body.optionalOptions.range.from),
                                        "$to": new Date(req.body.optionalOptions.range.to),
                                        "$dateBucketCount": this.getBucketCount(req.body.optionalOptions.range.from, req.body.optionalOptions.range.to, req.body.optionalOptions.range.intervalMs)
                                    };
                                    if (req.body.optionalOptions.range.from.indexOf("z") === -1 && req.body.optionalOptions.range.from.indexOf("Z") === -1 && req.body.optionalOptions.range.from.indexOf("T") === -1) {
                                        i1 = parseInt(req.body.range.from);
                                        i2 = parseInt(req.body.range.to);
                                        substitutions.$from = new Date(parseInt(req.body.range.from));
                                        substitutions.$to = new Date(parseInt(req.body.range.to));
                                        substitutions.$dateBucketCount = this.getBucketCount(substitutions.$from, substitutions.$to, req.body.intervalMs);
                                    }
                                }
                                queryArgs = this.parseQuery(query.query ? query.query : query, substitutions, query);
                                if (queryArgs.err != null) {
                                    this.queryError(requestId, queryArgs.err, res, next);
                                    return [2 /*return*/];
                                }
                                else {
                                    this.doTemplateQuery(requestId, queryArgs, req.body.db, jwt, req, res, next, span);
                                }
                                return [3 /*break*/, 14];
                            case 13:
                                error_1 = _a.sent();
                                this.queryError(requestId, error_1, res, next);
                                return [3 /*break*/, 14];
                            case 14:
                                Logger_1.Logger.otel.endTimer(ot_end, ot_grafana_search, { route: route, code: res.statusCode.toString(), method: req.method });
                                return [3 /*break*/, 17];
                            case 15:
                                error_2 = _a.sent();
                                span.recordException(error_2);
                                throw error_2;
                            case 16:
                                Logger_1.Logger.otel.endSpan(span);
                                return [7 /*endfinally*/];
                            case 17: return [2 /*return*/];
                        }
                    });
                }); });
                // Called to get graph points
                app.all("/grafana/query", function (req, res, next) { return __awaiter(_this, void 0, void 0, function () {
                    var span, result, json, ot_end, route, substitutions, i1, i2, jwt, openflowusername, openflowpassword, xgrafanauser, json, tuser, user, json, requestId, queryStates, error, queryId, tg, query, queryArgs, error_3;
                    return __generator(this, function (_a) {
                        switch (_a.label) {
                            case 0:
                                span = Logger_1.Logger.otel.startSpan("grafana.query");
                                _a.label = 1;
                            case 1:
                                _a.trys.push([1, 10, 11, 12]);
                                this.logRequest(req.body, "/query");
                                this.setCORSHeaders(req, res);
                                if (!Logger_1.Logger.License.validlicense) {
                                    result = { message: Logger_1.Logger.License.licenserror, statusCode: 500 };
                                    console.error(Logger_1.Logger.License.licenserror); // Log error message in our server"s console
                                    json = JSON.stringify(result);
                                    res.status(500).send(json); // All HTTP requests must have a response, so let's send back an error with its status code and message
                                    span.recordException(Logger_1.Logger.License.licenserror);
                                    return [2 /*return*/];
                                }
                                ot_end = Logger_1.Logger.otel.startTimer();
                                route = url.parse(req.url).pathname;
                                if (openflow_api_1.NoderedUtil.IsNullUndefinded(req.body.range)) {
                                    res.send({
                                        status: "success",
                                        display_status: "Success",
                                        message: "MongoDB Connection test OK"
                                    });
                                    // next();
                                    return [2 /*return*/];
                                }
                                substitutions = {
                                    "$from": new Date(req.body.range.from),
                                    "$to": new Date(req.body.range.to),
                                    "$dateBucketCount": this.getBucketCount(req.body.range.from, req.body.range.to, req.body.intervalMs)
                                };
                                if (req.body.range.from.indexOf("z") === -1 && req.body.range.from.indexOf("Z") === -1 && req.body.range.from.indexOf("T") === -1) {
                                    i1 = parseInt(req.body.range.from);
                                    i2 = parseInt(req.body.range.to);
                                    substitutions.$from = new Date(parseInt(req.body.range.from));
                                    substitutions.$to = new Date(parseInt(req.body.range.to));
                                    substitutions.$dateBucketCount = this.getBucketCount(substitutions.$from, substitutions.$to, req.body.intervalMs);
                                }
                                jwt = null;
                                openflowusername = (req.body.options && req.body.options.openflowusername ? req.body.options.openflowusername : "");
                                if (openflow_api_1.NoderedUtil.IsNullEmpty(openflowusername))
                                    openflowusername = (req.body.securedata && req.body.securedata.openflowusername ? req.body.securedata.openflowusername : "");
                                openflowpassword = (req.body.securedata && req.body.securedata.openflowpassword ? req.body.securedata.openflowpassword : "");
                                if (!(req.headers && req.headers["authorization"])) return [3 /*break*/, 3];
                                return [4 /*yield*/, this.GetJWT(req, res, span)];
                            case 2:
                                jwt = _a.sent();
                                return [3 /*break*/, 9];
                            case 3:
                                if (!(!openflow_api_1.NoderedUtil.IsNullEmpty(openflowusername) && !openflow_api_1.NoderedUtil.IsNullEmpty(openflowpassword))) return [3 /*break*/, 7];
                                xgrafanauser = (req.headers ? req.headers["X-Grafana-User"] : "");
                                if (!openflow_api_1.NoderedUtil.IsNullEmpty(xgrafanauser) && xgrafanauser != openflowusername) {
                                    json = JSON.stringify({ message: "Please login again" });
                                    span.recordException("Please login again");
                                    return [2 /*return*/, res.status(500).send(json)];
                                }
                                tuser = null;
                                user = Auth_1.Auth.getUser(openflowusername + ":" + openflowpassword, "grafana");
                                if (!openflow_api_1.NoderedUtil.IsNullUndefinded(user)) return [3 /*break*/, 6];
                                return [4 /*yield*/, Auth_1.Auth.ValidateByPassword(openflowusername, openflowpassword, span)];
                            case 4:
                                user = _a.sent();
                                if (!!openflow_api_1.NoderedUtil.IsNullUndefinded(user)) return [3 /*break*/, 6];
                                return [4 /*yield*/, Auth_1.Auth.AddUser(user, openflowusername + ":" + openflowpassword, "grafana")];
                            case 5:
                                _a.sent();
                                _a.label = 6;
                            case 6:
                                if (user != null)
                                    tuser = openflow_api_1.TokenUser.From(user);
                                if (tuser != null)
                                    jwt = Crypt_1.Crypt.createToken(tuser, Config_1.Config.shorttoken_expires_in);
                                return [3 /*break*/, 9];
                            case 7: return [4 /*yield*/, this.GetJWT(req, res, span)];
                            case 8:
                                jwt = _a.sent();
                                _a.label = 9;
                            case 9:
                                if (jwt == null) {
                                    json = JSON.stringify({ message: "Please login again" });
                                    span.recordException("Please login again");
                                    return [2 /*return*/, res.status(500).send(json)];
                                }
                                requestId = ++this.requestIdCounter;
                                queryStates = [];
                                this.requestsPending[requestId] = queryStates;
                                error = false;
                                req.body.targets = req.body.targets.filter(function (x) { return x.hide != true; });
                                for (queryId = 0; queryId < req.body.targets.length && !error; queryId++) {
                                    tg = req.body.targets[queryId];
                                    if (tg.intervalMs == null || tg.intervalMs == "") {
                                        tg.intervalMs = 1000;
                                    }
                                    if (tg.maxDataPoints == null || tg.maxDataPoints == "") {
                                        tg.maxDataPoints = 100;
                                    }
                                    substitutions["$intervalMs"] = tg.intervalMs;
                                    substitutions["$maxDataPoints"] = tg.maxDataPoints;
                                    query = tg.target;
                                    // if (query == null && tg.queryText != null) query = tg.queryText;
                                    if (tg.queryText != null)
                                        query = tg.queryText;
                                    queryArgs = this.parseQuery(query, substitutions, tg);
                                    queryArgs.type = tg.type;
                                    queryArgs.refId = tg.refId;
                                    if (tg.queryType)
                                        queryArgs.type = tg.queryType;
                                    if (queryArgs.err != null) {
                                        span.recordException(queryArgs.err);
                                        this.queryError(requestId, queryArgs.err, res, next);
                                        error = true;
                                        return [2 /*return*/];
                                    }
                                    else {
                                        // Add to the state
                                        queryStates.push({ pending: true, refId: tg.refId, type: queryArgs.type });
                                        // Run the query
                                        this.runAggregateQuery(requestId, queryId, req.body, queryArgs, jwt, req, res, next, span);
                                    }
                                }
                                if (req.body.targets.length == 0) {
                                    res.json([]);
                                }
                                Logger_1.Logger.otel.endTimer(ot_end, ot_grafana_query, { route: route, code: res.statusCode.toString(), method: req.method });
                                return [3 /*break*/, 12];
                            case 10:
                                error_3 = _a.sent();
                                span.recordException(error_3);
                                throw error_3;
                            case 11:
                                Logger_1.Logger.otel.endSpan(span);
                                return [7 /*endfinally*/];
                            case 12: return [2 /*return*/];
                        }
                    });
                }); });
                return [2 /*return*/];
            });
        });
    };
    GrafanaProxy.setCORSHeaders = function (req, res) {
        // res.setHeader("Access-Control-Allow-Origin", "*");
        // let port = 3000;
        // res.setHeader("Access-Control-Allow-Origin", req.protocol + "://" + req.get("host") + (port == 80 || port == 443 ? "" : ":" + port) + req.originalUrl);
        // res.setHeader("Access-Control-Allow-Origin", req.headers["referer"]);
        if (req.headers["origin"] == null)
            res.setHeader("Access-Control-Allow-Origin", "*");
        if (req.headers["origin"] != null)
            res.setHeader("Access-Control-Allow-Origin", req.headers["origin"]);
        res.setHeader("Access-Control-Allow-Methods", "POST");
        res.setHeader("Access-Control-Allow-Headers", "accept, content-type");
    };
    GrafanaProxy.logRequest = function (body, type) {
        if (this.logRequests) {
            console.log("REQUEST: " + type + ":\n" + JSON.stringify(body, null, 2));
        }
    };
    GrafanaProxy.logQuery = function (query, options) {
        if (this.logQueries) {
            console.log("Query:");
            console.log(JSON.stringify(query, null, 2));
            if (options != null) {
                console.log("Query Options:");
                console.log(JSON.stringify(options, null, 2));
            }
        }
    };
    GrafanaProxy.queryError = function (requestId, err, res, next) {
        // We only 1 return error per query so it may have been removed from the list
        if (requestId in this.requestsPending) {
            // Remove request
            delete this.requestsPending[requestId];
            // Send back error
            // next(err);
            // res.status(500).json(err);
            // next(new Error(err));
            var result = { message: err.message, statusCode: 500 };
            console.error(err.message); // Log error message in our server"s console
            if (!err.statusCode)
                err.statusCode = 500; // If err has no specified error code, set error code to "Internal Server Error (500)"
            var json = JSON.stringify(result);
            res.status(err.statusCode).send(json); // All HTTP requests must have a response, so let's send back an error with its status code and message
        }
    };
    // Called when query finished
    GrafanaProxy.queryFinished = function (requestId, queryId, results, res, next) {
        // We only 1 return error per query so it may have been removed from the list
        if (requestId in this.requestsPending) {
            var queryStatus = this.requestsPending[requestId];
            // Mark this as finished
            queryStatus[queryId].pending = false;
            queryStatus[queryId].results = results;
            // See if we're all done
            var done = true;
            for (var i = 0; i < queryStatus.length; i++) {
                if (queryStatus[i].pending == true) {
                    done = false;
                    break;
                }
            }
            // If query done, send back results
            if (done) {
                // Concatenate results
                var output = [];
                for (var i = 0; i < queryStatus.length; i++) {
                    var queryResults = queryStatus[i];
                    var refId = queryStatus[i].refId;
                    var type = queryStatus[i].type;
                    // const res = { "target": queryStatus[i].refId, "datapoints": queryResults.results }
                    // output.push(res)
                    if (type == "timeserie" || queryResults.results.type == "timeserie") {
                        var keys = Object.keys(queryResults.results);
                        for (var k = 0; k < keys.length; k++) {
                            var tg = keys[k];
                            // if (tg != "type") queryResults.results[tg].refId = refId;
                            // if (tg != "type") queryResults.results[tg].target = refId;
                            output.push(queryResults.results[tg]);
                        }
                    }
                    else {
                        queryResults.results.refId = refId;
                        // queryResults.results.Target = refId;
                        // queryResults.results.values = queryResults.results.rows;
                        output.push(queryResults.results);
                    }
                }
                res.json(output);
                // next()
                // Remove request
                delete this.requestsPending[requestId];
            }
        }
    };
    GrafanaProxy.parseQuery = function (query, substitutions, tg) {
        var doc = {};
        if (tg && tg.dtfield)
            doc.dtfield = tg.dtfield;
        var queryErrors = [];
        if (openflow_api_1.NoderedUtil.IsNullEmpty(query)) {
            doc.err = new Error("Query cannot be empty.");
            return doc;
        }
        query = query.trim();
        if (query.substring(0, 3) != "db.") {
            doc.err = new Error("Query must start with db.");
            Logger_1.Logger.instanse.debug(query);
            return doc;
        }
        // Query is of the form db.<collection> . aggregate or db.<collection>.find
        // Split on the first ( after db.
        var openBracketIndex = query.indexOf("(", 3);
        if (openBracketIndex == -1) {
            queryErrors.push("Can't find opening bracket");
        }
        else {
            // Split the first bit - it's the collection name and operation ( must be aggregate )
            var parts = query.substring(3, openBracketIndex).split(".");
            // Collection names can have .s so last part is operation, rest is the collection name
            if (parts.length >= 2) {
                doc.operation = parts.pop().trim();
                doc.collection = parts.join(".");
            }
            else {
                queryErrors.push("Invalid collection and operation syntax");
            }
            query = query.substring(openBracketIndex);
            do {
                try {
                    openBracketIndex = query.indexOf("(");
                    var closeBracketIndex = query.indexOf(")", openBracketIndex);
                    var operation = query.substring(0, openBracketIndex).trim();
                    if (operation.startsWith("."))
                        operation = operation.substring(1);
                    if (openflow_api_1.NoderedUtil.IsNullEmpty(operation))
                        operation = doc.operation; // The first time, operation will be empty
                    var args = query.substring(openBracketIndex + 1, closeBracketIndex);
                    query = query.substring(closeBracketIndex + 1);
                    if (operation == "aggregate") {
                        // Wrap args in array syntax so we can check for optional options arg
                        // args = args.trim();
                        // if (!args.startsWith("[") && !args.endsWith("]")) 
                        args = "[" + args + "]";
                        // let docs = JSON.parse(args);
                        var docs = this.parseJson(args, null, null);
                        // First Arg is pipeline
                        doc.pipeline = docs[0];
                        // If we have 2 top level args, second is agg options
                        if (docs.length == 2) {
                            doc.options = docs[1];
                        }
                        // Replace with substitutions
                        for (var i = 0; i < doc.pipeline.length; i++) {
                            var stage = doc.pipeline[i];
                            this.forIn(stage, function (obj, key, value) {
                                if (typeof (value) == "string") {
                                    if (value in substitutions) {
                                        obj[key] = substitutions[value];
                                    }
                                }
                            });
                        }
                    }
                    else if (operation == "find") {
                        // Wrap args in array syntax so we can check for optional options arg
                        args = "[" + args + "]";
                        // let docs = JSON.parse(args);
                        var docs = this.parseJson(args, null, null);
                        // First Arg is pipeline
                        doc.query = docs[0];
                        if (doc.query === undefined)
                            doc.query = {};
                        // If we have 2 top level args, second is options
                        if (docs.length == 2) {
                            doc.options = docs[1];
                        }
                        // Replace with substitutions
                        var stage = doc.query;
                        this.forIn(stage, function (obj, key, value) {
                            if (typeof (value) == "string") {
                                if (value in substitutions) {
                                    obj[key] = substitutions[value];
                                }
                            }
                        });
                    }
                    else if (operation == "limit") {
                        doc.limit = args;
                    }
                    else if (operation == "skip") {
                        doc.skip = args;
                    }
                    else if (operation == "sort") {
                        doc.sort = this.parseJson(args, null, null);
                    }
                    else if (operation == "projection") {
                        doc.projection = this.parseJson(args, null, null);
                    }
                    else {
                        queryErrors.push("Unknown operation " + operation + ", only aggregate supported");
                    }
                }
                catch (error) {
                    queryErrors.push(error.message);
                    console.error(args);
                }
                openBracketIndex = query.indexOf("(", 1);
            } while (openBracketIndex > -1);
        }
        if (queryErrors.length > 0) {
            doc.err = new Error("Failed to parse query - " + queryErrors.join(":"));
        }
        else {
            var dtfield = (!openflow_api_1.NoderedUtil.IsNullEmpty(doc.dtfield) ? doc.dtfield : "_created");
            // const dtfield = (!NoderedUtil.IsNullEmpty(doc.dtfield) ? doc.dtfield : "lastseen");
            if (tg.intervalMs && dtfield && doc.operation == "aggregate") {
                if (substitutions && substitutions.$from && substitutions.$to) {
                    var fromo = {};
                    fromo[dtfield] = { "$gte": substitutions.$from };
                    var too = {};
                    too[dtfield] = { "$lte": substitutions.$to };
                    var _and = { "$and": [fromo, too] };
                    if (dtfield && doc.operation == "find") {
                        if (JSON.stringify(doc.query) == "{}") {
                            doc.query = _and;
                        }
                        else {
                            doc.query = { "$and": [_and, doc.query] };
                        }
                    }
                    else if (dtfield && doc.operation == "aggregate") {
                        doc.pipeline.unshift({ "$match": _and });
                    }
                }
            }
        }
        return doc;
    };
    GrafanaProxy.forIn = function (obj, processFunc) {
        var key;
        for (key in obj) {
            var value = obj[key];
            processFunc(obj, key, value);
            if (value != null && typeof (value) == "object") {
                this.forIn(value, processFunc);
            }
        }
    };
    GrafanaProxy.getBucketCount = function (from, to, intervalMs) {
        var boundaries = [];
        var current = new Date(from).getTime();
        var toMs = new Date(to).getTime();
        var count = 0;
        while (current < toMs) {
            current += intervalMs;
            count++;
        }
        return count;
    };
    GrafanaProxy.getTableResults = function (docs) {
        var columns = {};
        // Build superset of columns
        for (var i = 0; i < docs.length; i++) {
            var doc = docs[i];
            // Go through all properties
            for (var propName in doc) {
                // See if we need to add a new column
                if (!(propName in columns)) {
                    columns[propName] =
                        {
                            text: propName,
                            type: "text"
                        };
                    if (typeof doc[propName] === "number")
                        columns[propName].type = "number";
                    if (typeof doc[propName] === "boolean")
                        columns[propName].type = "boolean";
                }
            }
        }
        // Build return rows
        var rows = [];
        for (var i = 0; i < docs.length; i++) {
            var doc = docs[i];
            var row = [];
            // All cols
            for (var colName in columns) {
                var col = columns[colName];
                if (col.text in doc) {
                    row.push(doc[col.text]);
                }
                else {
                    row.push(null);
                }
            }
            rows.push(row);
        }
        var results = {};
        results["table"] = {
            columns: Object.values(columns),
            rows: rows,
            type: "table"
        };
        return results["table"];
        // return results
    };
    GrafanaProxy.getTimeseriesResults = function (docs, queryArgs) {
        var results = {};
        for (var i = 0; i < docs.length; i++) {
            var doc = docs[i];
            var tg = null;
            if (!openflow_api_1.NoderedUtil.IsNullEmpty(doc["name"])) {
                tg = doc["name"];
            }
            // if (!NoderedUtil.IsNullEmpty(doc["name"])) {
            //     tg = doc["name"];
            // } else if (!NoderedUtil.IsNullEmpty(doc["_id"])) {
            //     tg = doc["_id"];
            // }
            var dp = null;
            if (tg in results) {
                dp = results[tg];
            }
            else {
                dp = { "target": tg, "datapoints": [] };
                results[tg] = dp;
                results[tg] = dp;
                if (typeof tg === "number") {
                    dp.target = tg.toString();
                }
                else if (typeof tg === "string") {
                    dp.target = tg.toString();
                }
                else if (typeof tg === "boolean") {
                    dp.target = tg.toString();
                }
                else {
                    dp.target = undefined;
                }
                // if (typeof tg === "number") {
                //     dp.target = tg.toString();
                // } else if (typeof tg !== "number") {
                //     // dp.target = tg.toString();
                //     // dp.target = JSON.stringify(tg);
                //     dp.target = queryArgs.refId
                // }
                // username
            }
            var ts = null;
            var value = 1;
            if (!openflow_api_1.NoderedUtil.IsNullEmpty(queryArgs.dtfield) && !openflow_api_1.NoderedUtil.IsNullEmpty(doc[queryArgs.dtfield])) {
                ts = doc[queryArgs.dtfield].getTime();
            }
            else if (!openflow_api_1.NoderedUtil.IsNullEmpty(doc["ts"])) {
                ts = doc["ts"].getTime();
            }
            else if (!openflow_api_1.NoderedUtil.IsNullEmpty(doc["_created"])) {
                ts = doc["_created"].getTime();
                // results[tg].datapoints.push([doc["value"], doc["_created"].getTime()])
            }
            else {
                throw new Error("Failed locating timeseries datatime as either ts or _created from result");
            }
            if (!openflow_api_1.NoderedUtil.IsNullEmpty(doc["value"])) {
                value = parseFloat(doc["value"]);
            }
            else if (!openflow_api_1.NoderedUtil.IsNullEmpty(doc["Value"])) {
                value = parseFloat(doc["Value"]);
            }
            results[tg].datapoints.push([value, ts]);
        }
        return results;
    };
    // Runs a query to support templates. Must returns documents of the form
    // { _id : <id> }
    GrafanaProxy.doTemplateQuery = function (requestId, queryArgs, db, jwt, req, res, next, parent) {
        return __awaiter(this, void 0, void 0, function () {
            var span, collectionname, result, limit, skip, sort, projection, output, i, doc, keys, error_4;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        span = Logger_1.Logger.otel.startSubSpan("grafana.runAggregateQuery", parent);
                        if (!(queryArgs.err == null)) return [3 /*break*/, 9];
                        if (!Config_1.Config.db.isConnected) {
                            this.queryError(requestId, new Error("OpenFlow not connected to DB"), res, next);
                            span.recordException("OpenFlow not connected to DB");
                            Logger_1.Logger.otel.endSpan(span);
                            return [2 /*return*/];
                        }
                        // Remove request from list
                        if (requestId in this.requestsPending) {
                            delete this.requestsPending[requestId];
                        }
                        collectionname = queryArgs.collection;
                        span.setAttribute("collectionname", collectionname);
                        span.setAttribute("operation", queryArgs.operation);
                        _a.label = 1;
                    case 1:
                        _a.trys.push([1, 7, , 8]);
                        result = void 0;
                        if (!(queryArgs.operation == "aggregate")) return [3 /*break*/, 3];
                        this.replaceinstring(queryArgs.pipeline);
                        return [4 /*yield*/, Config_1.Config.db.aggregate(queryArgs.pipeline, collectionname, jwt, undefined, span)];
                    case 2:
                        result = _a.sent();
                        return [3 /*break*/, 6];
                    case 3:
                        if (!(queryArgs.operation == "find")) return [3 /*break*/, 5];
                        limit = 6000, skip = 0, sort = null, projection = null;
                        this.replaceinstring(queryArgs.query);
                        if (!openflow_api_1.NoderedUtil.IsNullEmpty(queryArgs.limit))
                            limit = parseInt(queryArgs.limit);
                        if (!openflow_api_1.NoderedUtil.IsNullEmpty(queryArgs.skip))
                            skip = queryArgs.skip;
                        if (!openflow_api_1.NoderedUtil.IsNullUndefinded(queryArgs.sort))
                            sort = queryArgs.sort;
                        if (!openflow_api_1.NoderedUtil.IsNullUndefinded(queryArgs.projection))
                            projection = queryArgs.projection;
                        return [4 /*yield*/, Config_1.Config.db.query(queryArgs.query, projection, limit, skip, sort, collectionname, jwt, undefined, undefined, span)];
                    case 4:
                        result = _a.sent();
                        return [3 /*break*/, 6];
                    case 5:
                        this.queryError(requestId, "unknown operation" + queryArgs.operation, res, next);
                        span.recordException("unknown operation" + queryArgs.operation);
                        Logger_1.Logger.otel.endSpan(span);
                        return [2 /*return*/];
                    case 6:
                        output = [];
                        for (i = 0; i < result.length; i++) {
                            doc = result[i];
                            keys = Object.keys(doc);
                            // if (doc["_id"] != null) {
                            if (keys.length == 1) {
                                // output.push(doc["_id"])
                                output.push(doc[keys[0]]);
                            }
                            else {
                                if (openflow_api_1.NoderedUtil.IsNullEmpty(doc.text) && !openflow_api_1.NoderedUtil.IsNullEmpty(doc.name)) {
                                    doc.text = doc.name;
                                    delete doc.name;
                                }
                                if (openflow_api_1.NoderedUtil.IsNullEmpty(doc.value) && !openflow_api_1.NoderedUtil.IsNullEmpty(doc._id)) {
                                    doc.value = doc._id;
                                }
                                if (openflow_api_1.NoderedUtil.IsNullEmpty(doc.value) && !openflow_api_1.NoderedUtil.IsNullEmpty(doc.text)) {
                                    doc.value = doc.text;
                                }
                                keys.forEach(function (key) {
                                    if (key != "text" && key != "value") {
                                        delete doc[key];
                                    }
                                });
                                if (!openflow_api_1.NoderedUtil.IsNullEmpty(doc["text"]) || !openflow_api_1.NoderedUtil.IsNullEmpty(doc["value"]) ||
                                    !openflow_api_1.NoderedUtil.IsNullUndefinded(doc["_id"])) {
                                    output.push(doc);
                                }
                            }
                        }
                        res.json(output);
                        return [3 /*break*/, 8];
                    case 7:
                        error_4 = _a.sent();
                        console.error(error_4);
                        this.queryError(requestId, error_4, res, next);
                        span.recordException(error_4);
                        Logger_1.Logger.otel.endSpan(span);
                        return [2 /*return*/];
                    case 8: return [3 /*break*/, 10];
                    case 9:
                        // next(queryArgs.err)
                        Logger_1.Logger.otel.endSpan(span);
                        this.queryError(requestId, queryArgs.err, res, next);
                        _a.label = 10;
                    case 10: return [2 /*return*/];
                }
            });
        });
    };
    GrafanaProxy.replaceinstring = function (pipeline) {
        var keys = Object.keys(pipeline);
        for (var i = 0; i < keys.length; i++) {
            var key = keys[i];
            var value = pipeline[key];
            if (key == "$in" && typeof value === "string") {
                if (value.startsWith("{")) {
                    value = value.substring(1, value.length - 1);
                }
                var array = value.split(",");
                pipeline[key] = array;
            }
            if (typeof value == "object") {
                this.replaceinstring(value);
            }
        }
    };
    // Run an aggregate query. Must return documents of the form
    // { value : 0.34334, ts : <epoch time in seconds> }
    GrafanaProxy.runAggregateQuery = function (requestId, queryId, body, queryArgs, jwt, req, res, next, parent) {
        return __awaiter(this, void 0, void 0, function () {
            var span, collectionname, docs, limit, skip, sort, projection, results, err_1;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        span = Logger_1.Logger.otel.startSubSpan("grafana.runAggregateQuery", parent);
                        if (!Config_1.Config.db.isConnected) {
                            span.recordException("OpenFlow not connected to DB");
                            Logger_1.Logger.otel.endSpan(span);
                            this.queryError(requestId, new Error("OpenFlow not connected to DB"), res, next);
                            return [2 /*return*/];
                        }
                        collectionname = queryArgs.collection;
                        span.setAttribute("collectionname", collectionname);
                        span.setAttribute("operation", queryArgs.operation);
                        _a.label = 1;
                    case 1:
                        _a.trys.push([1, 7, 8, 9]);
                        docs = void 0;
                        if (!(queryArgs.operation == "aggregate")) return [3 /*break*/, 3];
                        this.logQuery(queryArgs.pipeline, queryArgs.options);
                        this.replaceinstring(queryArgs.pipeline);
                        return [4 /*yield*/, Config_1.Config.db.aggregate(queryArgs.pipeline, collectionname, jwt, undefined, span)];
                    case 2:
                        docs = _a.sent();
                        return [3 /*break*/, 6];
                    case 3:
                        if (!(queryArgs.operation == "find")) return [3 /*break*/, 5];
                        this.logQuery(queryArgs.query, queryArgs.options);
                        this.replaceinstring(queryArgs.query);
                        limit = 6000, skip = 0, sort = null, projection = null;
                        if (!openflow_api_1.NoderedUtil.IsNullEmpty(queryArgs.limit))
                            limit = parseInt(queryArgs.limit);
                        if (!openflow_api_1.NoderedUtil.IsNullEmpty(queryArgs.skip))
                            skip = queryArgs.skip;
                        if (!openflow_api_1.NoderedUtil.IsNullUndefinded(queryArgs.sort))
                            sort = queryArgs.sort;
                        if (!openflow_api_1.NoderedUtil.IsNullUndefinded(queryArgs.projection))
                            projection = queryArgs.projection;
                        return [4 /*yield*/, Config_1.Config.db.query(queryArgs.query, projection, limit, skip, sort, collectionname, jwt, undefined, undefined, span)];
                    case 4:
                        docs = _a.sent();
                        return [3 /*break*/, 6];
                    case 5:
                        span.recordException("unknown operation" + queryArgs.operation);
                        this.queryError(requestId, "unknown operation" + queryArgs.operation, res, next);
                        return [2 /*return*/];
                    case 6:
                        results = {};
                        if (queryArgs.type == "timeserie") {
                            results = this.getTimeseriesResults(docs, queryArgs);
                        }
                        else {
                            results = this.getTableResults(docs);
                        }
                        // Mark query as finished - will send back results when all queries finished
                        this.queryFinished(requestId, queryId, results, res, next);
                        return [3 /*break*/, 9];
                    case 7:
                        err_1 = _a.sent();
                        span.recordException(err_1);
                        this.queryError(requestId, err_1, res, next);
                        return [3 /*break*/, 9];
                    case 8:
                        Logger_1.Logger.otel.endSpan(span);
                        return [7 /*endfinally*/];
                    case 9: return [2 /*return*/];
                }
            });
        });
    };
    GrafanaProxy.GetJWT = function (req, res, parent) {
        return __awaiter(this, void 0, void 0, function () {
            var span, user_1, request, response, atoken, accesstoken, res2, user, jwt, error_5, token, user_2, jwt, error_6, username, user_3, user_4, jwt, error_7;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        span = Logger_1.Logger.otel.startSubSpan("grafana.GetJWT", parent);
                        _a.label = 1;
                    case 1:
                        _a.trys.push([1, 21, 22, 23]);
                        if (!req.user) return [3 /*break*/, 2];
                        user_1 = openflow_api_1.TokenUser.From(req.user);
                        return [2 /*return*/, Crypt_1.Crypt.createToken(user_1, Config_1.Config.shorttoken_expires_in)];
                    case 2:
                        request = new Request(req);
                        response = new Response(res);
                        atoken = request.headers['authorization'];
                        if (!!openflow_api_1.NoderedUtil.IsNullEmpty(atoken)) return [3 /*break*/, 16];
                        if (atoken.indexOf(" ") > -1)
                            atoken = atoken.substring(atoken.indexOf(" ") + 1);
                        _a.label = 3;
                    case 3:
                        _a.trys.push([3, 8, , 9]);
                        return [4 /*yield*/, OAuthProvider_1.OAuthProvider.instance.oidc.AccessToken.find(atoken)];
                    case 4:
                        accesstoken = _a.sent();
                        if (!(accesstoken != null)) return [3 /*break*/, 7];
                        return [4 /*yield*/, OAuthProvider_1.OAuthProvider.instance.oidc.Account.findAccount(undefined, accesstoken.accountId)];
                    case 5:
                        res2 = _a.sent();
                        if (!(res2 != null)) return [3 /*break*/, 7];
                        return [4 /*yield*/, res2.claims(null, null, null, null)];
                    case 6:
                        user = _a.sent();
                        if (!openflow_api_1.NoderedUtil.IsNullUndefinded(user)) {
                            jwt = Crypt_1.Crypt.createToken(user, "1d");
                            return [2 /*return*/, jwt];
                        }
                        else {
                            console.error("Failed locating user with id " + token.user._id + " ????");
                        }
                        _a.label = 7;
                    case 7: return [3 /*break*/, 9];
                    case 8:
                        error_5 = _a.sent();
                        return [3 /*break*/, 9];
                    case 9:
                        _a.trys.push([9, 15, , 16]);
                        return [4 /*yield*/, OAuthProvider_1.OAuthProvider.instance.oauthServer.authenticate(request, response)];
                    case 10:
                        token = _a.sent();
                        if (!(!openflow_api_1.NoderedUtil.IsNullUndefinded(token) && !openflow_api_1.NoderedUtil.IsNullUndefinded(token.user) && !openflow_api_1.NoderedUtil.IsNullEmpty(token.user._id))) return [3 /*break*/, 14];
                        user_2 = Auth_1.Auth.getUser(token.user._id, "grafana");
                        if (!openflow_api_1.NoderedUtil.IsNullUndefinded(user_2)) return [3 /*break*/, 13];
                        return [4 /*yield*/, DBHelper_1.DBHelper.FindById(token.user._id, undefined, span)];
                    case 11:
                        user_2 = _a.sent();
                        if (!!openflow_api_1.NoderedUtil.IsNullUndefinded(user_2)) return [3 /*break*/, 13];
                        return [4 /*yield*/, Auth_1.Auth.AddUser(user_2, token.user._id, "grafana")];
                    case 12:
                        _a.sent();
                        _a.label = 13;
                    case 13:
                        if (!openflow_api_1.NoderedUtil.IsNullUndefinded(user_2)) {
                            jwt = Crypt_1.Crypt.createToken(user_2, "1d");
                            return [2 /*return*/, jwt];
                        }
                        else {
                            console.error("Failed locating user with id " + token.user._id + " ????");
                        }
                        _a.label = 14;
                    case 14: return [3 /*break*/, 16];
                    case 15:
                        error_6 = _a.sent();
                        if (error_6.message) {
                            console.error(error_6.message);
                        }
                        else {
                            console.error(error_6);
                        }
                        return [3 /*break*/, 16];
                    case 16:
                        username = req.headers["X-Grafana-User"];
                        if (openflow_api_1.NoderedUtil.IsNullEmpty(username))
                            username = req.headers["x-grafana-user"];
                        if (!!openflow_api_1.NoderedUtil.IsNullEmpty(username)) return [3 /*break*/, 20];
                        user_3 = Auth_1.Auth.getUser(username, "grafana");
                        if (!openflow_api_1.NoderedUtil.IsNullUndefinded(user_3)) return [3 /*break*/, 19];
                        return [4 /*yield*/, DBHelper_1.DBHelper.FindByUsername(username, null, span)];
                    case 17:
                        user_4 = _a.sent();
                        if (!!openflow_api_1.NoderedUtil.IsNullUndefinded(user_4)) return [3 /*break*/, 19];
                        return [4 /*yield*/, Auth_1.Auth.AddUser(user_4, username, "grafana")];
                    case 18:
                        _a.sent();
                        _a.label = 19;
                    case 19:
                        if (!openflow_api_1.NoderedUtil.IsNullUndefinded(user_3)) {
                            jwt = Crypt_1.Crypt.createToken(user_3, "1d");
                            return [2 /*return*/, jwt];
                        }
                        else {
                            console.error("Failed locating user with username " + username + "????");
                        }
                        _a.label = 20;
                    case 20: return [2 /*return*/, null];
                    case 21:
                        error_7 = _a.sent();
                        span.recordException(error_7);
                        throw error_7;
                    case 22:
                        Logger_1.Logger.otel.endSpan(span);
                        return [7 /*endfinally*/];
                    case 23: return [2 /*return*/];
                }
            });
        });
    };
    GrafanaProxy.license_public_key = Config_1.Config.getEnv("license_public_key", "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFyVVJncVpLcHpmbTJISEUxTUJ3YwpmZW5oMk1PZVZQbEJMS2dUM3l2TXIvQlpTSHVPb3pkaFprZ3JoNVVOSVlXZ1JYNVR1SDVoeVZ1ODJzOFhoR2RLCnZ5ajFGWGt2SHVubWp4ZFpGWEhDcjJCVE12Y09VTnZsTk5MWFpTOXh0YXE1cHdBemh0SlpjbmZLdTVHcjZNR1MKbVFxcHpVeDRWRVIyN3hSaVo0QlhXUkQ4MGNVaHAvTHF3UGswdHYwU0FTNTNZVXVRNkJIcWp1MDdHd3lFYzZaUQpJNjlLRVpLaHMvbk5GRHlVOHhwT0JoL0EwR1dMT0wrb01JeU9NSkZ6a2RPZURDQzlnVzNabmJBOHo0dHZUc3VHCmI3c2ZidTJlSE5MK09ucWlzamdFYStrZWlIQXZXaTlCa2dGbWdZWnNaWk5ENnovWkpHUi84MElzekV4THRqL3YKNFFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==");
    GrafanaProxy.logRequests = false;
    GrafanaProxy.logQueries = false;
    GrafanaProxy.requestIdCounter = 0;
    GrafanaProxy.requestsPending = {};
    return GrafanaProxy;
}());
exports.GrafanaProxy = GrafanaProxy;
//# sourceMappingURL=grafana-proxy.js.map