diff --git a/ui/src/Components/Fetcher/index.test.tsx b/ui/src/Components/Fetcher/index.test.tsx
index d91cd99b8..38567f3e5 100644
--- a/ui/src/Components/Fetcher/index.test.tsx
+++ b/ui/src/Components/Fetcher/index.test.tsx
@@ -149,7 +149,7 @@ describe("", () => {
const fetchSpy = jest.spyOn(alertStore, "fetchWithThrottle");
settingsStore.gridConfig.setSortOrder("default");
mount();
- expect(fetchSpy).toHaveBeenCalledWith("", false, "", "", false, {});
+ expect(fetchSpy).toHaveBeenCalledWith("", false, "", "", false, {}, 5, {});
});
it("calls alertStore.fetchWithThrottle with correct sort arguments when sortOrder=disabled reverseSort=false", () => {
@@ -158,7 +158,16 @@ describe("", () => {
settingsStore.gridConfig.setSortOrder("disabled");
settingsStore.gridConfig.setSortReverse(false);
mount();
- expect(fetchSpy).toHaveBeenCalledWith("", false, "disabled", "", false, {});
+ expect(fetchSpy).toHaveBeenCalledWith(
+ "",
+ false,
+ "disabled",
+ "",
+ false,
+ {},
+ 5,
+ {}
+ );
});
it("calls alertStore.fetchWithThrottle with correct sort arguments when sortOrder=disabled reverseSort=true", () => {
@@ -167,7 +176,16 @@ describe("", () => {
settingsStore.gridConfig.setSortOrder("disabled");
settingsStore.gridConfig.setSortReverse(true);
mount();
- expect(fetchSpy).toHaveBeenCalledWith("", false, "disabled", "", false, {});
+ expect(fetchSpy).toHaveBeenCalledWith(
+ "",
+ false,
+ "disabled",
+ "",
+ false,
+ {},
+ 5,
+ {}
+ );
});
it("calls alertStore.fetchWithThrottle with correct sort arguments when sortOrder=startsAt reverseSort=false", () => {
@@ -176,7 +194,16 @@ describe("", () => {
settingsStore.gridConfig.setSortOrder("startsAt");
settingsStore.gridConfig.setSortReverse(false);
mount();
- expect(fetchSpy).toHaveBeenCalledWith("", false, "startsAt", "", false, {});
+ expect(fetchSpy).toHaveBeenCalledWith(
+ "",
+ false,
+ "startsAt",
+ "",
+ false,
+ {},
+ 5,
+ {}
+ );
});
it("calls alertStore.fetchWithThrottle with correct sort arguments when sortOrder=startsAt reverseSort=true", () => {
@@ -185,7 +212,16 @@ describe("", () => {
settingsStore.gridConfig.setSortOrder("startsAt");
settingsStore.gridConfig.setSortReverse(true);
mount();
- expect(fetchSpy).toHaveBeenCalledWith("", false, "startsAt", "", true, {});
+ expect(fetchSpy).toHaveBeenCalledWith(
+ "",
+ false,
+ "startsAt",
+ "",
+ true,
+ {},
+ 5,
+ {}
+ );
});
it("calls alertStore.fetchWithThrottle with correct sort arguments when sortOrder=label sortLabel=cluster reverseSort=false", () => {
@@ -201,6 +237,8 @@ describe("", () => {
"label",
"cluster",
false,
+ {},
+ 5,
{}
);
});
@@ -212,7 +250,16 @@ describe("", () => {
settingsStore.gridConfig.setSortLabel("job");
settingsStore.gridConfig.setSortReverse(true);
mount();
- expect(fetchSpy).toHaveBeenCalledWith("", false, "label", "job", true, {});
+ expect(fetchSpy).toHaveBeenCalledWith(
+ "",
+ false,
+ "label",
+ "job",
+ true,
+ {},
+ 5,
+ {}
+ );
});
it("calls alertStore.fetchWithThrottle with correct sort arguments when sortOrder=label sortLabel=instance reverseSort=null", () => {
@@ -228,6 +275,8 @@ describe("", () => {
"label",
"instance",
false,
+ {},
+ 5,
{}
);
});
@@ -239,7 +288,16 @@ describe("", () => {
settingsStore.multiGridConfig.setGridLabel("cluster");
settingsStore.multiGridConfig.setGridSortReverse(false);
mount();
- expect(fetchSpy).toHaveBeenCalledWith("cluster", false, "", "", false, {});
+ expect(fetchSpy).toHaveBeenCalledWith(
+ "cluster",
+ false,
+ "",
+ "",
+ false,
+ {},
+ 5,
+ {}
+ );
});
it("calls alertStore.fetchWithThrottle with gridLabel=cluster gridSortReverse=true", () => {
@@ -249,7 +307,16 @@ describe("", () => {
settingsStore.multiGridConfig.setGridLabel("cluster");
settingsStore.multiGridConfig.setGridSortReverse(true);
mount();
- expect(fetchSpy).toHaveBeenCalledWith("cluster", true, "", "", false, {});
+ expect(fetchSpy).toHaveBeenCalledWith(
+ "cluster",
+ true,
+ "",
+ "",
+ false,
+ {},
+ 5,
+ {}
+ );
});
it("calls alertStore.fetchWithThrottle with gridLabel= gridSortReverse=true", () => {
@@ -259,7 +326,7 @@ describe("", () => {
settingsStore.multiGridConfig.setGridLabel("");
settingsStore.multiGridConfig.setGridSortReverse(true);
mount();
- expect(fetchSpy).toHaveBeenCalledWith("", true, "", "", false, {});
+ expect(fetchSpy).toHaveBeenCalledWith("", true, "", "", false, {}, 5, {});
});
it("calls alertStore.fetchWithThrottle with limits set", () => {
@@ -268,10 +335,19 @@ describe("", () => {
settingsStore.gridConfig.setSortOrder("default");
settingsStore.multiGridConfig.setGridLabel("");
settingsStore.multiGridConfig.setGridSortReverse(false);
- alertStore.ui.setLimit("old", "bar", 10);
- alertStore.ui.setLimit("foo", "bar", 5);
+ alertStore.ui.setGridGroupLimit("old", "bar", 10);
+ alertStore.ui.setGridGroupLimit("foo", "bar", 5);
mount();
- expect(fetchSpy).toHaveBeenCalledWith("", false, "", "", false, { bar: 5 });
+ expect(fetchSpy).toHaveBeenCalledWith(
+ "",
+ false,
+ "",
+ "",
+ false,
+ { bar: 5 },
+ 5,
+ {}
+ );
});
it("internal timer is null after unmount", () => {
diff --git a/ui/src/Components/Fetcher/index.tsx b/ui/src/Components/Fetcher/index.tsx
index 21f1cf893..4faa4e59c 100644
--- a/ui/src/Components/Fetcher/index.tsx
+++ b/ui/src/Components/Fetcher/index.tsx
@@ -176,9 +176,11 @@ const Fetcher: FC<{
sortSettings.sortOrder,
sortSettings.sortLabel,
sortSettings.sortReverse === "1",
- Object.values(alertStore.ui.limits).length > 0
- ? toJS(Object.values(alertStore.ui.limits)[0])
- : {}
+ Object.values(alertStore.ui.gridGroupLimits).length > 0
+ ? toJS(Object.values(alertStore.ui.gridGroupLimits)[0])
+ : {},
+ settingsStore.alertGroupConfig.config.defaultRenderCount,
+ alertStore.ui.groupAlertLimits
);
};
@@ -197,7 +199,11 @@ const Fetcher: FC<{
grid: {
sortOrder: toJS(settingsStore.gridConfig.config.sortOrder),
sortLabel: toJS(settingsStore.gridConfig.config.sortLabel),
- limits: toJS(alertStore.ui.limits),
+ gridGroupLimits: toJS(alertStore.ui.gridGroupLimits),
+ defaultGroupLimit: toJS(
+ settingsStore.alertGroupConfig.config.defaultRenderCount
+ ),
+ groupAlertLimits: toJS(alertStore.ui.groupAlertLimits),
},
multigrid: {
gridLabel: toJS(settingsStore.multiGridConfig.config.gridLabel),
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/index.test.tsx b/ui/src/Components/Grid/AlertGrid/AlertGroup/index.test.tsx
index a0c076fc5..3f52da16c 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/index.test.tsx
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/index.test.tsx
@@ -56,7 +56,7 @@ afterEach(() => {
global.innerWidth = originalInnerWidth;
});
-const MockAlerts = (alertCount: number) => {
+const MockAlerts = (alertCount: number, totalAlerts: number) => {
for (let i = 1; i <= alertCount; i++) {
const alert = MockAlert([], { instance: `instance${i}` }, "active");
const startsAt = new Date();
@@ -64,12 +64,12 @@ const MockAlerts = (alertCount: number) => {
alert.alertmanager[0].startsAt = startsAt.toISOString();
group.alerts.push(alert);
}
+ group.totalAlerts = totalAlerts;
};
const MountedAlertGroup = (
afterUpdate: () => void,
showAlertmanagers: boolean,
- initialAlertsToRender?: number,
theme?: ThemeCtx
) => {
return mount(
@@ -77,7 +77,6 @@ const MountedAlertGroup = (
afterUpdate={afterUpdate}
group={group}
showAlertmanagers={showAlertmanagers}
- initialAlertsToRender={initialAlertsToRender}
settingsStore={settingsStore}
alertStore={alertStore}
silenceFormStore={silenceFormStore}
@@ -100,32 +99,31 @@ const ValidateCollapse = (
settingsStore.alertGroupConfig.setDefaultCollapseState(defaultCollapseState);
- MockAlerts(3);
+ MockAlerts(3, 3);
const tree = MountedAlertGroup(jest.fn(), false);
expect(tree.find("Alert")).toHaveLength(shouldBeCollapsed ? 0 : 3);
};
describe("", () => {
it("doesn't crash on unmount", () => {
- MockAlerts(5);
+ MockAlerts(5, 5);
const tree = MountedAlertGroup(jest.fn(), true);
tree.unmount();
});
it("uses 'animate' class when settingsStore.themeConfig.config.animations is true", () => {
- MockAlerts(5);
- const tree = MountedAlertGroup(jest.fn(), true, 5, MockThemeContext);
+ MockAlerts(5, 5);
+ const tree = MountedAlertGroup(jest.fn(), true, MockThemeContext);
expect(
tree.find("div.components-grid-alertgrid-alertgroup").hasClass("animate")
).toBe(true);
});
it("doesn't use 'animate' class when settingsStore.themeConfig.config.animations is false", () => {
- MockAlerts(5);
+ MockAlerts(5, 5);
const tree = MountedAlertGroup(
jest.fn(),
true,
- 5,
MockThemeContextWithoutAnimations
);
expect(
@@ -134,14 +132,14 @@ describe("", () => {
});
it("renders Alertmanager cluster labels in footer if shared", () => {
- MockAlerts(2);
+ MockAlerts(2, 2);
group.shared.clusters = ["default"];
const tree = MountedAlertGroup(jest.fn(), true).find("AlertGroup");
expect(tree.find("GroupFooter").html()).toMatch(/@cluster/);
});
it("only renders one @cluster label per cluster in the footer", () => {
- MockAlerts(2);
+ MockAlerts(2, 2);
for (let i = 0; i < group.alerts.length; i++) {
group.alerts[i].alertmanager.push({
fingerprint: "123",
@@ -174,7 +172,7 @@ describe("", () => {
});
it("doesn't render @cluster labels with empty alertmanager array", () => {
- MockAlerts(2);
+ MockAlerts(2, 2);
for (let i = 0; i < group.alerts.length; i++) {
group.alerts[i].alertmanager = [];
}
@@ -185,7 +183,7 @@ describe("", () => {
});
it("doesn't render @cluster labels in footer when they are unique", () => {
- MockAlerts(5);
+ MockAlerts(5, 5);
for (let i = 0; i < group.alerts.length; i++) {
group.alerts[i].alertmanager[0].name = `fakeAlertmanager${i}`;
}
@@ -206,7 +204,7 @@ describe("", () => {
});
it("only renders titlebar when collapsed", () => {
- MockAlerts(10);
+ MockAlerts(5, 10);
const tree = MountedAlertGroup(jest.fn(), false);
tree.find("span.badge.cursor-pointer").at(1).simulate("click");
expect(tree.find("Alert")).toHaveLength(0);
@@ -214,9 +212,9 @@ describe("", () => {
});
it("renders reduced details when idle", () => {
- MockAlerts(10);
+ MockAlerts(5, 10);
alertStore.ui.setIsIdle(true);
- const tree = MountedAlertGroup(jest.fn(), true, 10, MockThemeContext);
+ const tree = MountedAlertGroup(jest.fn(), true, MockThemeContext);
expect(tree.find("Alert")).toHaveLength(1);
});
@@ -252,40 +250,43 @@ describe("", () => {
it("renders @receiver label when alertStore.data.receivers.length > 1", () => {
alertStore.data.setReceivers(["foo", "bar"]);
- MockAlerts(10);
+ MockAlerts(5, 10);
const tree = MountedAlertGroup(jest.fn(), false);
expect(tree.html()).toMatch(/@receiver:/);
});
it("doesn't render @receiver label when alertStore.data.receivers.length == 0", () => {
alertStore.data.setReceivers([]);
- MockAlerts(10);
+ MockAlerts(5, 10);
const tree = MountedAlertGroup(jest.fn(), false);
expect(tree.html()).not.toMatch(/@receiver:/);
});
});
-const ValidateLoadButtonPresent = (totalAlerts: number, isPresent: boolean) => {
- MockAlerts(totalAlerts);
+const ValidateLoadButtonPresent = (
+ alertCount: number,
+ totalAlerts: number,
+ isPresent: boolean
+) => {
+ MockAlerts(alertCount, totalAlerts);
const tree = MountedAlertGroup(jest.fn(), false);
const buttons = tree.find("button");
expect(buttons).toHaveLength(isPresent ? 2 : 0);
};
const ValidateLoadButtonAction = (
+ alertCount: number,
totalAlerts: number,
buttonIndex: number,
iconMatch: RegExp,
- loadedAlerts: number,
- alertsToRenderBeforeClick?: number
+ loadedAlerts: number
) => {
- MockAlerts(totalAlerts);
- const tree = MountedAlertGroup(jest.fn(), false, alertsToRenderBeforeClick);
+ MockAlerts(alertCount, totalAlerts);
+ const tree = MountedAlertGroup(jest.fn(), false);
const loadMore = tree.find("button").at(buttonIndex);
expect(loadMore.html()).toMatch(iconMatch);
loadMore.simulate("click");
- tree.update();
- expect(tree.find("Alert")).toHaveLength(loadedAlerts);
+ expect(alertStore.ui.groupAlertLimits[group.id]).toBe(loadedAlerts);
};
describe(" renderConfig", () => {
@@ -293,61 +294,52 @@ describe(" renderConfig", () => {
expect(settingsStore.alertGroupConfig.config.defaultRenderCount).toBe(5);
});
- it("renders only up to settingsStore.alertGroupConfig.config.defaultRenderCount alerts", () => {
- MockAlerts(50);
- const tree = MountedAlertGroup(jest.fn(), false).find("AlertGroup");
- const alerts = tree.find("Alert");
- expect(alerts).toHaveLength(
- settingsStore.alertGroupConfig.config.defaultRenderCount
- );
- });
-
it("load buttons are not rendered for 1 alert", () => {
- ValidateLoadButtonPresent(1, false);
+ ValidateLoadButtonPresent(1, 1, false);
});
it("load buttons are not rendered for 5 alerts", () => {
- ValidateLoadButtonPresent(5, false);
+ ValidateLoadButtonPresent(5, 5, false);
});
it("load buttons are rendered for 6 alert", () => {
- ValidateLoadButtonPresent(6, true);
+ ValidateLoadButtonPresent(5, 6, true);
});
it("clicking - icon hides 1 alert if there's 6 in total", () => {
- ValidateLoadButtonAction(6, 0, /fa-minus/, 5, 6);
+ ValidateLoadButtonAction(6, 6, 0, /fa-minus/, 5);
});
it("clicking - icon hides 1 alert if there's 6 in total and we're showing 3", () => {
- ValidateLoadButtonAction(6, 0, /fa-minus/, 2, 3);
+ ValidateLoadButtonAction(3, 6, 0, /fa-minus/, 2);
});
it("clicking - icon hides 2 alerts if there's 7 in total and we're showing 7", () => {
- ValidateLoadButtonAction(7, 0, /fa-minus/, 5, 7);
+ ValidateLoadButtonAction(7, 7, 0, /fa-minus/, 5);
});
it("clicking - icon hides 5 alerts if there's 10 in total and we're showing 10", () => {
- ValidateLoadButtonAction(10, 0, /fa-minus/, 5, 10);
+ ValidateLoadButtonAction(10, 10, 0, /fa-minus/, 5);
});
it("clicking - icon hides 5 alerts if there's 18 in total and we're showing 17", () => {
- ValidateLoadButtonAction(18, 0, /fa-minus/, 12, 17);
+ ValidateLoadButtonAction(17, 18, 0, /fa-minus/, 12);
});
it("clicking + icon loads 1 more alert if there's 6 in total", () => {
- ValidateLoadButtonAction(6, 1, /fa-plus/, 6);
+ ValidateLoadButtonAction(5, 6, 1, /fa-plus/, 6);
});
it("clicking + icon loads 4 more alert if there's 9 in total", () => {
- ValidateLoadButtonAction(9, 1, /fa-plus/, 9);
+ ValidateLoadButtonAction(5, 9, 1, /fa-plus/, 9);
});
it("clicking + icon loads 5 more alert if there's 14 in total", () => {
- ValidateLoadButtonAction(14, 1, /fa-plus/, 10);
+ ValidateLoadButtonAction(5, 14, 1, /fa-plus/, 10);
});
it("clicking + icon loads 5 more alert if there's 25 in total and we're showing 16", () => {
- ValidateLoadButtonAction(25, 1, /fa-plus/, 22, 17);
+ ValidateLoadButtonAction(16, 25, 1, /fa-plus/, 21);
});
it("uses 'z-index: 100' style after setIsMenuOpen() is called on any Alert", async () => {
@@ -355,7 +347,7 @@ describe(" renderConfig", () => {
fetchMock.mock("*", { body: "" });
const promise = Promise.resolve();
- MockAlerts(5);
+ MockAlerts(5, 5);
const tree = MountedAlertGroup(jest.fn(), false);
tree
@@ -373,7 +365,7 @@ describe(" renderConfig", () => {
it("uses 'z-index: 100' style after setIsMenuOpen() is called on AlertGroup header menu", async () => {
const promise = Promise.resolve();
- MockAlerts(5);
+ MockAlerts(5, 5);
const tree = MountedAlertGroup(jest.fn(), false);
tree.find("span.cursor-pointer").at(0).simulate("click");
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/index.tsx b/ui/src/Components/Grid/AlertGrid/AlertGroup/index.tsx
index c3f2e6d30..b8b7983b8 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/index.tsx
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/index.tsx
@@ -48,7 +48,6 @@ const AlertGroup: FC<{
silenceFormStore: SilenceFormStore;
groupWidth: number;
gridLabelValue: string;
- initialAlertsToRender?: number;
}> = ({
group,
showAlertmanagers,
@@ -58,17 +57,10 @@ const AlertGroup: FC<{
settingsStore,
groupWidth,
gridLabelValue,
- initialAlertsToRender,
}) => {
const defaultRenderCount =
settingsStore.alertGroupConfig.config.defaultRenderCount;
- const [alertsToRenderInternal, setAlertsToRender] = useState(
- initialAlertsToRender || null
- );
-
- const alertsToRender = alertsToRenderInternal || defaultRenderCount;
-
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isCollapsed, setIsCollapsed] = useState(
@@ -94,15 +86,19 @@ const AlertGroup: FC<{
};
const loadMore = () => {
- const step = getStepSize(group.alerts.length);
- // show cur+step, but not more that total alert count
- setAlertsToRender(Math.min(alertsToRender + step, group.alerts.length));
+ const step = getStepSize(group.totalAlerts);
+ alertStore.ui.setGroupAlertLimit(
+ group.id,
+ Math.min(group.alerts.length + step, group.totalAlerts)
+ );
};
const loadLess = () => {
- const step = getStepSize(group.alerts.length);
- // show cur-step, but not less than 1
- setAlertsToRender(Math.max(alertsToRender - step, 1));
+ const step = getStepSize(group.totalAlerts);
+ alertStore.ui.setGroupAlertLimit(
+ group.id,
+ Math.max(group.alerts.length - step, 1)
+ );
};
const onAlertGroupCollapseEvent = useCallback(
@@ -177,7 +173,7 @@ const AlertGroup: FC<{
) : null}
{group.alerts
- .slice(0, alertStore.ui.isIdle ? 1 : alertsToRender)
+ .slice(0, alertStore.ui.isIdle ? 1 : group.alerts.length)
.map((alert) => (
))}
- {group.alerts.length > defaultRenderCount ? (
+ {group.totalAlerts > defaultRenderCount ? (
- {Math.min(alertsToRender, group.alerts.length)}
- {" of "}
{group.alerts.length}
+ {" of "}
+ {group.totalAlerts}
{
- alertStore.ui.setLimit(
+ alertStore.ui.setGridGroupLimit(
grid.labelName,
grid.labelValue,
grid.alertGroups.length +
diff --git a/ui/src/Components/Grid/AlertGrid/index.test.tsx b/ui/src/Components/Grid/AlertGrid/index.test.tsx
index 5fa909df5..02a35e699 100644
--- a/ui/src/Components/Grid/AlertGrid/index.test.tsx
+++ b/ui/src/Components/Grid/AlertGrid/index.test.tsx
@@ -241,7 +241,7 @@ describe("", () => {
}
);
tree.find("button").simulate("click");
- expect(alertStore.ui.limits).toStrictEqual({
+ expect(alertStore.ui.gridGroupLimits).toStrictEqual({
"": { "": 40 + alertStore.settings.values.gridGroupLimit },
});
});
@@ -276,7 +276,7 @@ describe("", () => {
}
);
tree.find("button").simulate("click");
- expect(alertStore.ui.limits).toStrictEqual({
+ expect(alertStore.ui.gridGroupLimits).toStrictEqual({
foo: { bar: 70 },
});
});
diff --git a/ui/src/Models/APITypes.ts b/ui/src/Models/APITypes.ts
index a1a7e0c2f..b4a4224a0 100644
--- a/ui/src/Models/APITypes.ts
+++ b/ui/src/Models/APITypes.ts
@@ -58,6 +58,7 @@ export interface APIAlertGroupT {
receiver: string;
labels: LabelsT;
alerts: APIAlertT[];
+ totalAlerts: number;
alertmanagerCount: { [key: string]: number };
stateCount: StateCountT;
shared: {
@@ -238,4 +239,6 @@ export interface AlertsRequestT {
sortOrder: string;
sortLabel: string;
sortReverse: boolean;
+ defaultGroupLimit: number;
+ groupLimits: { [key: string]: number };
}
diff --git a/ui/src/Stores/AlertStore.test.ts b/ui/src/Stores/AlertStore.test.ts
index 0a7ffbccb..0297cdea6 100644
--- a/ui/src/Stores/AlertStore.test.ts
+++ b/ui/src/Stores/AlertStore.test.ts
@@ -545,7 +545,7 @@ describe("AlertStore.fetch", () => {
const store = new AlertStore(["label=value"]);
await expect(
- store.fetch("", false, "", "", false, {})
+ store.fetch("", false, "", "", false, {}, 5, {})
).resolves.toBeUndefined();
expect(fetchMock.calls()).toHaveLength(1);
@@ -561,7 +561,7 @@ describe("AlertStore.fetch", () => {
const store = new AlertStore([]);
await expect(
- store.fetch("", false, "", "", false, {})
+ store.fetch("", false, "", "", false, {}, 5, {})
).resolves.toBeUndefined();
expect(fetchMock.calls()).toHaveLength(1);
@@ -580,7 +580,7 @@ describe("AlertStore.fetch", () => {
const store = new AlertStore([]);
await expect(
- store.fetch("", false, "", "", false, {})
+ store.fetch("", false, "", "", false, {}, 5, {})
).resolves.toHaveProperty("error");
expect(fetchMock.calls()).toHaveLength(10);
@@ -600,7 +600,7 @@ describe("AlertStore.fetch", () => {
});
await expect(
- store.fetch("", false, "", "", false, {})
+ store.fetch("", false, "", "", false, {}, 5, {})
).resolves.toHaveProperty("error");
expect(fetchMock.calls()).toHaveLength(10);
});
@@ -615,7 +615,7 @@ describe("AlertStore.fetch", () => {
});
await expect(
- store.fetch("", false, "", "", false, {})
+ store.fetch("", false, "", "", false, {}, 5, {})
).resolves.toHaveProperty("error");
expect(fetchMock.calls()).toHaveLength(10);
@@ -626,7 +626,7 @@ describe("AlertStore.fetch", () => {
});
await expect(
- store.fetch("", false, "", "", false, {})
+ store.fetch("", false, "", "", false, {}, 5, {})
).resolves.toBeUndefined();
expect(fetchMock.calls()).toHaveLength(1);
@@ -636,7 +636,7 @@ describe("AlertStore.fetch", () => {
});
await expect(
- store.fetch("", false, "", "", false, {})
+ store.fetch("", false, "", "", false, {}, 5, {})
).resolves.toHaveProperty("error");
expect(fetchMock.calls()).toHaveLength(10);
});
@@ -656,7 +656,7 @@ describe("AlertStore.fetch", () => {
);
await expect(
- store.fetch("", false, "", "", false, {})
+ store.fetch("", false, "", "", false, {}, 5, {})
).resolves.toBeUndefined();
expect(store.info.reloadNeeded).toBe(true);
@@ -673,7 +673,7 @@ describe("AlertStore.fetch", () => {
});
await expect(
- store.fetch("", false, "", "", false, {})
+ store.fetch("", false, "", "", false, {}, 5, {})
).resolves.toHaveProperty("error");
expect(store.filters.values[0].applied).toBe(true);
});
@@ -690,7 +690,7 @@ describe("AlertStore.fetch", () => {
// initial fetch, should update settings
store.settings.setValues({ foo: "bar" } as any);
await expect(
- store.fetch("", false, "", "", false, {})
+ store.fetch("", false, "", "", false, {}, 5, {})
).resolves.toBeUndefined();
expect(store.settings.values).toMatchObject({
staticColorLabels: ["job"],
@@ -702,7 +702,7 @@ describe("AlertStore.fetch", () => {
// second fetch, should keep same settings
await expect(
- store.fetch("", false, "", "", false, {})
+ store.fetch("", false, "", "", false, {}, 5, {})
).resolves.toBeUndefined();
expect(store.settings.values).toMatchObject({
staticColorLabels: ["job"],
@@ -721,7 +721,7 @@ describe("AlertStore.fetch", () => {
});
const store = new AlertStore(["label=value"]);
await expect(
- store.fetch("", false, "", "", false, {})
+ store.fetch("", false, "", "", false, {}, 5, {})
).resolves.toBeUndefined();
expect(store.info.upgradeReady).toBe(false);
@@ -731,7 +731,7 @@ describe("AlertStore.fetch", () => {
body: JSON.stringify(response),
});
await expect(
- store.fetch("", false, "", "", false, {})
+ store.fetch("", false, "", "", false, {}, 5, {})
).resolves.toBeUndefined();
expect(store.info.upgradeReady).toBe(true);
});
@@ -797,7 +797,7 @@ describe("AlertStore.fetch", () => {
});
const store = new AlertStore(["label=value"]);
await expect(
- store.fetch("", false, "sortOrder", "sortLabel", false, {})
+ store.fetch("", false, "sortOrder", "sortLabel", false, {}, 5, {})
).resolves.toBeUndefined();
expect(fetchMock.calls().length).toEqual(1);
expect(fetchMock.calls()[0][0]).toBe("/alerts.json");
@@ -809,6 +809,8 @@ describe("AlertStore.fetch", () => {
sortOrder: "sortOrder",
sortLabel: "sortLabel",
sortReverse: false,
+ defaultGroupLimit: 5,
+ groupLimits: {},
});
});
@@ -820,7 +822,7 @@ describe("AlertStore.fetch", () => {
});
const store = new AlertStore(["label=value"]);
await expect(
- store.fetch("cluster", true, "sortOrder", "sortLabel", true, {})
+ store.fetch("cluster", true, "sortOrder", "sortLabel", true, {}, 5, {})
).resolves.toBeUndefined();
expect(fetchMock.calls().length).toEqual(1);
expect(fetchMock.calls()[0][0]).toBe("/alerts.json");
@@ -832,6 +834,8 @@ describe("AlertStore.fetch", () => {
sortOrder: "sortOrder",
sortLabel: "sortLabel",
sortReverse: true,
+ defaultGroupLimit: 5,
+ groupLimits: {},
});
});
@@ -842,11 +846,23 @@ describe("AlertStore.fetch", () => {
body: JSON.stringify(response),
});
const store = new AlertStore(["label=value"]);
- store.ui.setLimit("foo", "bar", 7);
+ store.ui.setGridGroupLimit("foo", "bar", 7);
+ store.ui.setGroupAlertLimit("1234567890", 7);
+ store.ui.setGroupAlertLimit("1234567891", 1);
+
await expect(
- store.fetch("cluster", true, "sortOrder", "sortLabel", false, {
- bar: 7,
- })
+ store.fetch(
+ "cluster",
+ true,
+ "sortOrder",
+ "sortLabel",
+ false,
+ {
+ bar: 7,
+ },
+ 5,
+ { "1234567890": 7, "1234567891": 1 }
+ )
).resolves.toBeUndefined();
expect(fetchMock.calls().length).toEqual(1);
expect(fetchMock.calls()[0][0]).toBe("/alerts.json");
@@ -858,6 +874,66 @@ describe("AlertStore.fetch", () => {
sortOrder: "sortOrder",
sortLabel: "sortLabel",
sortReverse: false,
+ defaultGroupLimit: 5,
+ groupLimits: { "1234567890": 7, "1234567891": 1 },
});
});
+
+ it("purges unknown group limits after fetch", async () => {
+ const store = new AlertStore(["label=value"]);
+ store.ui.setGridGroupLimit("foo", "bar", 7);
+ store.ui.setGroupAlertLimit("g1", 7);
+ store.ui.setGroupAlertLimit("g2", 1);
+ store.ui.setGroupAlertLimit("g4", 6);
+
+ const g1 = MockGroup("group1", 1, 1, 0);
+ g1.id = "g1";
+ const g2 = MockGroup("group2", 1, 1, 0);
+ g2.id = "g2";
+ const g3 = MockGroup("group3", 1, 1, 0);
+ g3.id = "g3";
+ const response = EmptyAPIResponse();
+ response.grids = [
+ {
+ labelName: "",
+ labelValue: "",
+ alertGroups: [g1, g2, g3],
+ totalGroups: 3,
+ stateCount: { unprocessed: 0, active: 3, suppressed: 0 },
+ },
+ ];
+ fetchMock.reset();
+ fetchMock.mock("*", {
+ body: JSON.stringify(response),
+ });
+
+ await expect(
+ store.fetch(
+ "cluster",
+ true,
+ "sortOrder",
+ "sortLabel",
+ false,
+ {
+ bar: 7,
+ },
+ 5,
+ { g1: 7, g2: 1, g4: 6 }
+ )
+ ).resolves.toBeUndefined();
+ expect(fetchMock.calls().length).toEqual(1);
+ expect(fetchMock.calls()[0][0]).toBe("/alerts.json");
+ expect(JSON.parse(fetchMock.calls()[0][1]?.body as string)).toStrictEqual({
+ filters: ["label=value"],
+ gridLabel: "cluster",
+ gridLimits: { bar: 7 },
+ gridSortReverse: true,
+ sortOrder: "sortOrder",
+ sortLabel: "sortLabel",
+ sortReverse: false,
+ defaultGroupLimit: 5,
+ groupLimits: { g1: 7, g2: 1, g4: 6 },
+ });
+ expect(store.ui.groupAlertLimits).toStrictEqual({ g1: 7, g2: 1 });
+ });
});
diff --git a/ui/src/Stores/AlertStore.ts b/ui/src/Stores/AlertStore.ts
index a22a20fb3..591e2acf6 100644
--- a/ui/src/Stores/AlertStore.ts
+++ b/ui/src/Stores/AlertStore.ts
@@ -209,8 +209,11 @@ interface AlertStoreStatusT {
interface AlertStoreUIT {
isIdle: boolean;
setIsIdle: (val: boolean) => void;
- limits: { [key: string]: { [val: string]: number } };
- setLimit: (key: string, val: string, limit: number) => void;
+ gridGroupLimits: { [key: string]: { [val: string]: number } };
+ setGridGroupLimit: (key: string, val: string, limit: number) => void;
+ groupAlertLimits: { [gid: string]: number };
+ setGroupAlertLimit: (gid: string, limit: number) => void;
+ purgeGroupAlertLimits: (knownGids: string[]) => void;
}
class AlertStore {
@@ -558,14 +561,30 @@ class AlertStore {
setIsIdle(val: boolean) {
this.isIdle = val;
},
- limits: {} as { [key: string]: { [val: string]: number } },
- setLimit(key: string, val: string, limit: number) {
- this.limits = { [key]: { ...this.limits[key], [val]: limit } };
+ gridGroupLimits: {} as { [key: string]: { [val: string]: number } },
+ setGridGroupLimit(key: string, val: string, limit: number) {
+ this.gridGroupLimits = {
+ [key]: { ...this.gridGroupLimits[key], [val]: limit },
+ };
+ },
+ groupAlertLimits: {} as { [gid: string]: number },
+ setGroupAlertLimit(gid: string, limit: number) {
+ this.groupAlertLimits[gid] = limit;
+ },
+ purgeGroupAlertLimits(knownGids: string[]) {
+ const newLimits: { [gid: string]: number } = {};
+ Object.entries(this.groupAlertLimits)
+ .filter(([gid, _]) => knownGids.includes(gid))
+ .forEach(([gid, limit]) => {
+ newLimits[gid] = limit;
+ });
+ this.groupAlertLimits = newLimits;
},
},
{
setIsIdle: action.bound,
- setLimit: action.bound,
+ setGridGroupLimit: action.bound,
+ setGroupAlertLimit: action.bound,
}
);
@@ -579,7 +598,9 @@ class AlertStore {
sortOrder: string,
sortLabel: string,
sortReverse: boolean,
- limits: { [key: string]: number }
+ gridGroupLimits: { [key: string]: number },
+ defaultGroupLimit: number,
+ groupAlertLimits: { [key: string]: number }
) => {
this.status.setFetching();
@@ -587,10 +608,12 @@ class AlertStore {
filters: this.filters.values.map((f) => f.raw),
gridLabel: gridLabel,
gridSortReverse: gridSortReverse,
- gridLimits: limits,
+ gridLimits: gridGroupLimits,
sortOrder: sortOrder,
sortLabel: sortLabel,
sortReverse: sortReverse,
+ defaultGroupLimit: defaultGroupLimit,
+ groupLimits: groupAlertLimits,
};
const alertsURI = FormatBackendURI("alerts.json");
@@ -676,6 +699,16 @@ class AlertStore {
updates.receivers = result.receivers;
this.data = Object.assign(this.data, updates);
+ const knowGroups: string[] = [];
+ result.grids.map((grid) =>
+ grid.alertGroups
+ .map((group) => group.id)
+ .forEach((id) => {
+ knowGroups.push(id);
+ })
+ );
+ this.ui.purgeGroupAlertLimits(knowGroups);
+
// before storing new version check if we need to reload
if (
this.info.version !== "unknown" &&
diff --git a/ui/src/__fixtures__/Alerts.ts b/ui/src/__fixtures__/Alerts.ts
index 16467f554..8ec8198f2 100644
--- a/ui/src/__fixtures__/Alerts.ts
+++ b/ui/src/__fixtures__/Alerts.ts
@@ -56,6 +56,7 @@ const MockAlertGroup = (
): APIAlertGroupT => ({
receiver: "by-name",
labels: rootLabels,
+ totalAlerts: alerts.length,
alerts: alerts,
id: "099c5ca6d1c92f615b13056b935d0c8dee70f18c",
alertmanagerCount: {
diff --git a/ui/src/__fixtures__/Stories.ts b/ui/src/__fixtures__/Stories.ts
index 4b34db235..fa10bdd82 100644
--- a/ui/src/__fixtures__/Stories.ts
+++ b/ui/src/__fixtures__/Stories.ts
@@ -164,6 +164,8 @@ const MockGrid = (alertStore: AlertStore): void => {
group.stateCount.active = active;
group.stateCount.suppressed = suppressed;
group.stateCount.unprocessed = unprocessed;
+ group.totalAlerts = i;
+ group.alerts = group.alerts.slice(0, Math.min(i, 5));
if (i === 2 || i === 4) {
group.shared.clusters = ["default"];
}