Skip to content

Commit

Permalink
Implement browsing of previous query expressions (#3486)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lovisa Svallingson authored and juliusv committed Dec 21, 2017
1 parent 78625f8 commit a8ff643
Show file tree
Hide file tree
Showing 5 changed files with 273 additions and 152 deletions.
136 changes: 69 additions & 67 deletions web/ui/bindata.go

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions web/ui/static/css/graph.css
Expand Up @@ -6,6 +6,30 @@ body {
font-size: 11px;
}

div.query-history {
font-size: 0.8em;
padding-top: 1em;
}

div.query-history:hover {
cursor: pointer;
}

div.query-history button {
background-color: transparent;
border: none;
outline: none;
padding: 0;
}

div.query-history.is-checked {
color: #286090;
}

div.page-options {
display: flex;
}

.graph_wrapper {
margin-top: 12px;
}
Expand Down
File renamed without changes.
258 changes: 174 additions & 84 deletions web/ui/static/js/graph.js → web/ui/static/js/graph/index.js
Expand Up @@ -3,6 +3,9 @@ var graphTemplate;

var SECOND = 1000;

/**
* Graph
*/
Prometheus.Graph = function(element, options, handleChange, handleRemove) {
this.el = element;
this.graphHTML = null;
Expand Down Expand Up @@ -61,8 +64,8 @@ Prometheus.Graph.prototype.initialize = function() {

self.expr = graphWrapper.find("textarea[name=expr]");
self.expr.keypress(function(e) {
// Enter was pressed without the shift key.
if (e.which == 13 && !e.shiftKey) {
const enter = 13;
if (e.which == enter && !e.shiftKey) {
self.queryForm.submit();
e.preventDefault();
}
Expand Down Expand Up @@ -183,8 +186,14 @@ Prometheus.Graph.prototype.initialize = function() {
});

self.checkTimeDrift();
// initialize query history
if (!localStorage.getItem("history")) {
localStorage.setItem("history", JSON.stringify([]));
}
self.populateInsertableMetrics();

queryHistory.bindHistoryEvents(self);

if (self.expr.val()) {
self.submitQuery();
}
Expand Down Expand Up @@ -230,9 +239,10 @@ Prometheus.Graph.prototype.populateInsertableMetrics = function() {
self.showError("Error loading available metrics!");
return;
}
var metrics = json.data;
for (var i = 0; i < metrics.length; i++) {
self.insertMetric[0].options.add(new Option(metrics[i], metrics[i]));

pageConfig.allMetrics = json.data; // todo: do we need self.allMetrics? Or can it just live on the page
for (var i = 0; i < pageConfig.allMetrics.length; i++) {
self.insertMetric[0].options.add(new Option(pageConfig.allMetrics[i], pageConfig.allMetrics[i]));
}

self.fuzzyResult = {
Expand All @@ -241,48 +251,55 @@ Prometheus.Graph.prototype.populateInsertableMetrics = function() {
map: {}
}

self.expr.typeahead({
source: metrics,
items: "all",
matcher: function(item) {
// If we have result for current query, skip
if (self.fuzzyResult.query !== this.query) {
self.fuzzyResult.query = this.query;
self.fuzzyResult.map = {};
self.fuzzyResult.result = fuzzy.filter(this.query.replace(/ /g, ''), metrics, {
pre: '<strong>',
post: '</strong>'
});
self.fuzzyResult.result.forEach(function(r) {
self.fuzzyResult.map[r.original] = r;
});
}

return item in self.fuzzyResult.map;
},

sorter: function(items) {
items.sort(function(a,b) {
var i = self.fuzzyResult.map[b].score - self.fuzzyResult.map[a].score;
return i === 0 ? a.localeCompare(b) : i;
});
return items;
},

highlighter: function (item) {
return $('<div>' + self.fuzzyResult.map[item].string + '</div>')
},
});
// This needs to happen after attaching the typeahead plugin, as it
// otherwise breaks the typeahead functionality.
self.expr.focus();
self.initTypeahead(self);
},
error: function() {
self.showError("Error loading available metrics!");
},
});
};

Prometheus.Graph.prototype.initTypeahead = function(self) {
const historyIsChecked = $("div.query-history").hasClass("is-checked");
const source = historyIsChecked ? pageConfig.allMetrics.concat(JSON.parse(localStorage.getItem("history"))) : pageConfig.allMetrics;

self.expr.typeahead({
source,
items: "all",
matcher: function (item) {
// If we have result for current query, skip
if (self.fuzzyResult.query !== this.query) {
self.fuzzyResult.query = this.query;
self.fuzzyResult.map = {};
self.fuzzyResult.result = fuzzy.filter(this.query.replace(/ /g, ""), this.source, {
pre: "<strong>",
post: "</strong>"
});
self.fuzzyResult.result.forEach(function(r) {
self.fuzzyResult.map[r.original] = r;
});
}

return item in self.fuzzyResult.map;
},

sorter: function (items) {
items.sort(function(a,b) {
var i = self.fuzzyResult.map[b].score - self.fuzzyResult.map[a].score;
return i === 0 ? a.localeCompare(b) : i;
});
return items;
},

highlighter: function (item) {
return $("<div>" + self.fuzzyResult.map[item].string + "</div>");
},
});
// This needs to happen after attaching the typeahead plugin, as it
// otherwise breaks the typeahead functionality.
self.expr.focus();
}

Prometheus.Graph.prototype.getOptions = function() {
var self = this;
var options = {};
Expand Down Expand Up @@ -429,6 +446,7 @@ Prometheus.Graph.prototype.submitQuery = function() {
self.showError(json.error);
return;
}
queryHistory.handleHistory(self);
success(json.data, textStatus);
},
error: function(xhr, resp) {
Expand Down Expand Up @@ -807,33 +825,22 @@ Prometheus.Graph.prototype.formatKMBT = function(y) {
}
}

function escapeHTML(string) {
var entityMap = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': '&quot;',
"'": '&#39;',
"/": '&#x2F;'
};

return String(string).replace(/[&<>"'\/]/g, function (s) {
return entityMap[s];
});
}

Prometheus.Page = function() {
this.graphs = [];
/**
* Page
*/
const pageConfig = {
graphs: []
};

Prometheus.Page = function() {};

Prometheus.Page.prototype.init = function() {
var graphOptions = this.parseURL();
if (graphOptions.length === 0) {
graphOptions.push({});
}

graphOptions.forEach(this.addGraph, this);

$("#add_graph").click(this.addGraph.bind(this, {}));
};

Expand All @@ -855,15 +862,17 @@ Prometheus.Page.prototype.addGraph = function(options) {
this.removeGraph.bind(this)
);

this.graphs.push(graph);
// this.graphs.push(graph);
pageConfig.graphs.push(graph);

$(window).resize(function() {
graph.resizeGraph();
});
};

// NOTE: This needs to be kept in sync with /util/strutil/strconv.go:GraphLinkForExpression
Prometheus.Page.prototype.updateURL = function() {
var queryString = this.graphs.map(function(graph, index) {
var queryString = pageConfig.graphs.map(function(graph, index) {
var graphOptions = graph.getOptions();
var queryParamHelper = new Prometheus.Page.QueryParamHelper();
var queryObject = queryParamHelper.generateQueryObject(graphOptions, index);
Expand All @@ -874,7 +883,7 @@ Prometheus.Page.prototype.updateURL = function() {
};

Prometheus.Page.prototype.removeGraph = function(graph) {
this.graphs = this.graphs.filter(function(g) {return g !== graph});
pageConfig.graphs = pageConfig.graphs.filter(function(g) {return g !== graph});
};

Prometheus.Page.QueryParamHelper = function() {};
Expand Down Expand Up @@ -950,29 +959,6 @@ Prometheus.Page.QueryParamHelper.prototype.generateQueryObject = function(graphO
return queryObject;
};

function init() {
$.ajaxSetup({
cache: false
});

$.ajax({
url: PATH_PREFIX + "/static/js/graph_template.handlebar?v=" + BUILD_VERSION,
success: function(data) {

graphTemplate = data;
Mustache.parse(data);
if (isDeprecatedGraphURL()) {
redirectToMigratedURL();
} else {
var Page = new Prometheus.Page();
Page.init();
}
}
});
}



// These two methods (isDeprecatedGraphURL and redirectToMigratedURL)
// are added only for backward compatibility to old query format.
function isDeprecatedGraphURL() {
Expand Down Expand Up @@ -1004,4 +990,108 @@ function redirectToMigratedURL() {
window.location = PATH_PREFIX + "/graph?" + query;
}

/**
* Query History helper functions
* **/
const queryHistory = {
bindHistoryEvents: function(graph) {
const targetEl = $('div.query-history');
const icon = $(targetEl).children('i');
targetEl.off('click');

if (JSON.parse(localStorage.getItem('enable-query-history'))) {
this.toggleOn(targetEl);
}

targetEl.on('click', function() {
if (icon.hasClass('glyphicon-unchecked')) {
queryHistory.toggleOn(targetEl);
} else if (icon.hasClass('glyphicon-check')) {
queryHistory.toggleOff(targetEl);
}
});
},

handleHistory: function(graph) {
const query = graph.expr.val();
const isSimpleMetric = pageConfig.allMetrics.indexOf(query) !== -1;
if (isSimpleMetric) {
return;
}

let parsedQueryHistory = JSON.parse(localStorage.getItem('history'));
const hasStoredQuery = parsedQueryHistory.indexOf(query) !== -1;
if (hasStoredQuery) {
parsedQueryHistory.splice(parsedQueryHistory.indexOf(query), 1);
}

parsedQueryHistory.push(query);
const queryCount = parsedQueryHistory.length;
parsedQueryHistory = parsedQueryHistory.slice(queryCount - 50, queryCount);

localStorage.setItem('history', JSON.stringify(parsedQueryHistory));

this.updateTypeaheadMetricSet(parsedQueryHistory);
},

toggleOn: function(targetEl) {
this.updateTypeaheadMetricSet(JSON.parse(localStorage.getItem('history')));

$(targetEl).children('i').removeClass('glyphicon-unchecked').addClass('glyphicon-check');
targetEl.addClass('is-checked');
localStorage.setItem('enable-query-history', true);
},

toggleOff: function(targetEl) {
this.updateTypeaheadMetricSet();

$(targetEl).children('i').removeClass('glyphicon-check').addClass('glyphicon-unchecked');
targetEl.removeClass('is-checked');
localStorage.setItem('enable-query-history', false);
},

updateTypeaheadMetricSet: function(metricSet) {
pageConfig.graphs.forEach(function(graph) {
graph.expr.data('typeahead').source = metricSet ? pageConfig.allMetrics.concat(metricSet) : pageConfig.allMetrics;
});
}
};

function escapeHTML(string) {
var entityMap = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': '&quot;',
"'": '&#39;',
"/": '&#x2F;'
};

return String(string).replace(/[&<>"'\/]/g, function (s) {
return entityMap[s];
});
}

function init() {
$.ajaxSetup({
cache: false
});

$.ajax({
url: PATH_PREFIX + "/static/js/graph/graph_template.handlebar?v=" + BUILD_VERSION,
success: function(data) {

graphTemplate = data;
Mustache.parse(data);
if (isDeprecatedGraphURL()) {
redirectToMigratedURL();
} else {
var Page = new Prometheus.Page();
Page.init();
}
}
});
}

$(init);

0 comments on commit a8ff643

Please sign in to comment.