diff --git a/ui/src/Components/Fetcher/index.js b/ui/src/Components/Fetcher/index.js index 57141c014..6ff6b3600 100644 --- a/ui/src/Components/Fetcher/index.js +++ b/ui/src/Components/Fetcher/index.js @@ -81,10 +81,12 @@ const Fetcher = observer( }; callFetch = () => { - const { alertStore } = this.props; + const { alertStore, settingsStore } = this.props; const sortSettings = this.getSortSettings(); alertStore.fetchWithThrottle( + settingsStore.multiGridConfig.config.gridLabel, + settingsStore.multiGridConfig.config.gridSortReverse, sortSettings.sortOrder, sortSettings.sortLabel, sortSettings.sortReverse @@ -118,6 +120,10 @@ const Fetcher = observer( f.raw).join(" ")} data-interval={settingsStore.fetchConfig.config.interval} + data-multigrid-label={settingsStore.multiGridConfig.config.gridLabel} + data-multigrid-sort-reverse={ + settingsStore.multiGridConfig.config.gridSortReverse + } data-grid-sort-order={settingsStore.gridConfig.config.sortOrder} data-grid-sort-label={settingsStore.gridConfig.config.sortLabel} data-grid-sort-reverse={settingsStore.gridConfig.config.reverseSort} diff --git a/ui/src/Components/Fetcher/index.test.js b/ui/src/Components/Fetcher/index.test.js index 51a7e59cb..0b1b1d3c7 100644 --- a/ui/src/Components/Fetcher/index.test.js +++ b/ui/src/Components/Fetcher/index.test.js @@ -55,8 +55,12 @@ const MountedFetcher = () => { ); }; -const FetcherSpan = (label, interval, sortOrder) => - ``; +const FetcherSpan = (label, interval, sortOrder, gridLabel, gridSortReverse) => + ``; describe("", () => { it("renders correctly with 'label=value' filter", () => { @@ -147,7 +151,7 @@ describe("", () => { settingsStore.gridConfig.config.sortOrder = settingsStore.gridConfig.options.default.value; MountedFetcher(); - expect(fetchSpy).toHaveBeenCalledWith("", "", ""); + expect(fetchSpy).toHaveBeenCalledWith("", false, "", "", ""); }); it("calls alertStore.fetchWithThrottle with correct sort arguments when sortOrder=disabled reverseSort=false", () => { @@ -157,7 +161,7 @@ describe("", () => { settingsStore.gridConfig.options.disabled.value; settingsStore.gridConfig.config.reverseSort = false; MountedFetcher(); - expect(fetchSpy).toHaveBeenCalledWith("disabled", "", ""); + expect(fetchSpy).toHaveBeenCalledWith("", false, "disabled", "", ""); }); it("calls alertStore.fetchWithThrottle with correct sort arguments when sortOrder=disabled reverseSort=true", () => { @@ -167,7 +171,7 @@ describe("", () => { settingsStore.gridConfig.options.disabled.value; settingsStore.gridConfig.config.reverseSort = true; MountedFetcher(); - expect(fetchSpy).toHaveBeenCalledWith("disabled", "", ""); + expect(fetchSpy).toHaveBeenCalledWith("", false, "disabled", "", ""); }); it("calls alertStore.fetchWithThrottle with correct sort arguments when sortOrder=startsAt reverseSort=false", () => { @@ -177,7 +181,7 @@ describe("", () => { settingsStore.gridConfig.options.startsAt.value; settingsStore.gridConfig.config.reverseSort = false; MountedFetcher(); - expect(fetchSpy).toHaveBeenCalledWith("startsAt", "", "0"); + expect(fetchSpy).toHaveBeenCalledWith("", false, "startsAt", "", "0"); }); it("calls alertStore.fetchWithThrottle with correct sort arguments when sortOrder=startsAt reverseSort=true", () => { @@ -187,7 +191,7 @@ describe("", () => { settingsStore.gridConfig.options.startsAt.value; settingsStore.gridConfig.config.reverseSort = true; MountedFetcher(); - expect(fetchSpy).toHaveBeenCalledWith("startsAt", "", "1"); + expect(fetchSpy).toHaveBeenCalledWith("", false, "startsAt", "", "1"); }); it("calls alertStore.fetchWithThrottle with correct sort arguments when sortOrder=label sortLabel=cluster reverseSort=false", () => { @@ -198,7 +202,7 @@ describe("", () => { settingsStore.gridConfig.config.sortLabel = "cluster"; settingsStore.gridConfig.config.reverseSort = false; MountedFetcher(); - expect(fetchSpy).toHaveBeenCalledWith("label", "cluster", "0"); + expect(fetchSpy).toHaveBeenCalledWith("", false, "label", "cluster", "0"); }); it("calls alertStore.fetchWithThrottle with correct sort arguments when sortOrder=label sortLabel=job reverseSort=true", () => { @@ -209,7 +213,7 @@ describe("", () => { settingsStore.gridConfig.config.sortLabel = "job"; settingsStore.gridConfig.config.reverseSort = true; MountedFetcher(); - expect(fetchSpy).toHaveBeenCalledWith("label", "job", "1"); + expect(fetchSpy).toHaveBeenCalledWith("", false, "label", "job", "1"); }); it("calls alertStore.fetchWithThrottle with correct sort arguments when sortOrder=label sortLabel=instance reverseSort=null", () => { @@ -220,7 +224,40 @@ describe("", () => { settingsStore.gridConfig.config.sortLabel = "instance"; settingsStore.gridConfig.config.reverseSort = null; MountedFetcher(); - expect(fetchSpy).toHaveBeenCalledWith("label", "instance", ""); + expect(fetchSpy).toHaveBeenCalledWith("", false, "label", "instance", ""); + }); + + it("calls alertStore.fetchWithThrottle with gridLabel=cluster gridSortReverse=false", () => { + MockEmptyAPIResponseWithoutFilters(); + const fetchSpy = jest.spyOn(alertStore, "fetchWithThrottle"); + settingsStore.gridConfig.config.sortOrder = + settingsStore.gridConfig.options.default.value; + settingsStore.multiGridConfig.config.gridLabel = "cluster"; + settingsStore.multiGridConfig.config.gridSortReverse = false; + MountedFetcher(); + expect(fetchSpy).toHaveBeenCalledWith("cluster", false, "", "", ""); + }); + + it("calls alertStore.fetchWithThrottle with gridLabel=cluster gridSortReverse=true", () => { + MockEmptyAPIResponseWithoutFilters(); + const fetchSpy = jest.spyOn(alertStore, "fetchWithThrottle"); + settingsStore.gridConfig.config.sortOrder = + settingsStore.gridConfig.options.default.value; + settingsStore.multiGridConfig.config.gridLabel = "cluster"; + settingsStore.multiGridConfig.config.gridSortReverse = true; + MountedFetcher(); + expect(fetchSpy).toHaveBeenCalledWith("cluster", true, "", "", ""); + }); + + it("calls alertStore.fetchWithThrottle with gridLabel= gridSortReverse=true", () => { + MockEmptyAPIResponseWithoutFilters(); + const fetchSpy = jest.spyOn(alertStore, "fetchWithThrottle"); + settingsStore.gridConfig.config.sortOrder = + settingsStore.gridConfig.options.default.value; + settingsStore.multiGridConfig.config.gridLabel = ""; + settingsStore.multiGridConfig.config.gridSortReverse = true; + MountedFetcher(); + expect(fetchSpy).toHaveBeenCalledWith("", true, "", "", ""); }); it("internal timer is armed after render", () => { diff --git a/ui/src/Stores/AlertStore.js b/ui/src/Stores/AlertStore.js index 3eec57bbf..3fe084904 100644 --- a/ui/src/Stores/AlertStore.js +++ b/ui/src/Stores/AlertStore.js @@ -321,37 +321,46 @@ class AlertStore { this.filters.setFilters(initialFilters); } - fetch = action((sortOrder, sortLabel, sortReverse) => { - this.status.setFetching(); + fetch = action( + (gridLabel, gridSortReverse, sortOrder, sortLabel, sortReverse) => { + this.status.setFetching(); - const alertsURI = - FormatBackendURI( - `alerts.json?sortOrder=${sortOrder}&sortLabel=${sortLabel}&sortReverse=${sortReverse}&gridLabel=severity&` - ) + FormatAPIFilterQuery(this.filters.values.map((f) => f.raw)); + const args = [ + `gridLabel=${gridLabel}`, + `gridSortReverse=${gridSortReverse ? "1" : "0"}`, + `sortOrder=${sortOrder}`, + `sortLabel=${sortLabel}`, + `sortReverse=${sortReverse}`, + ]; - return FetchGet(alertsURI, {}, this.info.setIsRetrying) - .then((result) => { - // we're sending requests with mode=cors so the response should also be type=cors - // after a few failures in the retry loop we will switch to no-cors - // if that request comes back as type=opaque then we might be getting - // redirected by an auth proxy - if (result.type === "opaque") { - this.info.setReloadNeeded(); - } - this.info.clearIsRetrying(); - this.status.setProcessing(); - return result.json(); - }) - .then((result) => { - return this.parseAPIResponse(result); - }) - .catch((err) => { - console.trace(err); - return this.handleFetchError( - `Can't connect to the API, last error was "${err.message}"` - ); - }); - }); + const alertsURI = + FormatBackendURI(`alerts.json?&${args.join("&")}&`) + + FormatAPIFilterQuery(this.filters.values.map((f) => f.raw)); + + return FetchGet(alertsURI, {}, this.info.setIsRetrying) + .then((result) => { + // we're sending requests with mode=cors so the response should also be type=cors + // after a few failures in the retry loop we will switch to no-cors + // if that request comes back as type=opaque then we might be getting + // redirected by an auth proxy + if (result.type === "opaque") { + this.info.setReloadNeeded(); + } + this.info.clearIsRetrying(); + this.status.setProcessing(); + return result.json(); + }) + .then((result) => { + return this.parseAPIResponse(result); + }) + .catch((err) => { + console.trace(err); + return this.handleFetchError( + `Can't connect to the API, last error was "${err.message}"` + ); + }); + } + ); fetchWithThrottle = throttle(this.fetch, 300); diff --git a/ui/src/Stores/AlertStore.test.js b/ui/src/Stores/AlertStore.test.js index 8b595fb00..1a951fb1a 100644 --- a/ui/src/Stores/AlertStore.test.js +++ b/ui/src/Stores/AlertStore.test.js @@ -599,4 +599,30 @@ describe("AlertStore.fetch", () => { bar: "bar", }); }); + + it("uses correct query args with gridSortReverse=false", async () => { + const response = EmptyAPIResponse(); + fetch.mockResponse(JSON.stringify(response)); + const store = new AlertStore(["label=value"]); + await expect( + store.fetch("", false, "sortOrder", "sortLabel", "sortReverse") + ).resolves.toBeUndefined(); + expect(fetch.mock.calls.length).toEqual(1); + expect(fetch.mock.calls[0][0]).toBe( + "./alerts.json?&gridLabel=&gridSortReverse=0&sortOrder=sortOrder&sortLabel=sortLabel&sortReverse=sortReverse&q=label%3Dvalue" + ); + }); + + it("uses correct query args with gridSortReverse=true", async () => { + const response = EmptyAPIResponse(); + fetch.mockResponse(JSON.stringify(response)); + const store = new AlertStore(["label=value"]); + await expect( + store.fetch("cluster", true, "sortOrder", "sortLabel", "sortReverse") + ).resolves.toBeUndefined(); + expect(fetch.mock.calls.length).toEqual(1); + expect(fetch.mock.calls[0][0]).toBe( + "./alerts.json?&gridLabel=cluster&gridSortReverse=1&sortOrder=sortOrder&sortLabel=sortLabel&sortReverse=sortReverse&q=label%3Dvalue" + ); + }); }); diff --git a/ui/src/Stores/Settings.js b/ui/src/Stores/Settings.js index d12ec1033..179c020a5 100644 --- a/ui/src/Stores/Settings.js +++ b/ui/src/Stores/Settings.js @@ -132,6 +132,21 @@ class ThemeConfig { } } +class MultiGridConfig { + constructor(gridLabel, gridSortReverse) { + this.config = localStored( + "multiGridConfig", + { + gridLabel: gridLabel, + gridSortReverse: gridSortReverse, + }, + { + delay: 100, + } + ); + } +} + class Settings { constructor(defaults) { let defaultSettings; @@ -144,6 +159,8 @@ class Settings { MinimalGroupWidth: 420, AlertsPerGroup: 5, CollapseGroups: "collapsedOnMobile", + GridLabel: "", + GridSortReverse: false, }; } else { defaultSettings = defaults; @@ -164,6 +181,10 @@ class Settings { defaultSettings.HideFiltersWhenIdle ); this.themeConfig = new ThemeConfig(defaultSettings.Theme); + this.multiGridConfig = new MultiGridConfig( + defaultSettings.GridLabel, + defaultSettings.GridSortReverse + ); } }