mirror of
https://github.com/prymitive/karma
synced 2026-08-23 11:56:20 +00:00
feat(ui): support group limits
This commit is contained in:
committed by
Łukasz Mierzwa
parent
7b8e8bb17a
commit
8663e94c0b
@@ -149,7 +149,7 @@ describe("<Fetcher />", () => {
|
||||
const fetchSpy = jest.spyOn(alertStore, "fetchWithThrottle");
|
||||
settingsStore.gridConfig.setSortOrder("default");
|
||||
mount(<Fetcher alertStore={alertStore} settingsStore={settingsStore} />);
|
||||
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("<Fetcher />", () => {
|
||||
settingsStore.gridConfig.setSortOrder("disabled");
|
||||
settingsStore.gridConfig.setSortReverse(false);
|
||||
mount(<Fetcher alertStore={alertStore} settingsStore={settingsStore} />);
|
||||
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("<Fetcher />", () => {
|
||||
settingsStore.gridConfig.setSortOrder("disabled");
|
||||
settingsStore.gridConfig.setSortReverse(true);
|
||||
mount(<Fetcher alertStore={alertStore} settingsStore={settingsStore} />);
|
||||
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("<Fetcher />", () => {
|
||||
settingsStore.gridConfig.setSortOrder("startsAt");
|
||||
settingsStore.gridConfig.setSortReverse(false);
|
||||
mount(<Fetcher alertStore={alertStore} settingsStore={settingsStore} />);
|
||||
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("<Fetcher />", () => {
|
||||
settingsStore.gridConfig.setSortOrder("startsAt");
|
||||
settingsStore.gridConfig.setSortReverse(true);
|
||||
mount(<Fetcher alertStore={alertStore} settingsStore={settingsStore} />);
|
||||
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("<Fetcher />", () => {
|
||||
"label",
|
||||
"cluster",
|
||||
false,
|
||||
{},
|
||||
5,
|
||||
{}
|
||||
);
|
||||
});
|
||||
@@ -212,7 +250,16 @@ describe("<Fetcher />", () => {
|
||||
settingsStore.gridConfig.setSortLabel("job");
|
||||
settingsStore.gridConfig.setSortReverse(true);
|
||||
mount(<Fetcher alertStore={alertStore} settingsStore={settingsStore} />);
|
||||
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("<Fetcher />", () => {
|
||||
"label",
|
||||
"instance",
|
||||
false,
|
||||
{},
|
||||
5,
|
||||
{}
|
||||
);
|
||||
});
|
||||
@@ -239,7 +288,16 @@ describe("<Fetcher />", () => {
|
||||
settingsStore.multiGridConfig.setGridLabel("cluster");
|
||||
settingsStore.multiGridConfig.setGridSortReverse(false);
|
||||
mount(<Fetcher alertStore={alertStore} settingsStore={settingsStore} />);
|
||||
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("<Fetcher />", () => {
|
||||
settingsStore.multiGridConfig.setGridLabel("cluster");
|
||||
settingsStore.multiGridConfig.setGridSortReverse(true);
|
||||
mount(<Fetcher alertStore={alertStore} settingsStore={settingsStore} />);
|
||||
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("<Fetcher />", () => {
|
||||
settingsStore.multiGridConfig.setGridLabel("");
|
||||
settingsStore.multiGridConfig.setGridSortReverse(true);
|
||||
mount(<Fetcher alertStore={alertStore} settingsStore={settingsStore} />);
|
||||
expect(fetchSpy).toHaveBeenCalledWith("", true, "", "", false, {});
|
||||
expect(fetchSpy).toHaveBeenCalledWith("", true, "", "", false, {}, 5, {});
|
||||
});
|
||||
|
||||
it("calls alertStore.fetchWithThrottle with limits set", () => {
|
||||
@@ -268,10 +335,19 @@ describe("<Fetcher />", () => {
|
||||
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(<Fetcher alertStore={alertStore} settingsStore={settingsStore} />);
|
||||
expect(fetchSpy).toHaveBeenCalledWith("", false, "", "", false, { bar: 5 });
|
||||
expect(fetchSpy).toHaveBeenCalledWith(
|
||||
"",
|
||||
false,
|
||||
"",
|
||||
"",
|
||||
false,
|
||||
{ bar: 5 },
|
||||
5,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it("internal timer is null after unmount", () => {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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("<AlertGroup />", () => {
|
||||
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("<AlertGroup />", () => {
|
||||
});
|
||||
|
||||
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("<AlertGroup />", () => {
|
||||
});
|
||||
|
||||
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("<AlertGroup />", () => {
|
||||
});
|
||||
|
||||
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("<AlertGroup />", () => {
|
||||
});
|
||||
|
||||
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("<AlertGroup />", () => {
|
||||
});
|
||||
|
||||
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("<AlertGroup />", () => {
|
||||
|
||||
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("<AlertGroup /> renderConfig", () => {
|
||||
@@ -293,61 +294,52 @@ describe("<AlertGroup /> 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("<AlertGroup /> 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("<AlertGroup /> 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");
|
||||
|
||||
@@ -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<number | null>(
|
||||
initialAlertsToRender || null
|
||||
);
|
||||
|
||||
const alertsToRender = alertsToRenderInternal || defaultRenderCount;
|
||||
|
||||
const [isMenuOpen, setIsMenuOpen] = useState<boolean>(false);
|
||||
|
||||
const [isCollapsed, setIsCollapsed] = useState<boolean>(
|
||||
@@ -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}
|
||||
<ul className="list-group">
|
||||
{group.alerts
|
||||
.slice(0, alertStore.ui.isIdle ? 1 : alertsToRender)
|
||||
.slice(0, alertStore.ui.isIdle ? 1 : group.alerts.length)
|
||||
.map((alert) => (
|
||||
<Alert
|
||||
key={alert.id}
|
||||
@@ -194,7 +190,7 @@ const AlertGroup: FC<{
|
||||
setIsMenuOpen={setIsMenuOpen}
|
||||
/>
|
||||
))}
|
||||
{group.alerts.length > defaultRenderCount ? (
|
||||
{group.totalAlerts > defaultRenderCount ? (
|
||||
<li
|
||||
className="list-group-item border-0 p-0 text-center bg-transparent"
|
||||
style={{
|
||||
@@ -214,9 +210,9 @@ const AlertGroup: FC<{
|
||||
tooltip="Show fewer alerts in this group"
|
||||
/>
|
||||
<small className="text-muted mx-2">
|
||||
{Math.min(alertsToRender, group.alerts.length)}
|
||||
{" of "}
|
||||
{group.alerts.length}
|
||||
{" of "}
|
||||
{group.totalAlerts}
|
||||
</small>
|
||||
<LoadButton
|
||||
icon={faPlus}
|
||||
|
||||
@@ -179,7 +179,7 @@ const Grid: FC<{
|
||||
type="button"
|
||||
className="btn btn-secondary mb-3"
|
||||
onClick={() => {
|
||||
alertStore.ui.setLimit(
|
||||
alertStore.ui.setGridGroupLimit(
|
||||
grid.labelName,
|
||||
grid.labelValue,
|
||||
grid.alertGroups.length +
|
||||
|
||||
@@ -241,7 +241,7 @@ describe("<Grid />", () => {
|
||||
}
|
||||
);
|
||||
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("<Grid />", () => {
|
||||
}
|
||||
);
|
||||
tree.find("button").simulate("click");
|
||||
expect(alertStore.ui.limits).toStrictEqual({
|
||||
expect(alertStore.ui.gridGroupLimits).toStrictEqual({
|
||||
foo: { bar: 70 },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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" &&
|
||||
|
||||
@@ -56,6 +56,7 @@ const MockAlertGroup = (
|
||||
): APIAlertGroupT => ({
|
||||
receiver: "by-name",
|
||||
labels: rootLabels,
|
||||
totalAlerts: alerts.length,
|
||||
alerts: alerts,
|
||||
id: "099c5ca6d1c92f615b13056b935d0c8dee70f18c",
|
||||
alertmanagerCount: {
|
||||
|
||||
@@ -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"];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user