diff --git a/ui/src/Components/AlertAck/index.test.tsx b/ui/src/Components/AlertAck/index.test.tsx
index 65bb02455..e49ab36cc 100644
--- a/ui/src/Components/AlertAck/index.test.tsx
+++ b/ui/src/Components/AlertAck/index.test.tsx
@@ -58,11 +58,17 @@ beforeEach(() => {
});
alerts = [
- MockAlert([], { foo: "bar" }, "active"),
- MockAlert([], { foo: "baz" }, "active"),
- MockAlert([], { foo: "ignore" }, "suppressed"),
+ MockAlert([], [{ name: "foo", value: "bar" }], "active"),
+ MockAlert([], [{ name: "foo", value: "baz" }], "active"),
+ MockAlert([], [{ name: "foo", value: "ignore" }], "suppressed"),
];
- group = MockAlertGroup({ alertname: "Fake Alert" }, alerts, [], {}, {});
+ group = MockAlertGroup(
+ [{ name: "alertname", value: "Fake Alert" }],
+ alerts,
+ [],
+ [],
+ {}
+ );
group.allLabels.active = {
alertname: ["Fake Alert"],
foo: ["bar", "baz"],
diff --git a/ui/src/Components/AlertHistory/index.test.tsx b/ui/src/Components/AlertHistory/index.test.tsx
index 4cb4c5ad8..f71c7046b 100644
--- a/ui/src/Components/AlertHistory/index.test.tsx
+++ b/ui/src/Components/AlertHistory/index.test.tsx
@@ -20,18 +20,22 @@ import type {
APIAlertGroupT,
APIGridT,
HistoryResponseT,
+ LabelsT,
} from "Models/APITypes";
import { AlertHistory } from ".";
let group: APIAlertGroupT;
let grid: APIGridT;
-const MockGroup = (groupName: string) => {
+const MockGroup = (groupName: string, sharedLabels: LabelsT = []) => {
const group = MockAlertGroup(
- { alertname: "Fake Alert", groupName: groupName },
+ [
+ { name: "alertname", value: "Fake Alert" },
+ { name: "groupName", value: groupName },
+ ],
[],
[],
- {},
+ sharedLabels,
{}
);
return group;
@@ -39,7 +43,11 @@ const MockGroup = (groupName: string) => {
const MockAlerts = (alertCount: number) => {
for (let i = 1; i <= alertCount; i++) {
- const alert = MockAlert([], { instance: `instance${i}` }, "active");
+ const alert = MockAlert(
+ [],
+ [{ name: "instance", value: `instance${i}` }],
+ "active"
+ );
const startsAt = new Date();
alert.startsAt = startsAt.toISOString();
for (let j = 0; j < alert.alertmanager.length; j++) {
@@ -171,6 +179,47 @@ describe("", () => {
tree.unmount();
});
+ it("send a correct payload with shared labels", async () => {
+ fetchMock.resetHistory();
+ fetchMock.mock(
+ "*",
+ {
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(EmptyHistoryResponse),
+ },
+ {
+ overwriteRoutes: true,
+ }
+ );
+
+ MockAlerts(3);
+ group = MockGroup("fakeGroup", [
+ { name: "shared1", value: "value1" },
+ { name: "shared2", value: "value2" },
+ ]);
+ const tree = mount();
+ await act(async () => {
+ await fetchMock.flush(true);
+ });
+ expect(fetchMock.calls()).toHaveLength(1);
+ expect(fetchMock.calls()[0][1]?.body).toStrictEqual(
+ JSON.stringify({
+ sources: [
+ "https://secure.example.com/graph",
+ "http://plain.example.com/",
+ ],
+ labels: {
+ alertname: "Fake Alert",
+ groupName: "fakeGroup",
+ shared1: "value1",
+ shared2: "value2",
+ foo: "bar",
+ },
+ })
+ );
+ tree.unmount();
+ });
+
it("matches snapshot with empty response", async () => {
fetchMock.resetHistory();
fetchMock.mock(
@@ -433,7 +482,11 @@ describe("", () => {
for (const testCase of testCases) {
const g = MockGroup("fakeGroup");
for (let i = 1; i <= 5; i++) {
- const alert = MockAlert([], { instance: `instance${i}` }, "active");
+ const alert = MockAlert(
+ [],
+ [{ name: "instance", value: `instance${i}` }],
+ "active"
+ );
const startsAt = new Date();
alert.startsAt = startsAt.toISOString();
alert.alertmanager.push(alert.alertmanager[0]);
diff --git a/ui/src/Components/AlertHistory/index.tsx b/ui/src/Components/AlertHistory/index.tsx
index 2d52b046c..fc72dbd01 100644
--- a/ui/src/Components/AlertHistory/index.tsx
+++ b/ui/src/Components/AlertHistory/index.tsx
@@ -34,9 +34,9 @@ export const AlertHistory: FC<{ group: APIAlertGroupT; grid: APIGridT }> = ({
const [lastUpdate, setLastUpdate] = useState(GetUTCSeconds());
const [upstreams, setUpstreams] = useState([]);
- const [labels] = useState({
- ...group.labels,
- ...group.shared.labels,
+ const [labels] = useState<{ [key: string]: string }>({
+ ...Object.fromEntries(group.labels.map((l) => [l.name, l.value])),
+ ...Object.fromEntries(group.shared.labels.map((l) => [l.name, l.value])),
...(grid.labelName !== "" && grid.labelName[0] !== "@"
? { [grid.labelName]: grid.labelValue }
: {}),
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/AlertMenu.test.tsx b/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/AlertMenu.test.tsx
index f37a3b6d5..001b6dd7b 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/AlertMenu.test.tsx
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/AlertMenu.test.tsx
@@ -74,8 +74,14 @@ beforeEach(() => {
MockAfterClick = jest.fn();
MockSetIsMenuOpen = jest.fn();
- alert = MockAlert([], { foo: "bar" }, "active");
- group = MockAlertGroup({ alertname: "Fake Alert" }, [alert], [], {}, {});
+ alert = MockAlert([], [{ name: "foo", value: "bar Alert" }], "active");
+ group = MockAlertGroup(
+ [{ name: "alertname", value: "Fake Alert" }],
+ [alert],
+ [],
+ [],
+ {}
+ );
grid = {
labelName: "foo",
labelValue: "bar",
@@ -233,11 +239,11 @@ describe("", () => {
isAction: false,
},
],
- { foo: "bar" },
+ [{ name: "foo", value: "bar" }],
"active"
);
group = MockAlertGroup(
- { alertname: "Fake Alert" },
+ [{ name: "alertname", value: "Fake Alert" }],
[alert],
[
{
@@ -262,7 +268,7 @@ describe("", () => {
isAction: false,
},
],
- {},
+ [],
{}
);
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/index.test.tsx b/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/index.test.tsx
index 7308abba7..4acb31f17 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/index.test.tsx
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/index.test.tsx
@@ -58,7 +58,10 @@ const MockedAlert = () => {
MockAnnotation("hidden", "some hidden text", false, false, false),
MockAnnotation("link", "http://localhost", true, true, false),
],
- { job: "node_exporter", cluster: "dev" },
+ [
+ { name: "job", value: "node_exporter" },
+ { name: "cluster", value: "dev" },
+ ],
"active"
);
};
@@ -91,7 +94,7 @@ const MountedAlert = (
describe("", () => {
it("matches snapshot with showAlertmanagers=false showReceiver=false", () => {
const alert = MockedAlert();
- const group = MockAlertGroup({}, [alert], [], {}, {});
+ const group = MockAlertGroup([], [alert], [], [], {});
group.shared.clusters = ["default"];
const tree = MountedAlert(alert, group, false, false);
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
@@ -100,7 +103,7 @@ describe("", () => {
it("matches snapshot when inhibited", () => {
const alert = MockedAlert();
alert.alertmanager[0].inhibitedBy = ["123456"];
- const group = MockAlertGroup({}, [alert], [], {}, {});
+ const group = MockAlertGroup([], [alert], [], [], {});
group.shared.clusters = ["default"];
const tree = MountedAlert(alert, group, false, false);
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
@@ -119,7 +122,7 @@ describe("", () => {
silencedBy: [],
inhibitedBy: ["123456"],
});
- const group = MockAlertGroup({}, [alert], [], {}, {});
+ const group = MockAlertGroup([], [alert], [], [], {});
const tree = MountedAlert(alert, group, false, false);
expect(tree.find(".fa-volume-mute")).toHaveLength(1);
});
@@ -127,14 +130,14 @@ describe("", () => {
it("inhibition icon passes only unique fingerprints", () => {
const alert = MockedAlert();
alert.alertmanager[0].inhibitedBy = ["123456"];
- const group = MockAlertGroup({}, [alert], [], {}, {});
+ const group = MockAlertGroup([], [alert], [], [], {});
const tree = MountedAlert(alert, group, false, false);
expect(tree.find(".fa-volume-mute")).toHaveLength(1);
});
it("renders @cluster label for non-shared clusters", () => {
const alert = MockedAlert();
- const group = MockAlertGroup({}, [alert], [], {}, {});
+ const group = MockAlertGroup([], [alert], [], [], {});
const tree = MountedAlert(alert, group, false, false);
const label = tree
.find("FilteringLabel")
@@ -164,7 +167,7 @@ describe("", () => {
silencedBy: [],
inhibitedBy: [],
});
- const group = MockAlertGroup({}, [alert], [], {}, {});
+ const group = MockAlertGroup([], [alert], [], [], {});
const tree = MountedAlert(alert, group, false, false);
const labels = tree
.find("FilteringLabel")
@@ -176,7 +179,7 @@ describe("", () => {
it("renders @receiver label with showReceiver=true", () => {
const alert = MockedAlert();
- const group = MockAlertGroup({}, [alert], [], {}, {});
+ const group = MockAlertGroup([], [alert], [], [], {});
const tree = MountedAlert(alert, group, true, false);
const label = tree
.find("FilteringLabel")
@@ -192,7 +195,7 @@ describe("", () => {
silence123456789: MockSilence(),
},
});
- const group = MockAlertGroup({}, [alert], [], {}, { default: [] });
+ const group = MockAlertGroup([], [alert], [], [], { default: [] });
const tree = MountedAlert(alert, group, false, false);
const silence = tree.find("ManagedSilence");
expect(silence).toHaveLength(1);
@@ -207,7 +210,7 @@ describe("", () => {
"123": MockSilence(),
},
});
- const group = MockAlertGroup({}, [alert], [], {}, { default: [] });
+ const group = MockAlertGroup([], [alert], [], [], { default: [] });
const tree = MountedAlert(alert, group, false, false);
const silence = tree.find("FallbackSilenceDesciption");
expect(silence).toHaveLength(1);
@@ -222,7 +225,7 @@ describe("", () => {
"123": MockSilence(),
},
});
- const group = MockAlertGroup({}, [alert], [], {}, { default: [] });
+ const group = MockAlertGroup([], [alert], [], [], { default: [] });
const tree = MountedAlert(alert, group, false, false);
const silence = tree.find("FallbackSilenceDesciption");
expect(silence).toHaveLength(1);
@@ -258,7 +261,7 @@ describe("", () => {
silence123456789: MockSilence(),
},
});
- const group = MockAlertGroup({}, [alert], [], {}, {});
+ const group = MockAlertGroup([], [alert], [], [], {});
const tree = MountedAlert(alert, group, false, false);
const silence = tree.find("ManagedSilence");
expect(silence).toHaveLength(1);
@@ -268,13 +271,9 @@ describe("", () => {
it("doesn't render shared silences", () => {
const alert = MockedAlert();
alert.alertmanager[0].silencedBy = ["silence123456789"];
- const group = MockAlertGroup(
- {},
- [alert],
- [],
- {},
- { default: ["silence123456789"] }
- );
+ const group = MockAlertGroup([], [alert], [], [], {
+ default: ["silence123456789"],
+ });
const tree = MountedAlert(alert, group, false, false);
const silence = tree.find("ManagedSilence");
expect(silence).toHaveLength(0);
@@ -298,7 +297,7 @@ describe("", () => {
isAction: false,
},
];
- const group = MockAlertGroup({}, [alert], [], {}, {});
+ const group = MockAlertGroup([], [alert], [], [], {});
const tree = MountedAlert(alert, group, false, false);
const annotations = tree.find("div.components-grid-annotation");
expect(annotations).toHaveLength(2);
@@ -322,7 +321,7 @@ describe("", () => {
isAction: false,
},
];
- const group = MockAlertGroup({}, [alert], [], {}, {});
+ const group = MockAlertGroup([], [alert], [], [], {});
const tree = MountedAlert(alert, group, false, true);
const annotations = tree.find("div.components-grid-annotation");
expect(annotations).toHaveLength(1);
@@ -331,7 +330,7 @@ describe("", () => {
it("uses BorderClassMap.active when @state=active", () => {
const alert = MockedAlert();
alert.state = "active";
- const group = MockAlertGroup({}, [alert], [], {}, {});
+ const group = MockAlertGroup([], [alert], [], [], {});
const tree = MountedAlert(alert, group, false, false);
expect(
tree
@@ -343,7 +342,7 @@ describe("", () => {
it("uses BorderClassMap.suppressed when @state=suppressed", () => {
const alert = MockedAlert();
alert.state = "suppressed";
- const group = MockAlertGroup({}, [alert], [], {}, {});
+ const group = MockAlertGroup([], [alert], [], [], {});
const tree = MountedAlert(alert, group, false, false);
expect(
tree
@@ -355,7 +354,7 @@ describe("", () => {
it("uses BorderClassMap.unprocessed when @state=unprocessed", () => {
const alert = MockedAlert();
alert.state = "unprocessed";
- const group = MockAlertGroup({}, [alert], [], {}, {});
+ const group = MockAlertGroup([], [alert], [], [], {});
const tree = MountedAlert(alert, group, false, false);
expect(
tree
@@ -369,7 +368,7 @@ describe("", () => {
const alert = MockedAlert();
(alert.state as string) = "foobar";
- const group = MockAlertGroup({}, [alert], [], {}, {});
+ const group = MockAlertGroup([], [alert], [], [], {});
const tree = MountedAlert(alert, group, false, false);
expect(
tree
@@ -384,7 +383,7 @@ describe("", () => {
advanceTo(new Date(Date.UTC(2018, 7, 14, 17, 36, 41)));
const alert = MockedAlert();
- const group = MockAlertGroup({}, [alert], [], {}, {});
+ const group = MockAlertGroup([], [alert], [], [], {});
const tree = MountedAlert(alert, group, false, false);
expect(
tree
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/index.tsx b/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/index.tsx
index 357b8c1ff..c8ab41154 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/index.tsx
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/index.tsx
@@ -112,11 +112,11 @@ const Alert: FC<{
{inhibitedBy.length > 0 ? (
) : null}
- {Object.entries(alert.labels).map(([name, value]) => (
+ {alert.labels.map((label) => (
))}
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupFooter/index.test.tsx b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupFooter/index.test.tsx
index 4568c79b7..3444c8995 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupFooter/index.test.tsx
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupFooter/index.test.tsx
@@ -23,11 +23,11 @@ let silenceFormStore: SilenceFormStore;
const MockGroup = () => {
const group = MockAlertGroup(
- { alertname: "Fake Alert" },
+ [{ name: "alertname", value: "Fake Alert" }],
[
- MockAlert([], {}, "suppressed"),
- MockAlert([], {}, "suppressed"),
- MockAlert([], {}, "suppressed"),
+ MockAlert([], [], "suppressed"),
+ MockAlert([], [], "suppressed"),
+ MockAlert([], [], "suppressed"),
],
[
MockAnnotation("summary", "This is summary", true, false, false),
@@ -40,7 +40,10 @@ const MockGroup = () => {
),
MockAnnotation("link", "http://link.example.com", true, true, false),
],
- { label1: "foo", label2: "bar" },
+ [
+ { name: "label1", value: "foo" },
+ { name: "label2", value: "bar" },
+ ],
{}
);
return group;
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupFooter/index.tsx b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupFooter/index.tsx
index dad8edc15..5a5a861c1 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupFooter/index.tsx
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupFooter/index.tsx
@@ -43,11 +43,11 @@ const GroupFooter: FC<{
))
: null}
- {Object.entries(group.shared.labels).map(([name, value]) => (
+ {group.shared.labels.map((label) => (
))}
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/GroupMenu.test.tsx b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/GroupMenu.test.tsx
index 8a35830b6..4630bebee 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/GroupMenu.test.tsx
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/GroupMenu.test.tsx
@@ -103,7 +103,13 @@ const MountedGroupMenu = (group: APIAlertGroupT, themed: boolean) => {
describe("", () => {
it("menu content is hidden by default", () => {
- const group = MockAlertGroup({ alertname: "Fake Alert" }, [], [], {}, {});
+ const group = MockAlertGroup(
+ [{ name: "alertname", value: "Fake Alert" }],
+ [],
+ [],
+ [],
+ {}
+ );
const tree = MountedGroupMenu(group, true);
expect(tree.find("div.dropdown-menu")).toHaveLength(0);
expect(MockSetIsMenuOpen).not.toHaveBeenCalled();
@@ -111,7 +117,13 @@ describe("", () => {
it("clicking toggle renders menu content", async () => {
const promise = Promise.resolve();
- const group = MockAlertGroup({ alertname: "Fake Alert" }, [], [], {}, {});
+ const group = MockAlertGroup(
+ [{ name: "alertname", value: "Fake Alert" }],
+ [],
+ [],
+ [],
+ {}
+ );
const tree = MountedGroupMenu(group, true);
const toggle = tree.find("span.cursor-pointer");
toggle.simulate("click");
@@ -122,7 +134,13 @@ describe("", () => {
it("clicking toggle twice hides menu content", async () => {
const promise = Promise.resolve();
- const group = MockAlertGroup({ alertname: "Fake Alert" }, [], [], {}, {});
+ const group = MockAlertGroup(
+ [{ name: "alertname", value: "Fake Alert" }],
+ [],
+ [],
+ [],
+ {}
+ );
const tree = MountedGroupMenu(group, true);
const toggle = tree.find("span.cursor-pointer");
@@ -145,7 +163,13 @@ describe("", () => {
it("clicking menu item hides menu content", async () => {
const promise = Promise.resolve();
- const group = MockAlertGroup({ alertname: "Fake Alert" }, [], [], {}, {});
+ const group = MockAlertGroup(
+ [{ name: "alertname", value: "Fake Alert" }],
+ [],
+ [],
+ [],
+ {}
+ );
const tree = MountedGroupMenu(group, true);
const toggle = tree.find("span.cursor-pointer");
@@ -180,7 +204,13 @@ const MountedMenuContent = (group: APIAlertGroupT) => {
describe("", () => {
it("clicking on 'Copy' icon copies the link to clickboard", () => {
- const group = MockAlertGroup({ alertname: "Fake Alert" }, [], [], {}, {});
+ const group = MockAlertGroup(
+ [{ name: "alertname", value: "Fake Alert" }],
+ [],
+ [],
+ [],
+ {}
+ );
const tree = MountedMenuContent(group);
const button = tree.find(".dropdown-item").at(0);
button.simulate("click");
@@ -188,7 +218,13 @@ describe("", () => {
});
it("clicking on 'Silence' icon opens the silence form modal", () => {
- const group = MockAlertGroup({ alertname: "Fake Alert" }, [], [], {}, {});
+ const group = MockAlertGroup(
+ [{ name: "alertname", value: "Fake Alert" }],
+ [],
+ [],
+ [],
+ {}
+ );
group.alertmanagerCount = { am1: 1, ro: 1 };
const tree = MountedMenuContent(group);
const button = tree.find(".dropdown-item").at(1);
@@ -205,7 +241,13 @@ describe("", () => {
upstreams.instances[2].readonly = true;
alertStore.data.setUpstreams(upstreams);
- const group = MockAlertGroup({ alertname: "Fake Alert" }, [], [], {}, {});
+ const group = MockAlertGroup(
+ [{ name: "alertname", value: "Fake Alert" }],
+ [],
+ [],
+ [],
+ {}
+ );
const tree = MountedMenuContent(group);
const button = tree.find(".dropdown-item").at(1);
expect(button.hasClass("disabled")).toBe(true);
@@ -215,7 +257,7 @@ describe("", () => {
it("renders action annotations when present", () => {
const group = MockAlertGroup(
- { alertname: "Fake Alert" },
+ [{ name: "alertname", value: "Fake Alert" }],
[],
[
{
@@ -240,7 +282,7 @@ describe("", () => {
isAction: false,
},
],
- {},
+ [],
{}
);
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/GroupMenu.tsx b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/GroupMenu.tsx
index 3e75b5e1e..5c1503e30 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/GroupMenu.tsx
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/GroupMenu.tsx
@@ -72,8 +72,8 @@ const MenuContent: FC<{
alertStore,
silenceFormStore,
}) => {
- const groupFilters = Object.keys(group.labels).map((name) =>
- FormatQuery(name, QueryOperators.Equal, group.labels[name])
+ const groupFilters = group.labels.map((label) =>
+ FormatQuery(label.name, QueryOperators.Equal, label.value)
);
groupFilters.push(
FormatQuery(StaticLabels.Receiver, QueryOperators.Equal, group.receiver)
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/index.tsx b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/index.tsx
index f11ff3a76..51894d3c3 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/index.tsx
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/index.tsx
@@ -66,11 +66,11 @@ const GroupHeader: FC<{
/>
- {Object.keys(group.labels).map((name) => (
+ {group.labels.map((label) => (
))}
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/index.test.tsx b/ui/src/Components/Grid/AlertGrid/AlertGroup/index.test.tsx
index a9d7d7a8e..d7f365119 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/index.test.tsx
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/index.test.tsx
@@ -26,10 +26,13 @@ let originalInnerWidth: number;
const MockGroup = (groupName: string) => {
const group = MockAlertGroup(
- { alertname: "Fake Alert", groupName: groupName },
+ [
+ { name: "alertname", value: "Fake Alert" },
+ { name: "groupName", value: "groupName" },
+ ],
+ [],
[],
[],
- {},
{}
);
return group;
@@ -71,7 +74,11 @@ afterEach(() => {
const MockAlerts = (alertCount: number, totalAlerts: number) => {
for (let i = 1; i <= alertCount; i++) {
- const alert = MockAlert([], { instance: `instance${i}` }, "active");
+ const alert = MockAlert(
+ [],
+ [{ name: "instance", value: `instance${i}` }],
+ "active"
+ );
const startsAt = new Date();
alert.startsAt = startsAt.toISOString();
alert.alertmanager[0].startsAt = startsAt.toISOString();
diff --git a/ui/src/Components/Grid/AlertGrid/index.test.tsx b/ui/src/Components/Grid/AlertGrid/index.test.tsx
index 44e065fce..13a5b7f51 100644
--- a/ui/src/Components/Grid/AlertGrid/index.test.tsx
+++ b/ui/src/Components/Grid/AlertGrid/index.test.tsx
@@ -143,13 +143,18 @@ const MountedGrid = (theme?: ThemeCtx) => {
const MockGroup = (groupName: string, alertCount: number) => {
const alerts = [];
for (let i = 1; i <= alertCount; i++) {
- alerts.push(MockAlert([], { instance: `instance${i}` }, "active"));
+ alerts.push(
+ MockAlert([], [{ name: "instance", value: `instance${i}` }], "active")
+ );
}
const group = MockAlertGroup(
- { alertname: "Fake Alert", group: groupName },
+ [
+ { name: "alertname", value: "Fake Alert" },
+ { name: "group", value: "groupName" },
+ ],
alerts,
[],
- {},
+ [],
{}
);
return group;
diff --git a/ui/src/Components/LabelSetList/index.test.tsx b/ui/src/Components/LabelSetList/index.test.tsx
index 2aae602f3..a15b143a7 100644
--- a/ui/src/Components/LabelSetList/index.test.tsx
+++ b/ui/src/Components/LabelSetList/index.test.tsx
@@ -35,17 +35,17 @@ describe("", () => {
});
it("renders labels on populated list", () => {
- const tree = MountedLabelSetList([{ foo: "bar" }]);
+ const tree = MountedLabelSetList([[{ name: "foo", value: "bar" }]]);
expect(tree.text()).not.toBe("No alerts matched");
expect(tree.find("ul.list-group").text()).toBe("foo: bar");
});
it("matches snapshot with populated list", () => {
const tree = MountedLabelSetList([
- { foo: "bar" },
- { job: "node_exporter" },
- { instance: "server1" },
- { cluster: "prod" },
+ [{ name: "foo", value: "bar" }],
+ [{ name: "job", value: "node_exporter" }],
+ [{ name: "instance", value: "server1" }],
+ [{ name: "cluster", value: "prod" }],
]);
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
});
@@ -53,7 +53,9 @@ describe("", () => {
it("doesn't render pagination when list has 10 elements on desktop", () => {
global.window.innerWidth = 1024;
const tree = MountedLabelSetList(
- Array.from(Array(10), (_, i) => ({ instance: `server${i}` }))
+ Array.from(Array(10), (_, i) => [
+ { name: "instance", value: `server${i}` },
+ ])
);
expect(tree.find(".pagination")).toHaveLength(0);
});
@@ -61,7 +63,9 @@ describe("", () => {
it("doesn't render pagination when list has 5 elements on desktop", () => {
global.window.innerWidth = 500;
const tree = MountedLabelSetList(
- Array.from(Array(5), (_, i) => ({ instance: `server${i}` }))
+ Array.from(Array(5), (_, i) => [
+ { name: "instance", value: `server${i}` },
+ ])
);
expect(tree.find(".pagination")).toHaveLength(0);
});
@@ -69,7 +73,9 @@ describe("", () => {
it("renders pagination when list has 11 elements on desktop", () => {
global.window.innerWidth = 1024;
const tree = MountedLabelSetList(
- Array.from(Array(11), (_, i) => ({ instance: `server${i}` }))
+ Array.from(Array(11), (_, i) => [
+ { name: "instance", value: `server${i}` },
+ ])
);
expect(tree.find(".pagination")).toHaveLength(1);
});
@@ -77,14 +83,18 @@ describe("", () => {
it("renders pagination when list has 6 elements on mobile", () => {
global.window.innerWidth = 500;
const tree = MountedLabelSetList(
- Array.from(Array(6), (_, i) => ({ instance: `server${i}` }))
+ Array.from(Array(6), (_, i) => [
+ { name: "instance", value: `server${i}` },
+ ])
);
expect(tree.find(".pagination")).toHaveLength(1);
});
it("clicking on pagination changes displayed elements", () => {
const tree = MountedLabelSetList(
- Array.from(Array(21), (_, i) => ({ instance: `server${i + 1}` }))
+ Array.from(Array(21), (_, i) => [
+ { name: "instance", value: `server${i + 1}` },
+ ])
);
const pageLink = tree.find(".page-link").at(3);
pageLink.simulate("click");
diff --git a/ui/src/Components/LabelSetList/index.tsx b/ui/src/Components/LabelSetList/index.tsx
index 7416b931a..7a4cffa73 100644
--- a/ui/src/Components/LabelSetList/index.tsx
+++ b/ui/src/Components/LabelSetList/index.tsx
@@ -4,10 +4,11 @@ import type { AlertStore } from "Stores/AlertStore";
import { IsMobile } from "Common/Device";
import StaticLabel from "Components/Labels/StaticLabel";
import { PageSelect } from "Components/Pagination";
+import type { LabelsT } from "Models/APITypes";
const LabelSetList: FC<{
alertStore: AlertStore;
- labelsList: { [labelName: string]: string }[];
+ labelsList: LabelsT[];
title?: string;
}> = ({ alertStore, labelsList, title }) => {
const [activePage, setActivePage] = useState(1);
@@ -26,12 +27,12 @@ const LabelSetList: FC<{
key={`${index}/${labels.length}`}
className="list-group-item px-0 pt-2 pb-1"
>
- {Object.entries(labels).map(([name, value]) => (
+ {labels.map((label) => (
))}
diff --git a/ui/src/Components/PaginatedAlertList/index.test.tsx b/ui/src/Components/PaginatedAlertList/index.test.tsx
index 1c77b44ac..8c4ddb64d 100644
--- a/ui/src/Components/PaginatedAlertList/index.test.tsx
+++ b/ui/src/Components/PaginatedAlertList/index.test.tsx
@@ -78,11 +78,11 @@ describe("", () => {
useFetchGetMock.fetch.setMockedData({
response: {
alerts: [
- {
- alertname: "Fake Alert",
- foo: "1",
- bar: "2",
- },
+ [
+ { name: "alertname", value: "Fake Alert" },
+ { name: "foo", value: "1" },
+ { name: "bar", value: "2" },
+ ],
],
},
error: undefined,
@@ -125,11 +125,11 @@ describe("", () => {
useFetchGetMock.fetch.setMockedData({
response: {
alerts: [
- {
- alertname: "Fake Alert",
- foo: "1",
- bar: "2",
- },
+ [
+ { name: "alertname", value: "Fake Alert" },
+ { name: "foo", value: "1" },
+ { name: "bar", value: "2" },
+ ],
],
},
error: undefined,
diff --git a/ui/src/Components/SilenceModal/SilenceMatch/MatchCounter.test.tsx b/ui/src/Components/SilenceModal/SilenceMatch/MatchCounter.test.tsx
index 6bfcc5c51..164c01863 100644
--- a/ui/src/Components/SilenceModal/SilenceMatch/MatchCounter.test.tsx
+++ b/ui/src/Components/SilenceModal/SilenceMatch/MatchCounter.test.tsx
@@ -35,7 +35,11 @@ const MountedMatchCounter = () => {
describe("", () => {
it("matches snapshot", () => {
useFetchGetMock.fetch.setMockedData({
- response: { alerts: Array(25).map((i) => ({ alertname: `alert${i}` })) },
+ response: {
+ alerts: Array(25).map((i) => [
+ { name: "alertname", value: `alert${i}` },
+ ]),
+ },
error: null,
isLoading: false,
isRetrying: false,
@@ -110,7 +114,11 @@ describe("", () => {
it("updates totalAlerts after successful fetch", () => {
useFetchGetMock.fetch.setMockedData({
- response: { alerts: Array(25).map((i) => ({ alertname: `alert${i}` })) },
+ response: {
+ alerts: Array(25).map((i) => [
+ { name: "alertname", value: `alert${i}` },
+ ]),
+ },
error: null,
isLoading: false,
isRetrying: false,
diff --git a/ui/src/Components/SilenceModal/SilencePreview/index.test.tsx b/ui/src/Components/SilenceModal/SilencePreview/index.test.tsx
index 536f17511..74814d7d6 100644
--- a/ui/src/Components/SilenceModal/SilencePreview/index.test.tsx
+++ b/ui/src/Components/SilenceModal/SilencePreview/index.test.tsx
@@ -65,7 +65,11 @@ describe("", () => {
it("matches snapshot", () => {
useFetchGetMock.fetch.setMockedData({
- response: { alerts: Array(25).map((i) => ({ alertname: `alert${i}` })) },
+ response: {
+ alerts: Array(25).map((i) => [
+ { name: "alertname", value: `alert${i}` },
+ ]),
+ },
error: undefined,
isLoading: false,
isRetrying: false,
@@ -94,7 +98,15 @@ describe("", () => {
it("renders StaticLabel after fetch", () => {
useFetchGetMock.fetch.setMockedData({
- response: { alerts: [{ alertname: "Fake Alert", foo: "1", bar: "1" }] },
+ response: {
+ alerts: [
+ [
+ { name: "alertname", value: "Fake Alert" },
+ { name: "foo", value: "1" },
+ { name: "bar", value: "1" },
+ ],
+ ],
+ },
error: undefined,
isLoading: false,
isRetrying: false,
diff --git a/ui/src/Components/SilenceModal/index.stories.tsx b/ui/src/Components/SilenceModal/index.stories.tsx
index ed7d39f68..03e56a925 100644
--- a/ui/src/Components/SilenceModal/index.stories.tsx
+++ b/ui/src/Components/SilenceModal/index.stories.tsx
@@ -128,7 +128,7 @@ storiesOf("SilenceModal", module)
);
fetchMock.mock(
"begin:/alertList.json?q=instance",
- { alerts: Array(23).fill(MockAlert([], {}, "active")) },
+ { alerts: Array(23).fill(MockAlert([], [], "active")) },
{
overwriteRoutes: true,
}
diff --git a/ui/src/Models/APITypes.ts b/ui/src/Models/APITypes.ts
index 32e10ff44..7ada29a7d 100644
--- a/ui/src/Models/APITypes.ts
+++ b/ui/src/Models/APITypes.ts
@@ -1,6 +1,11 @@
export type AlertStateT = "unprocessed" | "active" | "suppressed";
-export type LabelsT = { [key: string]: string };
+export interface LabelT {
+ name: string;
+ value: string;
+}
+
+export type LabelsT = LabelT[];
export interface AlertmanagerSilenceMatcherT {
name: string;
diff --git a/ui/src/Stores/SilenceFormStore.test.ts b/ui/src/Stores/SilenceFormStore.test.ts
index ce026e8c5..7a87e356e 100644
--- a/ui/src/Stores/SilenceFormStore.test.ts
+++ b/ui/src/Stores/SilenceFormStore.test.ts
@@ -25,17 +25,41 @@ beforeEach(() => {
const MockGroup = () => {
const alerts = [
- MockAlert([], { instance: "prod1", cluster: "prod" }, "active"),
- MockAlert([], { instance: "prod2", cluster: "prod" }, "active"),
- MockAlert([], { instance: "dev1", cluster: "dev" }, "active"),
+ MockAlert(
+ [],
+ [
+ { name: "instance", value: "prod1" },
+ { name: "cluster", value: "prod" },
+ ],
+ "active"
+ ),
+ MockAlert(
+ [],
+ [
+ { name: "instance", value: "prod2" },
+ { name: "cluster", value: "prod" },
+ ],
+ "active"
+ ),
+ MockAlert(
+ [],
+ [
+ { name: "instance", value: "dev1" },
+ { name: "cluster", value: "dev" },
+ ],
+ "active"
+ ),
];
const group = MockAlertGroup(
- { alertname: "FakeAlert" },
+ [{ name: "alertname", value: "FakeAlert" }],
alerts,
[],
- {
- job: "mock",
- },
+ [
+ {
+ name: "job",
+ value: "mock",
+ },
+ ],
{}
);
return group;
@@ -281,17 +305,27 @@ describe("SilenceFormStore.data", () => {
it("fillMatchersFromGroup() creates correct matcher object for a list of alerts with uncommon labels", () => {
const alerts = [
- MockAlert([], { instance: "1", banana: "ignore" }, "active"),
- MockAlert([], { instance: "2" }, "suppressed"),
- MockAlert([], { instance: "3" }, "active"),
+ MockAlert(
+ [],
+ [
+ { name: "instance", value: "1" },
+ { name: "banana", value: "ignore" },
+ ],
+ "active"
+ ),
+ MockAlert([], [{ name: "instance", value: "2" }], "suppressed"),
+ MockAlert([], [{ name: "instance", value: "3" }], "active"),
];
const group = MockAlertGroup(
- { alertname: "FakeAlert" },
+ [{ name: "alertname", value: "FakeAlert" }],
alerts,
[],
- {
- job: "mock",
- },
+ [
+ {
+ name: "job",
+ value: "mock",
+ },
+ ],
{}
);
group.allLabels.active = {
@@ -332,17 +366,15 @@ describe("SilenceFormStore.data", () => {
it("fillMatchersFromGroup() creates correct matcher object for a list of alerts with no labels", () => {
const alerts = [
- MockAlert([], {}, "active"),
- MockAlert([], {}, "suppressed"),
- MockAlert([], {}, "active"),
+ MockAlert([], [], "active"),
+ MockAlert([], [], "suppressed"),
+ MockAlert([], [], "active"),
];
const group = MockAlertGroup(
- { alertname: "FakeAlert" },
+ [{ name: "alertname", value: "FakeAlert" }],
alerts,
[],
- {
- job: "mock",
- },
+ [{ name: "job", value: "mock" }],
{}
);
group.allLabels.active = {
@@ -430,32 +462,37 @@ describe("SilenceFormStore.data", () => {
it("fillMatchersFromGroup() handles alerts with different label sets", () => {
const group = MockAlertGroup(
- { region: "AF" },
+ [{ name: "region", value: "AF" }],
[
MockAlert(
[],
- {
- alertname: "Alert1",
- cluster: "prod",
- foo: "bar",
- },
+ [
+ { name: "alertname", value: "Alert1" },
+ { name: "cluster", value: "prod" },
+ { name: "foo", value: "bar" },
+ ],
"active"
),
MockAlert(
[],
- {
- alertname: "Alert2",
- instance: "prod2",
- cluster: "prod",
- },
+ [
+ { name: "alertname", value: "Alert2" },
+ { name: "cluster", value: "prod2" },
+ { name: "foo", value: "prod" },
+ ],
+ "active"
+ ),
+ MockAlert(
+ [],
+ [
+ { name: "alertname", value: "Alert3" },
+ { name: "instance", value: "dev1" },
+ ],
"active"
),
- MockAlert([], { alertname: "Alert3", instance: "dev1" }, "active"),
],
[],
- {
- job: "mock",
- },
+ [{ name: "job", value: "mock" }],
{}
);
group.allLabels.active = {
diff --git a/ui/src/Stores/SilenceFormStore.ts b/ui/src/Stores/SilenceFormStore.ts
index 76127f11e..f319ec7ec 100644
--- a/ui/src/Stores/SilenceFormStore.ts
+++ b/ui/src/Stores/SilenceFormStore.ts
@@ -119,7 +119,11 @@ const MatchersFromAlerts = (
// add matchers for all shared labels in this group
for (const [key, value] of Object.entries(
- Object.assign({}, group.labels, group.shared.labels)
+ Object.assign(
+ {},
+ Object.fromEntries(group.labels.map((l) => [l.name, l.value])),
+ Object.fromEntries(group.shared.labels.map((l) => [l.name, l.value]))
+ )
)) {
if (!stripLabels.includes(key)) {
const matcher = NewEmptyMatcher();
@@ -131,7 +135,7 @@ const MatchersFromAlerts = (
// array of arrays with label keys for each alert
const allLabelKeys = alerts
- .map((alert) => Object.keys(alert.labels))
+ .map((alert) => alert.labels.map((l) => l.name))
.filter((a) => a.length > 0);
// this is the list of label key that are shared across all alerts in the group
@@ -153,12 +157,15 @@ const MatchersFromAlerts = (
// add matchers for all unique labels in this group
const labels: { [key: string]: Set } = {};
for (const alert of alerts) {
- for (const [key, value] of Object.entries(alert.labels)) {
- if (sharedLabelKeys.includes(key) && !stripLabels.includes(key)) {
- if (!labels[key]) {
- labels[key] = new Set();
+ for (const label of alert.labels) {
+ if (
+ sharedLabelKeys.includes(label.name) &&
+ !stripLabels.includes(label.name)
+ ) {
+ if (!labels[label.name]) {
+ labels[label.name] = new Set();
}
- labels[key].add(value);
+ labels[label.name].add(label.value);
}
}
}
diff --git a/ui/src/__fixtures__/Alerts.ts b/ui/src/__fixtures__/Alerts.ts
index 95e21e248..e26250dd9 100644
--- a/ui/src/__fixtures__/Alerts.ts
+++ b/ui/src/__fixtures__/Alerts.ts
@@ -63,7 +63,7 @@ const MockAlertGroup = (
suppressed: {},
unprocessed: {},
},
- id: "099c5ca6d1c92f615b13056b935d0c8dee70f18c",
+ id: "839708582c92ce59088d0af392601eec2d0fc02b",
alertmanagerCount: {
default: 1,
},
diff --git a/ui/src/__fixtures__/Fetch.ts b/ui/src/__fixtures__/Fetch.ts
index 800d69928..47bc98446 100644
--- a/ui/src/__fixtures__/Fetch.ts
+++ b/ui/src/__fixtures__/Fetch.ts
@@ -95,10 +95,10 @@ const MockAPIResponse = (): APIAlertsResponseT => {
labelValue: "",
alertGroups: [
MockAlertGroup(
- { alertname: "foo" },
- [MockAlert([], { instance: "foo" }, "suppressed")],
+ [{ name: "alertname", value: "foo" }],
+ [MockAlert([], [{ name: "instance", value: "foo" }], "suppressed")],
[],
- { cluster: "dev" },
+ [{ name: "cluster", value: "dev" }],
{}
),
],
diff --git a/ui/src/__fixtures__/Stories.ts b/ui/src/__fixtures__/Stories.ts
index fa10bdd82..6685ea4a2 100644
--- a/ui/src/__fixtures__/Stories.ts
+++ b/ui/src/__fixtures__/Stories.ts
@@ -64,17 +64,20 @@ const MockGroup = (
},
]
: [],
- { instance: `instance${i}` },
+ [{ name: "instance", value: `instance${i}` }],
state
);
alert.startsAt = subMinutes(new Date(), alertCount).toISOString();
alerts.push(alert);
}
const group = MockAlertGroup(
- { alertname: "Fake Alert", group: groupName },
+ [
+ { name: "alertname", value: "Fake Alert" },
+ { name: "group", value: groupName },
+ ],
alerts,
[],
- {},
+ [],
{}
);
return group;
@@ -170,10 +173,10 @@ const MockGrid = (alertStore: AlertStore): void => {
group.shared.clusters = ["default"];
}
if (i < 3) {
- group.shared.labels = {
- cluster: `prod${i}`,
- job: "textfile_exporter",
- };
+ group.shared.labels = [
+ { name: "cluster", value: `prod${i}` },
+ { name: "job", value: "textfile_exporter" },
+ ];
}
if (i < 5) {
group.shared.annotations = [
diff --git a/ui/src/__fixtures__/useFetchGet.ts b/ui/src/__fixtures__/useFetchGet.ts
index b77db0c36..5408b1f29 100644
--- a/ui/src/__fixtures__/useFetchGet.ts
+++ b/ui/src/__fixtures__/useFetchGet.ts
@@ -105,7 +105,7 @@ const useFetchGetMock = (
},
{
re: /^\.\/alertList\.json\?q=/,
- response: { alerts: [{ instance: "foo" }] },
+ response: { alerts: [[{ name: "instance", value: "foo" }]] },
},
// silence browser
{