mirror of
https://github.com/prymitive/karma
synced 2026-05-05 03:16:51 +00:00
Rewrite unsee.js as CommonJS, add basic test
This commit is contained in:
@@ -1,318 +1,325 @@
|
||||
/* globals Raven */ // raven.js
|
||||
/* globals moment */ // moment.js
|
||||
const $ = require("jquery");
|
||||
const moment = require("moment");
|
||||
const Raven = require("raven-js");
|
||||
|
||||
/* globals Alerts, Autocomplete, Colors, Config, Counter, Grid, Filters, Progress, Silence, Summary, Templates, UI, Unsilence, Watchdog */
|
||||
const alerts = require("./alerts");
|
||||
const autocomplete = require("./autocomplete");
|
||||
const colors = require("./colors");
|
||||
const config = require("./config");
|
||||
const counter = require("./counter");
|
||||
const grid = require("./grid");
|
||||
const filters = require("./filters");
|
||||
const progress = require("./progress");
|
||||
const silence = require("./silence");
|
||||
const summary = require("./summary");
|
||||
const templates = require("./templates");
|
||||
const ui = require("./ui");
|
||||
const unsilence = require("./unsilence");
|
||||
const watchdog = require("./watchdog");
|
||||
|
||||
/* exported Unsee */
|
||||
var Unsee = (function() {
|
||||
var timer = false;
|
||||
var version = false;
|
||||
var refreshInterval = 15;
|
||||
var hiddenAt = false;
|
||||
|
||||
var timer = false;
|
||||
var version = false;
|
||||
var refreshInterval = 15;
|
||||
var hiddenAt = false;
|
||||
var selectors = {
|
||||
refreshButton: "#refresh",
|
||||
errors: "#errors",
|
||||
instanceErrors: "#instance-errors",
|
||||
};
|
||||
|
||||
var selectors = {
|
||||
refreshButton: "#refresh",
|
||||
errors: "#errors",
|
||||
instanceErrors: "#instance-errors",
|
||||
};
|
||||
function parseAJAXError(xhr, textStatus) {
|
||||
// default to textStatus, it's usually just "error" string
|
||||
var err = textStatus;
|
||||
if (xhr.readyState === 0) {
|
||||
// ajax() completed but request wasn't send
|
||||
err = "Connection to the remote endpoint failed";
|
||||
} else if (xhr.responseJSON && xhr.responseJSON.error) {
|
||||
// there's response JSON and an error key in it
|
||||
err = xhr.responseJSON.error;
|
||||
} else if (xhr.responseText) {
|
||||
// else check response as a string
|
||||
err = xhr.responseText;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
// when user switches to a different tab but keeps unsee tab open in the background
|
||||
// some browsers (like Chrome) will try to apply some forms of throttling for the JS
|
||||
// code, to ensure that there are no visual artifacts (like state alerts not removed from the page)
|
||||
// redraw all alerts if we detect that the user switches from a different tab to unsee
|
||||
var setupPageVisibilityHandler = function() {
|
||||
// based on https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
|
||||
if (typeof document.hidden !== "undefined" && typeof document.addEventListener !== "undefined") {
|
||||
document.addEventListener("visibilitychange", function() {
|
||||
if (document.hidden) {
|
||||
// when tab is hidden set a timestamp of that event
|
||||
hiddenAt = moment().utc().unix();
|
||||
} else {
|
||||
// when user switches back check if we have a timestamp
|
||||
// and if autorefresh is enable
|
||||
if (hiddenAt && Config.GetOption("autorefresh").Get()) {
|
||||
// get the diff to see how long tab was hidden
|
||||
var diff = moment().utc().unix() - hiddenAt;
|
||||
if (diff > refreshInterval) {
|
||||
// if it was hidden for more than one refresh cycle
|
||||
// then manually refresh alerts to ensure everything
|
||||
// is up to date
|
||||
Unsee.Reload();
|
||||
}
|
||||
}
|
||||
hiddenAt = false;
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
};
|
||||
function getRefreshRate() {
|
||||
return refreshInterval;
|
||||
}
|
||||
|
||||
var parseAJAXError = function(xhr, textStatus) {
|
||||
// default to textStatus, it's usually just "error" string
|
||||
var err = textStatus;
|
||||
if (xhr.readyState === 0) {
|
||||
// ajax() completed but request wasn't send
|
||||
err = "Connection to the remote endpoint failed";
|
||||
} else if (xhr.responseJSON && xhr.responseJSON.error) {
|
||||
// there's response JSON and an error key in it
|
||||
err = xhr.responseJSON.error;
|
||||
} else if (xhr.responseText) {
|
||||
// else check response as a string
|
||||
err = xhr.responseText;
|
||||
}
|
||||
return err;
|
||||
};
|
||||
|
||||
var init = function() {
|
||||
Progress.Init();
|
||||
|
||||
Config.Init({
|
||||
CopySelector: "#copy-settings-with-filter",
|
||||
SaveSelector: "#save-default-filter",
|
||||
ResetSelector: "#reset-settings"
|
||||
});
|
||||
Config.Load();
|
||||
|
||||
Counter.Init();
|
||||
Summary.Init();
|
||||
Grid.Init();
|
||||
Autocomplete.Init();
|
||||
Filters.Init();
|
||||
Watchdog.Init(30, 60*15); // set watchdog to 15 minutes
|
||||
|
||||
$(selectors.refreshButton).click(function() {
|
||||
if (!$(selectors.refreshButton).prop("disabled")) {
|
||||
Unsee.Reload();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
setupPageVisibilityHandler();
|
||||
};
|
||||
|
||||
var getRefreshRate = function() {
|
||||
return refreshInterval;
|
||||
};
|
||||
|
||||
var setRefreshRate = function(seconds) {
|
||||
var rate = parseInt(seconds);
|
||||
function setRefreshRate(seconds) {
|
||||
var rate = parseInt(seconds);
|
||||
if (isNaN(rate) || rate === null) {
|
||||
// if passed rate is incorrect use select value
|
||||
rate = config.getOption("refresh").Get();
|
||||
if (isNaN(rate) || rate === null) {
|
||||
// if passed rate is incorrect use select value
|
||||
rate = Config.GetOption("refresh").Get();
|
||||
if (isNaN(rate) || rate === null) {
|
||||
// if that's also borked use default 15
|
||||
rate = 15;
|
||||
}
|
||||
// if that's also borked use default 15
|
||||
rate = 15;
|
||||
}
|
||||
// don't allow setting refresh rate lower than 1s
|
||||
if (rate < 1) {
|
||||
rate = 1;
|
||||
}
|
||||
refreshInterval = rate;
|
||||
Progress.Reset();
|
||||
};
|
||||
}
|
||||
// don't allow setting refresh rate lower than 1s
|
||||
if (rate < 1) {
|
||||
rate = 1;
|
||||
}
|
||||
refreshInterval = rate;
|
||||
progress.resetTimer();
|
||||
}
|
||||
|
||||
var needsUpgrade = function(responseVersion) {
|
||||
if (version === false) {
|
||||
version = responseVersion;
|
||||
return false;
|
||||
}
|
||||
return version != responseVersion;
|
||||
};
|
||||
function needsUpgrade(responseVersion) {
|
||||
if (version === false) {
|
||||
version = responseVersion;
|
||||
return false;
|
||||
}
|
||||
return version != responseVersion;
|
||||
}
|
||||
|
||||
var updateIsReady = function() {
|
||||
Progress.Complete();
|
||||
$(selectors.refreshButton).prop("disabled", true);
|
||||
Counter.Hide();
|
||||
};
|
||||
function updateIsReady() {
|
||||
progress.complete();
|
||||
$(selectors.refreshButton).prop("disabled", true);
|
||||
counter.hide();
|
||||
}
|
||||
|
||||
var updateCompleted = function() {
|
||||
Counter.Show();
|
||||
Filters.UpdateCompleted();
|
||||
Progress.Complete();
|
||||
$(selectors.refreshButton).prop("disabled", false);
|
||||
// hack for fixing padding since input can grow and change height
|
||||
$("body").css("padding-top", $(".navbar").height());
|
||||
};
|
||||
function updateCompleted() {
|
||||
counter.show();
|
||||
filters.updateDone();
|
||||
progress.complete();
|
||||
$(selectors.refreshButton).prop("disabled", false);
|
||||
// hack for fixing padding since input can grow and change height
|
||||
$("body").css("padding-top", $(".navbar").height());
|
||||
}
|
||||
|
||||
var renderError = function(template, context) {
|
||||
Counter.Error();
|
||||
Grid.Clear();
|
||||
Grid.Hide();
|
||||
$(selectors.errors).html(Templates.Render(template, context));
|
||||
$(selectors.errors).show();
|
||||
Counter.Unknown();
|
||||
Summary.Update({});
|
||||
document.title = "(◕ O ◕)";
|
||||
updateCompleted();
|
||||
};
|
||||
function renderError(template, context) {
|
||||
counter.markError();
|
||||
grid.clear();
|
||||
grid.hide();
|
||||
$(selectors.errors).html(templates.renderTemplate(template, context));
|
||||
$(selectors.errors).show();
|
||||
counter.markUnknown();
|
||||
summary.update({});
|
||||
document.title = "(◕ O ◕)";
|
||||
updateCompleted();
|
||||
}
|
||||
|
||||
var handleError = function(err) {
|
||||
Raven.captureException(err);
|
||||
if (window.console) {
|
||||
console.error(err.stack);
|
||||
}
|
||||
renderError("internalError", {
|
||||
name: err.name,
|
||||
message: err.message,
|
||||
raw: err
|
||||
});
|
||||
setTimeout(function() {
|
||||
Unsee.WaitForNextReload();
|
||||
}, 500);
|
||||
};
|
||||
function resume() {
|
||||
if (config.getOption("autorefresh").Get()) {
|
||||
filters.updateDone();
|
||||
} else {
|
||||
filters.setPause();
|
||||
return false;
|
||||
}
|
||||
progress.resetTimer();
|
||||
if (timer !== false) {
|
||||
clearInterval(timer);
|
||||
}
|
||||
timer = setTimeout(triggerReload, getRefreshRate() * 1000);
|
||||
}
|
||||
|
||||
var upgrade = function() {
|
||||
renderError("reloadNeeded", {});
|
||||
setTimeout(function() {
|
||||
location.reload();
|
||||
}, 3000);
|
||||
};
|
||||
function handleError(err) {
|
||||
Raven.captureException(err);
|
||||
if (window.console) {
|
||||
console.error(err.stack);
|
||||
}
|
||||
renderError("internalError", {
|
||||
name: err.name,
|
||||
message: err.message,
|
||||
raw: err
|
||||
});
|
||||
setTimeout(function() {
|
||||
resume();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
var triggerReload = function() {
|
||||
updateIsReady();
|
||||
$.ajax({
|
||||
url: "alerts.json?q=" + Filters.GetFilters().join(","),
|
||||
success: function(resp) {
|
||||
Counter.Success();
|
||||
if (needsUpgrade(resp.version)) {
|
||||
upgrade();
|
||||
} else {
|
||||
if (resp.upstreams.counters.total === 0) {
|
||||
// no upstream to use fail hard
|
||||
Counter.Unknown();
|
||||
$(selectors.instanceErrors).html("");
|
||||
renderError("updateError", {
|
||||
error: "Fatal error",
|
||||
messages: [ "No working Alertmanager server found" ],
|
||||
lastTs: Watchdog.GetLastUpdate()
|
||||
function upgrade() {
|
||||
renderError("reloadNeeded", {});
|
||||
setTimeout(function() {
|
||||
location.reload();
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
function triggerReload() {
|
||||
updateIsReady();
|
||||
$.ajax({
|
||||
url: "alerts.json?q=" + filters.getFilters().join(","),
|
||||
success: function(resp) {
|
||||
counter.markSuccess();
|
||||
if (needsUpgrade(resp.version)) {
|
||||
upgrade();
|
||||
} else {
|
||||
if (resp.upstreams.counters.total === 0) {
|
||||
// no upstream to use fail hard
|
||||
counter.markUnknown();
|
||||
$(selectors.instanceErrors).html("");
|
||||
renderError("updateError", {
|
||||
error: "Fatal error",
|
||||
messages: [ "No working Alertmanager server found" ],
|
||||
lastTs: watchdog.getLastUpdate()
|
||||
});
|
||||
resume();
|
||||
} else if (resp.upstreams.counters.healthy > 0 ) {
|
||||
// we have some healthy upstreams, check for failed ones
|
||||
if (resp.upstreams.counters.failed > 0) {
|
||||
var instances = [];
|
||||
resp.upstreams.instances.sort(function(a, b){
|
||||
if(a.name < b.name) return -1;
|
||||
if(a.name > b.name) return 1;
|
||||
return 0;
|
||||
});
|
||||
Unsee.WaitForNextReload();
|
||||
} else if (resp.upstreams.counters.healthy > 0 ) {
|
||||
// we have some healthy upstreams, check for failed ones
|
||||
if (resp.upstreams.counters.failed > 0) {
|
||||
var instances = [];
|
||||
resp.upstreams.instances.sort(function(a, b){
|
||||
if(a.name < b.name) return -1;
|
||||
if(a.name > b.name) return 1;
|
||||
return 0;
|
||||
});
|
||||
$.each(resp.upstreams.instances, function(i, instance){
|
||||
if (instance.error !== "") {
|
||||
instances.push(instance);
|
||||
}
|
||||
});
|
||||
$(selectors.instanceErrors).html(
|
||||
Templates.Render("instanceError", {
|
||||
instances: instances
|
||||
})
|
||||
);
|
||||
} else {
|
||||
$(selectors.instanceErrors).html("");
|
||||
}
|
||||
// update_alerts() is cpu heavy so it will block browser from applying css changes
|
||||
// inject tiny delay between addClass() above and update_alerts() so that the browser
|
||||
// have a chance to reflect those updates
|
||||
setTimeout(function() {
|
||||
try {
|
||||
Summary.Update({});
|
||||
Filters.ReloadBadges(resp.filters);
|
||||
Colors.Update(resp.colors);
|
||||
Alerts.Update(resp);
|
||||
updateCompleted();
|
||||
Watchdog.Pong(moment(resp.timestamp));
|
||||
Unsee.WaitForNextReload();
|
||||
if (!Watchdog.IsFatal()) {
|
||||
$(selectors.errors).html("");
|
||||
$(selectors.errors).hide("");
|
||||
}
|
||||
} catch (err) {
|
||||
Counter.Unknown();
|
||||
handleError(err);
|
||||
Unsee.WaitForNextReload();
|
||||
}
|
||||
}, 50);
|
||||
} else {
|
||||
// we have upstreams but none is working, fail hard
|
||||
Counter.Unknown();
|
||||
$(selectors.instanceErrors).html("");
|
||||
var failedInstances = [];
|
||||
$.each(resp.upstreams.instances, function(i, instance) {
|
||||
$.each(resp.upstreams.instances, function(i, instance){
|
||||
if (instance.error !== "") {
|
||||
failedInstances.push(instance);
|
||||
instances.push(instance);
|
||||
}
|
||||
});
|
||||
renderError("configError", {
|
||||
instances: failedInstances
|
||||
});
|
||||
Unsee.WaitForNextReload();
|
||||
$(selectors.instanceErrors).html(
|
||||
templates.renderTemplate("instanceError", {
|
||||
instances: instances
|
||||
})
|
||||
);
|
||||
} else {
|
||||
$(selectors.instanceErrors).html("");
|
||||
}
|
||||
// update_alerts() is cpu heavy so it will block browser from applying css changes
|
||||
// inject tiny delay between addClass() above and update_alerts() so that the browser
|
||||
// have a chance to reflect those updates
|
||||
setTimeout(function() {
|
||||
try {
|
||||
summary.update({});
|
||||
filters.reloadBadges(resp.filters);
|
||||
colors.update(resp.colors);
|
||||
alerts.update(resp);
|
||||
updateCompleted();
|
||||
watchdog.pong(moment(resp.timestamp));
|
||||
resume();
|
||||
if (!watchdog.isFatal()) {
|
||||
$(selectors.errors).html("");
|
||||
$(selectors.errors).hide("");
|
||||
}
|
||||
} catch (err) {
|
||||
counter.markUnknown();
|
||||
handleError(err);
|
||||
resume();
|
||||
}
|
||||
}, 50);
|
||||
} else {
|
||||
// we have upstreams but none is working, fail hard
|
||||
counter.markUnknown();
|
||||
$(selectors.instanceErrors).html("");
|
||||
var failedInstances = [];
|
||||
$.each(resp.upstreams.instances, function(i, instance) {
|
||||
if (instance.error !== "") {
|
||||
failedInstances.push(instance);
|
||||
}
|
||||
});
|
||||
renderError("configError", {
|
||||
instances: failedInstances
|
||||
});
|
||||
resume();
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function(xhr, textStatus) {
|
||||
counter.markUnknown();
|
||||
$(selectors.instanceErrors).html("");
|
||||
// if fatal error was already triggered we have error message
|
||||
// so don't add new one
|
||||
if (!watchdog.isFatal()) {
|
||||
var err = parseAJAXError(xhr, textStatus);
|
||||
renderError("updateError", {
|
||||
error: "Backend error",
|
||||
messages: [ err ],
|
||||
lastTs: watchdog.getLastUpdate()
|
||||
});
|
||||
}
|
||||
resume();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function pause() {
|
||||
progress.pause();
|
||||
filters.setPause();
|
||||
if (timer !== false) {
|
||||
clearInterval(timer);
|
||||
timer = false;
|
||||
}
|
||||
}
|
||||
|
||||
function flash() {
|
||||
var bg = $("#flash").css("background-color");
|
||||
$("#flash").css("display", "block").animate({
|
||||
backgroundColor: "#fff"
|
||||
}, 300, function() {
|
||||
$(this).animate({
|
||||
backgroundColor: bg
|
||||
}, 100).css("display", "none");
|
||||
});
|
||||
}
|
||||
|
||||
// when user switches to a different tab but keeps unsee tab open in the background
|
||||
// some browsers (like Chrome) will try to apply some forms of throttling for the JS
|
||||
// code, to ensure that there are no visual artifacts (like state alerts not removed from the page)
|
||||
// redraw all alerts if we detect that the user switches from a different tab to unsee
|
||||
function setupPageVisibilityHandler() {
|
||||
// based on https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
|
||||
if (typeof document.hidden !== "undefined" && typeof document.addEventListener !== "undefined") {
|
||||
document.addEventListener("visibilitychange", function() {
|
||||
if (document.hidden) {
|
||||
// when tab is hidden set a timestamp of that event
|
||||
hiddenAt = moment().utc().unix();
|
||||
} else {
|
||||
// when user switches back check if we have a timestamp
|
||||
// and if autorefresh is enable
|
||||
if (hiddenAt && config.getOption("autorefresh").Get()) {
|
||||
// get the diff to see how long tab was hidden
|
||||
var diff = moment().utc().unix() - hiddenAt;
|
||||
if (diff > refreshInterval) {
|
||||
// if it was hidden for more than one refresh cycle
|
||||
// then manually refresh alerts to ensure everything
|
||||
// is up to date
|
||||
triggerReload();
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function(xhr, textStatus) {
|
||||
Counter.Unknown();
|
||||
$(selectors.instanceErrors).html("");
|
||||
// if fatal error was already triggered we have error message
|
||||
// so don't add new one
|
||||
if (!Watchdog.IsFatal()) {
|
||||
var err = Unsee.ParseAJAXError(xhr, textStatus);
|
||||
renderError("updateError", {
|
||||
error: "Backend error",
|
||||
messages: [ err ],
|
||||
lastTs: Watchdog.GetLastUpdate()
|
||||
});
|
||||
}
|
||||
Unsee.WaitForNextReload();
|
||||
hiddenAt = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
|
||||
var pause = function() {
|
||||
Progress.Pause();
|
||||
Filters.Pause();
|
||||
if (timer !== false) {
|
||||
clearInterval(timer);
|
||||
timer = false;
|
||||
function init() {
|
||||
progress.init();
|
||||
|
||||
config.init({
|
||||
CopySelector: "#copy-settings-with-filter",
|
||||
SaveSelector: "#save-default-filter",
|
||||
ResetSelector: "#reset-settings"
|
||||
});
|
||||
config.loadFromCookies();
|
||||
|
||||
counter.init();
|
||||
summary.init();
|
||||
grid.init();
|
||||
autocomplete.init();
|
||||
filters.init();
|
||||
watchdog.init(30, 60*15); // set watchdog to 15 minutes
|
||||
|
||||
$(selectors.refreshButton).click(function() {
|
||||
if (!$(selectors.refreshButton).prop("disabled")) {
|
||||
triggerReload();
|
||||
}
|
||||
};
|
||||
return false;
|
||||
});
|
||||
|
||||
var resume = function() {
|
||||
if (Config.GetOption("autorefresh").Get()) {
|
||||
Filters.UpdateCompleted();
|
||||
} else {
|
||||
Filters.Pause();
|
||||
return false;
|
||||
}
|
||||
Progress.Reset();
|
||||
if (timer !== false) {
|
||||
clearInterval(timer);
|
||||
}
|
||||
timer = setTimeout(Unsee.Reload, Unsee.GetRefreshRate() * 1000);
|
||||
};
|
||||
setupPageVisibilityHandler();
|
||||
}
|
||||
|
||||
var flash = function() {
|
||||
var bg = $("#flash").css("background-color");
|
||||
$("#flash").css("display", "block").animate({
|
||||
backgroundColor: "#fff"
|
||||
}, 300, function() {
|
||||
$(this).animate({
|
||||
backgroundColor: bg
|
||||
}, 100).css("display", "none");
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
Init: init,
|
||||
Pause: pause,
|
||||
WaitForNextReload: resume,
|
||||
Reload: triggerReload,
|
||||
GetRefreshRate: getRefreshRate,
|
||||
SetRefreshRate: setRefreshRate,
|
||||
Flash: flash,
|
||||
ParseAJAXError: parseAJAXError
|
||||
};
|
||||
|
||||
})();
|
||||
exports.init = init;
|
||||
exports.pause = pause;
|
||||
exports.resume = resume;
|
||||
exports.triggerReload = triggerReload;
|
||||
exports.getRefreshRate = getRefreshRate;
|
||||
exports.setRefreshRate = setRefreshRate;
|
||||
exports.flash = flash;
|
||||
exports.parseAJAXError = parseAJAXError;
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
@@ -326,15 +333,16 @@ $(document).ready(function() {
|
||||
trigger: "hover"
|
||||
});
|
||||
|
||||
Templates.Init();
|
||||
UI.Init();
|
||||
Silence.Init();
|
||||
Unsilence.Init();
|
||||
Unsee.Init();
|
||||
colors.init($("#alerts").data("static-color-labels").split(" "));
|
||||
templates.init();
|
||||
ui.init();
|
||||
silence.init();
|
||||
unsilence.init();
|
||||
init();
|
||||
|
||||
// delay initial alert load to allow browser finish rendering
|
||||
setTimeout(function() {
|
||||
Filters.SetFilters();
|
||||
filters.setFilters();
|
||||
}, 100);
|
||||
} catch (error) {
|
||||
Raven.captureException(error);
|
||||
|
||||
5
assets/static/unsee.test.js
Normal file
5
assets/static/unsee.test.js
Normal file
@@ -0,0 +1,5 @@
|
||||
test("unsee getRefreshRate()", () => {
|
||||
window.jQuery = require("jquery");
|
||||
const unsee = require("./unsee");
|
||||
unsee.getRefreshRate();
|
||||
});
|
||||
Reference in New Issue
Block a user