From 80338927f73e4b1bf2a9e75363a1404c0b011195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Sat, 4 May 2019 21:58:34 +0100 Subject: [PATCH] feat(ui): use alerts timestamp as secondary sort key --- ui/src/Components/Grid/AlertGrid/index.js | 83 ++++++++++++------- .../Components/Grid/AlertGrid/index.test.js | 44 ++++++++++ 2 files changed, 97 insertions(+), 30 deletions(-) diff --git a/ui/src/Components/Grid/AlertGrid/index.js b/ui/src/Components/Grid/AlertGrid/index.js index 0c1a6ab3a..59d749bb6 100644 --- a/ui/src/Components/Grid/AlertGrid/index.js +++ b/ui/src/Components/Grid/AlertGrid/index.js @@ -25,6 +25,33 @@ import { GridSizesConfig, GetGridElementWidth } from "./GridSize"; import "./index.css"; +const getGroupStartsAt = g => moment.max(g.alerts.map(a => moment(a.startsAt))); + +const getLabelValue = (alertStore, settingsStore, sortOrder, sortLabel, g) => { + // if timestamp sort is enabled use latest alert for sorting + if (sortOrder === settingsStore.gridConfig.options.startsAt.value) { + return getGroupStartsAt(g); + } + + const labelValue = + g.labels[sortLabel] || + g.shared.labels[sortLabel] || + g.alerts[0].labels[sortLabel]; + let mappedValue; + + // check if we have a mapping for label value + if ( + labelValue !== undefined && + alertStore.settings.values.sorting.valueMapping[sortLabel] !== undefined + ) { + mappedValue = + alertStore.settings.values.sorting.valueMapping[sortLabel][labelValue]; + } + + // if we have a mapped value then return it, if not return original value + return mappedValue !== undefined ? mappedValue : labelValue; +}; + const AlertGrid = observer( class AlertGrid extends Component { static propTypes = { @@ -136,38 +163,22 @@ const AlertGrid = observer( ? alertStore.settings.values.sorting.grid.label : settingsStore.gridConfig.config.sortLabel; - const getLabelValue = g => { - // if timestamp sort is enabled use latest alert for sorting - if (sortOrder === settingsStore.gridConfig.options.startsAt.value) { - return moment.max(g.alerts.map(a => moment(a.startsAt))); - } - - const labelValue = - g.labels[sortLabel] || - g.shared.labels[sortLabel] || - g.alerts[0].labels[sortLabel]; - let mappedValue; - - // check if we have a mapping for label value - if ( - labelValue !== undefined && - alertStore.settings.values.sorting.valueMapping[sortLabel] !== - undefined - ) { - mappedValue = - alertStore.settings.values.sorting.valueMapping[sortLabel][ - labelValue - ]; - } - - // if we have a mapped value then return it, if not return original value - return mappedValue !== undefined ? mappedValue : labelValue; - }; - const val = sortReverse ? -1 : 1; - const av = getLabelValue(a); - const bv = getLabelValue(b); + const av = getLabelValue( + alertStore, + settingsStore, + sortOrder, + sortLabel, + a + ); + const bv = getLabelValue( + alertStore, + settingsStore, + sortOrder, + sortLabel, + b + ); if (av === undefined && av === undefined) { // if both alerts lack the label they are equal @@ -178,6 +189,18 @@ const AlertGrid = observer( } else if (bv === undefined || av < bv) { // if the first one has label but the second doesn't then the second should be rendered after the first return val * -1; + } else if ( + sortOrder !== settingsStore.gridConfig.options.startsAt.value + ) { + const ast = getGroupStartsAt(a); + const bst = getGroupStartsAt(b); + if (ast > bst) { + return 1; + } else if (ast < bst) { + return -1; + } else { + return 0; + } } else { return 0; } diff --git a/ui/src/Components/Grid/AlertGrid/index.test.js b/ui/src/Components/Grid/AlertGrid/index.test.js index 35e2b6497..e7cce65b5 100644 --- a/ui/src/Components/Grid/AlertGrid/index.test.js +++ b/ui/src/Components/Grid/AlertGrid/index.test.js @@ -326,6 +326,50 @@ describe("", () => { ]); }); + it("startsAt is used as secondary sort key if all labels have equal value", () => { + settingsStore.gridConfig.config.sortOrder = + settingsStore.gridConfig.options.label.value; + settingsStore.gridConfig.config.sortLabel = "cluster"; + + MockGroupList(3, 1); + alertStore.data.groups.id1.alerts[0].labels.cluster = "dev"; + alertStore.data.groups.id1.alerts[0].startsAt = "2000-01-01T00:00:02.000Z"; + alertStore.data.groups.id2.alerts[0].labels.cluster = "dev"; + alertStore.data.groups.id2.alerts[0].startsAt = "2000-01-01T00:00:03.000Z"; + alertStore.data.groups.id3.alerts[0].labels.cluster = "dev"; + alertStore.data.groups.id3.alerts[0].startsAt = "2000-01-01T00:00:01.000Z"; + + const tree = ShallowAlertGrid(); + const alertGroups = tree.find("AlertGroup"); + expect(alertGroups.map(g => g.props().group.id)).toEqual([ + "id3", + "id1", + "id2" + ]); + }); + + it("original order is preserved when startsAt is used as fallback and all alerts have the same timestamp", () => { + settingsStore.gridConfig.config.sortOrder = + settingsStore.gridConfig.options.label.value; + settingsStore.gridConfig.config.sortLabel = "cluster"; + + MockGroupList(3, 1); + alertStore.data.groups.id1.alerts[0].labels.cluster = "dev"; + alertStore.data.groups.id1.alerts[0].startsAt = "2000-01-01T00:00:01.000Z"; + alertStore.data.groups.id2.alerts[0].labels.cluster = "dev"; + alertStore.data.groups.id2.alerts[0].startsAt = "2000-01-01T00:00:01.000Z"; + alertStore.data.groups.id3.alerts[0].labels.cluster = "dev"; + alertStore.data.groups.id3.alerts[0].startsAt = "2000-01-01T00:00:01.000Z"; + + const tree = ShallowAlertGrid(); + const alertGroups = tree.find("AlertGroup"); + expect(alertGroups.map(g => g.props().group.id)).toEqual([ + "id1", + "id2", + "id3" + ]); + }); + it("doesn't throw errors after FontFaceObserver timeout", () => { MockGroupList(60, 5); ShallowAlertGrid();