diff --git a/alerts.go b/alerts.go
index 0b50f20be..a1020f05e 100644
--- a/alerts.go
+++ b/alerts.go
@@ -1,6 +1,7 @@
package main
import (
+ "fmt"
"math"
"sort"
@@ -44,18 +45,30 @@ func countersToLabelStats(counters map[string]map[string]int) models.LabelNameSt
Name: name,
Values: models.LabelValueStatsList{},
}
+
for value, hits := range valueMap {
nameStats.Hits += hits
valueStats := models.LabelValueStats{
Value: value,
+ Raw: fmt.Sprintf("%s=%s", name, value),
Hits: hits,
}
nameStats.Values = append(nameStats.Values, valueStats)
}
+
+ // now that we have total hits we can calculate %
for i, value := range nameStats.Values {
nameStats.Values[i].Percent = int(math.Round((float64(value.Hits) / float64(nameStats.Hits)) * 100.0))
}
+
sort.Sort(nameStats.Values)
+
+ // now that we have all % and values are sorted we can calculate offsets
+ offset := 0
+ for i, value := range nameStats.Values {
+ nameStats.Values[i].Offset = offset
+ offset += value.Percent
+ }
data = append(data, nameStats)
}
diff --git a/internal/models/api.go b/internal/models/api.go
index 57a835fab..eebaffd48 100644
--- a/internal/models/api.go
+++ b/internal/models/api.go
@@ -40,8 +40,10 @@ type LabelsCountMap map[string]map[string]int
type LabelValueStats struct {
Value string `json:"value"`
+ Raw string `json:"raw"`
Hits int `json:"hits"`
Percent int `json:"percent"`
+ Offset int `json:"offset"`
}
type LabelValueStatsList []LabelValueStats
diff --git a/internal/models/api_test.go b/internal/models/api_test.go
index 557ab9c9a..0474a4700 100644
--- a/internal/models/api_test.go
+++ b/internal/models/api_test.go
@@ -265,13 +265,17 @@ func TestNameStatsSort(t *testing.T) {
Values: models.LabelValueStatsList{
models.LabelValueStats{
Value: "suppressed",
+ Raw: "@state=suppressed",
Hits: 8,
Percent: 33,
+ Offset: 67,
},
models.LabelValueStats{
Value: "active",
+ Raw: "@state=actuve",
Hits: 16,
Percent: 67,
+ Offset: 0,
},
},
},
@@ -281,18 +285,24 @@ func TestNameStatsSort(t *testing.T) {
Values: models.LabelValueStatsList{
models.LabelValueStats{
Value: "dev",
+ Raw: "cluster=dev",
Hits: 10,
Percent: 42,
+ Offset: 0,
},
models.LabelValueStats{
Value: "prod",
+ Raw: "cluster=prod",
Hits: 6,
Percent: 25,
+ Offset: 42,
},
models.LabelValueStats{
Value: "staging",
+ Raw: "cluster=staging",
Hits: 8,
Percent: 33,
+ Offset: 67,
},
},
},
@@ -302,23 +312,32 @@ func TestNameStatsSort(t *testing.T) {
Values: models.LabelValueStatsList{
models.LabelValueStats{
Value: "HTTP_Probe_Failed",
+ Raw: "alertname=HTTP_Probe_Failed",
Hits: 4,
Percent: 17,
+ Offset: 0,
},
models.LabelValueStats{
Value: "Host_Down",
+ Raw: "alertname=Host_Down",
Hits: 16,
Percent: 67,
+ Offset: 17,
},
models.LabelValueStats{
- Value: "Free_Disk_Space_Too_Low",
+ Value: "Free_Disk_Space_Too_Low",
+ Raw: "alertname=Free_Disk_Space_Too_Low",
+
Hits: 2,
Percent: 8,
+ Offset: 84,
},
models.LabelValueStats{
Value: "Memory_Usage_Too_High",
+ Raw: "alertname=Memory_Usage_Too_High",
Hits: 2,
Percent: 8,
+ Offset: 92,
},
},
},
@@ -328,53 +347,72 @@ func TestNameStatsSort(t *testing.T) {
Values: models.LabelValueStatsList{
models.LabelValueStats{
Value: "server4",
+ Raw: "instance=server4",
Hits: 2,
Percent: 8,
},
models.LabelValueStats{
Value: "server5",
+ Raw: "instance=server5",
Hits: 4,
Percent: 17,
+ Offset: 17,
},
models.LabelValueStats{
Value: "server6",
+ Raw: "instance=server6",
Hits: 2,
Percent: 8,
+ Offset: 17,
},
models.LabelValueStats{
Value: "server1",
+ Raw: "instance=server1",
Hits: 2,
Percent: 8,
+ Offset: 17,
},
models.LabelValueStats{
Value: "server2",
+ Raw: "instance=server2",
Hits: 4,
Percent: 17,
+ Offset: 17,
},
models.LabelValueStats{
Value: "server3",
+ Raw: "instance=server3",
Hits: 2,
Percent: 8,
+ Offset: 17,
},
models.LabelValueStats{
Value: "server7",
+ Raw: "instance=server7",
Hits: 2,
Percent: 8,
+ Offset: 17,
},
models.LabelValueStats{
Value: "server8",
+ Raw: "instance=server8",
Hits: 2,
Percent: 8,
+ Offset: 17,
},
models.LabelValueStats{
Value: "web1",
+ Raw: "instance=web1",
Hits: 2,
Percent: 8,
+ Offset: 17,
},
models.LabelValueStats{
Value: "web2",
+ Raw: "instance=web2",
Hits: 2,
Percent: 8,
+ Offset: 17,
},
},
},
@@ -384,13 +422,17 @@ func TestNameStatsSort(t *testing.T) {
Values: models.LabelValueStatsList{
models.LabelValueStats{
Value: "by-name",
+ Raw: "@receiver=by-name",
Hits: 12,
Percent: 50,
+ Offset: 0,
},
models.LabelValueStats{
Value: "by-cluster-service",
+ Raw: "@receiver=by-cluster-service",
Hits: 12,
Percent: 50,
+ Offset: 50,
},
},
},
@@ -400,13 +442,17 @@ func TestNameStatsSort(t *testing.T) {
Values: models.LabelValueStatsList{
models.LabelValueStats{
Value: "node_exporter",
+ Raw: "job=node_exporter",
Hits: 8,
Percent: 50,
+ Offset: 0,
},
models.LabelValueStats{
Value: "node_ping",
+ Raw: "job=node_ping",
Hits: 8,
Percent: 50,
+ Offset: 50,
},
},
},
diff --git a/ui/src/Components/Labels/LabelWithPercent/__snapshots__/index.test.js.snap b/ui/src/Components/Labels/LabelWithPercent/__snapshots__/index.test.js.snap
index 68faeead3..a6b248c68 100644
--- a/ui/src/Components/Labels/LabelWithPercent/__snapshots__/index.test.js.snap
+++ b/ui/src/Components/Labels/LabelWithPercent/__snapshots__/index.test.js.snap
@@ -1,5 +1,55 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[` matches snapshot with isActive=true 1`] = `
+"
+
+
+
+ 25
+
+
+
+ foo:
+
+
+ bar
+
+
+
+
+
+
+"
+`;
+
exports[` matches snapshot with offset=0 1`] = `
"
matches snapshot with offset=0 1`] = `
25
-
- foo:
-
-
- bar
+
+
+ foo:
+
+
+ bar
+
@@ -45,11 +97,13 @@ exports[`
matches snapshot with offset=25 1`] = `
25
-
- foo:
-
-
- bar
+
+
+ foo:
+
+
+ bar
+
diff --git a/ui/src/Components/Labels/LabelWithPercent/index.js b/ui/src/Components/Labels/LabelWithPercent/index.js
index 4965e77ca..896f2b248 100644
--- a/ui/src/Components/Labels/LabelWithPercent/index.js
+++ b/ui/src/Components/Labels/LabelWithPercent/index.js
@@ -3,7 +3,11 @@ import PropTypes from "prop-types";
import { inject, observer } from "mobx-react";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faTimes } from "@fortawesome/free-solid-svg-icons/faTimes";
+
import { AlertStore } from "Stores/AlertStore";
+import { QueryOperators, FormatQuery } from "Common/Query";
import { TooltipWrapper } from "Components/TooltipWrapper";
import { BaseLabel } from "Components/Labels/BaseLabel";
@@ -11,18 +15,26 @@ import "./index.scss";
const LabelWithPercent = inject("alertStore")(
observer(
- class FilteringLabel extends BaseLabel {
+ class LabelWithPercent extends BaseLabel {
static propTypes = {
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
name: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
hits: PropTypes.number.isRequired,
percent: PropTypes.number.isRequired,
- offset: PropTypes.number.isRequired
+ offset: PropTypes.number.isRequired,
+ isActive: PropTypes.bool.isRequired
+ };
+
+ removeFromFilters = () => {
+ const { alertStore, name, value } = this.props;
+ alertStore.filters.removeFilter(
+ FormatQuery(name, QueryOperators.Equal, value)
+ );
};
render() {
- const { name, value, hits, percent, offset } = this.props;
+ const { name, value, hits, percent, offset, isActive } = this.props;
let cs = this.getClassAndStyle(
name,
@@ -39,16 +51,22 @@ const LabelWithPercent = inject("alertStore")(
return (
- this.handleClick(e)}
- >
+
{hits}
- {name}:{" "}
- {value}
+ this.handleClick(e)}>
+ {name}:{" "}
+ {value}
+
+ {isActive ? (
+
+ ) : null}
{offset === 0 ? null : (
diff --git a/ui/src/Components/Labels/LabelWithPercent/index.test.js b/ui/src/Components/Labels/LabelWithPercent/index.test.js
index e5e871c3d..6f2390fc6 100644
--- a/ui/src/Components/Labels/LabelWithPercent/index.test.js
+++ b/ui/src/Components/Labels/LabelWithPercent/index.test.js
@@ -14,7 +14,14 @@ beforeEach(() => {
alertStore = new AlertStore([]);
});
-const MountedLabelWithPercent = (name, value, hits, percent, offset) => {
+const MountedLabelWithPercent = (
+ name,
+ value,
+ hits,
+ percent,
+ offset,
+ isActive
+) => {
return mount(
{
hits={hits}
percent={percent}
offset={offset}
+ isActive={isActive}
/>
);
};
const RenderAndClick = (name, value, clickOptions) => {
- const tree = MountedLabelWithPercent(name, value, 25, 50, 0);
- tree.find(".components-label").simulate("click", clickOptions || {});
+ const tree = MountedLabelWithPercent(name, value, 25, 50, 0, false);
+ tree
+ .find(".components-label")
+ .find("span")
+ .at(2)
+ .simulate("click", clickOptions || {});
};
describe("", () => {
it("matches snapshot with offset=0", () => {
- const tree = MountedLabelWithPercent("foo", "bar", 25, 50, 0);
+ const tree = MountedLabelWithPercent("foo", "bar", 25, 50, 0, false);
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
});
it("matches snapshot with offset=25", () => {
- const tree = MountedLabelWithPercent("foo", "bar", 25, 50, 25);
+ const tree = MountedLabelWithPercent("foo", "bar", 25, 50, 25, false);
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
});
- it("calling onClick() adds a new filter 'foo=bar'", () => {
+ it("matches snapshot with isActive=true", () => {
+ const tree = MountedLabelWithPercent("foo", "bar", 25, 50, 0, true);
+ expect(toDiffableHtml(tree.html())).toMatchSnapshot();
+ });
+
+ it("calling adds a new filter 'foo=bar'", () => {
RenderAndClick("foo", "bar");
expect(alertStore.filters.values).toHaveLength(1);
expect(alertStore.filters.values).toContainEqual(
@@ -51,6 +68,18 @@ describe("", () => {
);
});
+ it("clicking the X buttom removes label from filters", () => {
+ const tree = MountedLabelWithPercent("foo", "bar", 25, 50, 0, true);
+ tree
+ .find(".components-label")
+ .find("svg")
+ .simulate("click");
+ expect(alertStore.filters.values).toHaveLength(0);
+ expect(alertStore.filters.values).not.toContainEqual(
+ NewUnappliedFilter("foo=bar")
+ );
+ });
+
it("calling onClick() while holding Alt key adds a new filter 'foo!=bar'", () => {
RenderAndClick("foo", "bar", { altKey: true });
expect(alertStore.filters.values).toHaveLength(1);
@@ -60,17 +89,17 @@ describe("", () => {
});
it("uses bg-danger when percent is >66", () => {
- const tree = MountedLabelWithPercent("foo", "bar", 25, 67, 0);
+ const tree = MountedLabelWithPercent("foo", "bar", 25, 67, 0, false);
expect(tree.html()).toMatch(/progress-bar bg-danger/);
});
it("uses bg-warning when percent is >33", () => {
- const tree = MountedLabelWithPercent("foo", "bar", 25, 66, 0);
+ const tree = MountedLabelWithPercent("foo", "bar", 25, 66, 0, false);
expect(tree.html()).toMatch(/progress-bar bg-warning/);
});
it("uses bg-success when percent is <=33", () => {
- const tree = MountedLabelWithPercent("foo", "bar", 25, 33, 0);
+ const tree = MountedLabelWithPercent("foo", "bar", 25, 33, 0, false);
expect(tree.html()).toMatch(/progress-bar bg-success/);
});
});
diff --git a/ui/src/Components/OverviewModal/OverviewModalContent.js b/ui/src/Components/OverviewModal/OverviewModalContent.js
index cc18854db..dd6b2bc33 100644
--- a/ui/src/Components/OverviewModal/OverviewModalContent.js
+++ b/ui/src/Components/OverviewModal/OverviewModalContent.js
@@ -23,17 +23,19 @@ const LabelsTable = observer(({ alertStore }) => (
- {nameStats.values.slice(0, 9).map((valueStats, i, array) => (
+ {nameStats.values.slice(0, 9).map((valueStats, i) => (
ns.percent)
- .reduce((a, b) => a + b, 0)}
+ offset={valueStats.offset}
+ isActive={
+ alertStore.filters.values.filter(
+ f => f.raw === valueStats.raw
+ ).length > 0
+ }
/>
))}
|
diff --git a/ui/src/Components/OverviewModal/OverviewModalContent.test.js b/ui/src/Components/OverviewModal/OverviewModalContent.test.js
index 3efa460b0..c337a6119 100644
--- a/ui/src/Components/OverviewModal/OverviewModalContent.test.js
+++ b/ui/src/Components/OverviewModal/OverviewModalContent.test.js
@@ -6,7 +6,7 @@ import { mount } from "enzyme";
import toDiffableHtml from "diffable-html";
-import { AlertStore } from "Stores/AlertStore";
+import { AlertStore, NewUnappliedFilter } from "Stores/AlertStore";
import { OverviewModalContent } from "./OverviewModalContent";
let alertStore;
@@ -23,14 +23,31 @@ afterEach(() => {
describe("", () => {
it("matches snapshot with labels to show", () => {
+ alertStore.filters.values = [
+ NewUnappliedFilter("abc=xyz"),
+ NewUnappliedFilter("foo=bar")
+ ];
alertStore.data.counters = [
{
name: "foo",
hits: 16,
values: [
- { value: "bar1", hits: 8, percent: 50 },
- { value: "bar2", hits: 4, percent: 25 },
- { value: "bar3", hits: 4, percent: 25 }
+ { value: "bar1", raw: "foo=bar1", hits: 8, percent: 50, offset: 0 },
+ { value: "bar2", raw: "foo=bar2", hits: 4, percent: 25, offset: 50 },
+ { value: "bar3", raw: "foo=bar3", hits: 4, percent: 25, offset: 75 }
+ ]
+ },
+ {
+ name: "alertname",
+ hits: 5,
+ values: [
+ {
+ value: "Host_Down",
+ raw: "alertname=Host_Down",
+ hits: 5,
+ percent: 100,
+ offset: 0
+ }
]
}
];
diff --git a/ui/src/Components/OverviewModal/__snapshots__/OverviewModalContent.test.js.snap b/ui/src/Components/OverviewModal/__snapshots__/OverviewModalContent.test.js.snap
index 53950a672..9946996cb 100644
--- a/ui/src/Components/OverviewModal/__snapshots__/OverviewModalContent.test.js.snap
+++ b/ui/src/Components/OverviewModal/__snapshots__/OverviewModalContent.test.js.snap
@@ -44,11 +44,13 @@ exports[` matches snapshot with labels to show 1`] = `
8
-
- foo:
-
-
- bar1
+
+
+ foo:
+
+
+ bar1
+
@@ -72,11 +74,13 @@ exports[`
matches snapshot with labels to show 1`] = `
4
-
- foo:
-
-
- bar2
+
+
+ foo:
+
+
+ bar2
+
@@ -108,11 +112,13 @@ exports[`
matches snapshot with labels to show 1`] = `
4
-
- foo:
-
-
- bar3
+
+
+ foo:
+
+
+ bar3
+
@@ -136,6 +142,52 @@ exports[` matches snapshot with labels to show 1`] = `
+
+ |
+
+
+ 5
+
+ alertname
+
+ |
+
+
+
+
+ 5
+
+
+
+ alertname:
+
+
+ Host_Down
+
+
+
+
+
+ |
+