diff --git a/assets/static/alerts.js b/assets/static/alerts.js index aa733ac86..cbd3ea388 100644 --- a/assets/static/alerts.js +++ b/assets/static/alerts.js @@ -1,184 +1,186 @@ -/* globals LRUCache */ // lru.js -/* globals moment */ // moment.js -/* globals Autocomplete, Colors, Config, Counter, Grid, Templates, Summary, UI, Unsee */ +const LRUMap = require("lru"); +const moment = require("moment"); +const $ = require("jquery"); -/* exported Alerts */ -var Alerts = (function() { +const autocomplete = require("./autocomplete"); +const colors = require("./colors"); +const counter = require("./counter"); +const grid = require("./grid"); +const summary = require("./summary"); +const templates = require("./templates"); +const ui = require("./ui"); +const unsee = require("./unsee"); - var labelCache = new LRUCache(1000); +var labelCache = new LRUMap(1000); - function AlertGroup(groupData) { - $.extend(this, groupData); - } +function AlertGroup(groupData) { + $.extend(this, groupData); +} - AlertGroup.prototype.Render = function() { - return Templates.Render("alertGroup", { - group: this, - alertLimit: 5 +AlertGroup.prototype.Render = function() { + return templates.render("alertGroup", { + group: this, + alertLimit: 5 + }); +}; + +// called after group was rendered for the first time +AlertGroup.prototype.Added = function() { + ui.setupAlertGroupUI($("#" + this.id)); +}; + +AlertGroup.prototype.Update = function() { + // hide popovers in this group + $("#" + this.id + " [data-label-type='filter']").popover("hide"); + + // remove all elements prior to content update to purge all event listeners and hooks + $.each($("#" + this.id).find(".panel-body, .panel-heading"), function(i, elem) { + $(elem).remove(); + }); + + $("#" + this.id).html($(this.Render()).html()); + $("#" + this.id).data("hash", this.hash); + + // pulse the badge to show that group content was changed, repeat it twice + $("#" + this.id + " > .panel > .panel-heading > .badge:in-viewport").finish().fadeOut(300).fadeIn(300).fadeOut(300).fadeIn(300); +}; + + +function destroyGroup(groupID) { + $("#" + groupID + " [data-label-type='filter']").popover("hide"); + $("#" + groupID + " [data-toggle='tooltip']").tooltip("hide"); + $.each($("#" + groupID).find(".panel-body, .panel-heading"), function(i, elem) { + $(elem).remove(); + }); + grid.remove($("#" + groupID)); +} + +function sortMapByKey(mapToSort) { + var keys = Object.keys(mapToSort); + keys.sort(); + var sorted = []; + $.each(keys, function(i, key) { + sorted.push({ + key: key, + value: mapToSort[key], + text: key + ": " + mapToSort[key] }); + }); + return sorted; +} + +function getLabelAttrs(key, value) { + var label = key + ": " + value; + + var attrs = labelCache.get(label); + if (attrs !== undefined) return attrs; + + attrs = { + text: label, + class: "label label-list " + colors.getClass(key, value), + style: colors.getStyle(key, value) }; + labelCache.set(label, attrs); + return attrs; +} - // called after group was rendered for the first time - AlertGroup.prototype.Added = function() { - UI.SetupAlertGroupUI($("#" + this.id)); - }; +function humanizeTimestamps() { + var now = moment(); + // change timestamp labels to be relative + $.each($(".label-ts"), function(i, elem) { + var ts = moment($(elem).data("ts"), moment.ISO_8601); + var label = ts.fromNow(); + $(elem).find(".label-ts-span").text(label); + $(elem).attr("data-ts-title", ts.toString()); + var tsAge = now.diff(ts, "minutes"); + if (tsAge >= 0 && tsAge < 3) { + $(elem).addClass("recent-alert").find(".incident-indicator").removeClass("hidden"); + } else { + $(elem).removeClass("recent-alert").find(".incident-indicator").addClass("hidden"); + } + }); - AlertGroup.prototype.Update = function() { - // hide popovers in this group - $("#" + this.id + " [data-label-type='filter']").popover("hide"); + // flash recent alerts + $(".recent-alert:in-viewport").finish().fadeToggle(300).fadeToggle(300).fadeToggle(300).fadeToggle(300); +} - // remove all elements prior to content update to purge all event listeners and hooks - $.each($("#" + this.id).find(".panel-body, .panel-heading"), function(i, elem) { - $(elem).remove(); +function updateAlerts(apiResponse) { + var alertCount = 0; + var groups = {}; + + var summaryData = {}; + $.each(apiResponse.counters, function(labelKey, counters){ + $.each(counters, function(labelVal, hits){ + summaryData[labelKey + ": " + labelVal] = hits; }); + }); + summary.update(summaryData); - $("#" + this.id).html($(this.Render()).html()); - $("#" + this.id).data("hash", this.hash); + $.each(apiResponse.groups, function(i, groupData) { + var alertGroup = new AlertGroup(groupData); + groups[alertGroup.id] = alertGroup; + alertCount += alertGroup.alerts.length; + }); - // pulse the badge to show that group content was changed, repeat it twice - $("#" + this.id + " > .panel > .panel-heading > .badge:in-viewport").finish().fadeOut(300).fadeIn(300).fadeOut(300).fadeIn(300); - }; + counter.setCounter(alertCount); + grid.show(); + var dirty = false; - var destroyGroup = function(groupID) { - $("#" + groupID + " [data-label-type='filter']").popover("hide"); - $("#" + groupID + " [data-toggle='tooltip']").tooltip("hide"); - $.each($("#" + groupID).find(".panel-body, .panel-heading"), function(i, elem) { - $(elem).remove(); - }); - Grid.Remove($("#" + groupID)); - }; - - var sortMapByKey = function(mapToSort) { - var keys = Object.keys(mapToSort); - keys.sort(); - var sorted = []; - $.each(keys, function(i, key) { - sorted.push({ - key: key, - value: mapToSort[key], - text: key + ": " + mapToSort[key] - }); - }); - return sorted; - }; - - var labelAttrs = function(key, value) { - var label = key + ": " + value; - - var attrs = labelCache.get(label); - if (attrs !== undefined) return attrs; - - attrs = { - text: label, - class: "label label-list " + Colors.GetClass(key, value), - style: Colors.Get(key, value) - }; - labelCache.set(label, attrs); - return attrs; - }; - - var humanizeTimestamps = function() { - var now = moment(); - // change timestamp labels to be relative - $.each($(".label-ts"), function(i, elem) { - var ts = moment($(elem).data("ts"), moment.ISO_8601); - var label = ts.fromNow(); - $(elem).find(".label-ts-span").text(label); - $(elem).attr("data-ts-title", ts.toString()); - var tsAge = now.diff(ts, "minutes"); - if (tsAge >= 0 && tsAge < 3) { - $(elem).addClass("recent-alert").find(".incident-indicator").removeClass("hidden"); - } else { - $(elem).removeClass("recent-alert").find(".incident-indicator").addClass("hidden"); - } - }); - - // flash recent alerts - $(".recent-alert:in-viewport").finish().fadeToggle(300).fadeToggle(300).fadeToggle(300).fadeToggle(300); - }; - - var updateAlerts = function(apiResponse) { - var alertCount = 0; - var groups = {}; - - var summaryData = {}; - $.each(apiResponse.counters, function(labelKey, counters){ - $.each(counters, function(labelVal, hits){ - summaryData[labelKey + ": " + labelVal] = hits; - }); - }); - Summary.Update(summaryData); - - $.each(apiResponse.groups, function(i, groupData) { - var alertGroup = new AlertGroup(groupData); - groups[alertGroup.id] = alertGroup; - alertCount += alertGroup.alerts.length; - }); - - Counter.Set(alertCount); - Grid.Show(); - - var dirty = false; - - // handle already existing groups - $.each(Grid.Items(), function(i, existingGroup) { - var group = groups[existingGroup.id]; - if (group !== undefined) { - // group still present, check if changed - if (group.hash != existingGroup.dataset.hash) { - // group was updated, render changes - group.Update(); - existingGroup.dataset.hash = group.hash; - dirty = true; - - // https://github.com/twbs/bootstrap/issues/16376 - setTimeout(function() { - group.Added(); - }, 1000); - } - } else { - // group is gone, destroy it - destroyGroup(existingGroup.id); + // handle already existing groups + $.each(grid.items(), function(i, existingGroup) { + var group = groups[existingGroup.id]; + if (group !== undefined) { + // group still present, check if changed + if (group.hash != existingGroup.dataset.hash) { + // group was updated, render changes + group.Update(); + existingGroup.dataset.hash = group.hash; dirty = true; - } - // remove from groups dict as we're done with it - delete groups[existingGroup.id]; - }); - // render new groups - var content = []; - $.each(groups, function(id, group) { - content.push(group.Render()); - }); - // append new groups in chunks - if (content.length > 0) { - Grid.Append($(content.splice(0, 100).join("\n"))); + // https://github.com/twbs/bootstrap/issues/16376 + setTimeout(function() { + group.Added(); + }, 1000); + } + } else { + // group is gone, destroy it + destroyGroup(existingGroup.id); dirty = true; } + // remove from groups dict as we're done with it + delete groups[existingGroup.id]; + }); - // always refresh timestamp labels - humanizeTimestamps(); + // render new groups + var content = []; + $.each(groups, function(id, group) { + content.push(group.Render()); + }); + // append new groups in chunks + if (content.length > 0) { + grid.append($(content.splice(0, 100).join("\n"))); + dirty = true; + } - $.each(groups, function(id, group) { - group.Added(); - }); + // always refresh timestamp labels + humanizeTimestamps(); - if (dirty) { - Autocomplete.Reset(); - Grid.Redraw(); - if (Config.GetOption("flash").Get()) { - Unsee.Flash(); - } + $.each(groups, function(id, group) { + group.Added(); + }); + + if (dirty) { + autocomplete.reset(); + grid.redraw(); + if (Config.GetOption("flash").Get()) { + unsee.flash(); } + } - }; +} - return { - Update: updateAlerts, - SortMapByKey: sortMapByKey, - GetLabelAttrs: labelAttrs - }; - -}()); +exports.updateAlerts = updateAlerts; +exports.sortMapByKey = sortMapByKey; +exports.getLabelAttrs = getLabelAttrs; diff --git a/assets/static/alerts.test.js b/assets/static/alerts.test.js new file mode 100644 index 000000000..9e6ebe050 --- /dev/null +++ b/assets/static/alerts.test.js @@ -0,0 +1,24 @@ +test("alerts getLabelAttrs()", () => { + window.jQuery = require("jquery"); + const alerts = require("./alerts"); + expect(alerts.getLabelAttrs("foo", "bar")).toEqual({ + "class": "label label-list label-warning", + "style": "", + "text": "foo: bar" + }); + expect(alerts.getLabelAttrs("@state", "active")).toEqual({ + "class": "label label-list label-danger", + "style": "", + "text": "@state: active" + }); + expect(alerts.getLabelAttrs("@state", "suppressed")).toEqual({ + "class": "label label-list label-success", + "style": "", + "text": "@state: suppressed" + }); + expect(alerts.getLabelAttrs("@state", "unprocessed")).toEqual({ + "class": "label label-list label-default", + "style": "", + "text": "@state: unprocessed" + }); +});