diff --git a/ui/src/Stores/AlertStore.js b/ui/src/Stores/AlertStore.js index 7bd470fd6..f4ebb3568 100644 --- a/ui/src/Stores/AlertStore.js +++ b/ui/src/Stores/AlertStore.js @@ -285,13 +285,7 @@ class AlertStore { let updates = {}; // update data dicts if they changed - for (const key of [ - "colors", - "counters", - "groups", - "silences", - "upstreams" - ]) { + for (const key of ["colors", "counters", "silences", "upstreams"]) { if (!equal(this.data[key], result[key])) { updates[key] = result[key]; } @@ -300,6 +294,27 @@ class AlertStore { this.data = Object.assign(this.data, updates); } + // update groups, it can be huge so we have custom logic with cheaper + // comparision logic running per group using content hashes from the API + // response + for (const key of Object.keys(result.groups)) { + // set/update each group if: + // * it's not yet stored in AlertStore + // * it's stored but hash is different than in the API response + if ( + !(key in this.data.groups) || + (key in this.data.groups && + result.groups[key].hash !== this.data.groups[key].hash) + ) { + this.data.groups[key] = result.groups[key]; + } + } + for (const key of Object.keys(this.data.groups).filter( + k => !(k in result.groups) + )) { + delete this.data.groups[key]; + } + // before storing new version check if we need to reload if ( this.info.version !== "unknown" && diff --git a/ui/src/Stores/AlertStore.test.js b/ui/src/Stores/AlertStore.test.js index a9df5f260..2d692c927 100644 --- a/ui/src/Stores/AlertStore.test.js +++ b/ui/src/Stores/AlertStore.test.js @@ -333,4 +333,56 @@ describe("AlertStore.fetch", () => { await expect(store.fetch()).resolves.toBeUndefined(); expect(store.info.upgradeNeeded).toBe(true); }); + + it("adds new groups to the store after fetch", () => { + const response = EmptyAPIResponse(); + response.groups = { foo: "foo", bar: "bar" }; + const store = new AlertStore(["label=value"]); + store.parseAPIResponse(response); + expect(Object.keys(store.data.groups)).toHaveLength(2); + expect(store.data.groups).toMatchObject({ foo: "foo", bar: "bar" }); + }); + + it("is no-op for groups that didn't change", () => { + const store = new AlertStore(["label=value"]); + store.data.groups = { foo: { hash: "foo" }, bar: { hash: "bar" } }; + + const response = EmptyAPIResponse(); + response.groups = { foo: { hash: "foo" }, bar: { hash: "bar" } }; + + store.parseAPIResponse(response); + expect(Object.keys(store.data.groups)).toHaveLength(2); + expect(store.data.groups).toMatchObject({ + foo: { hash: "foo" }, + bar: { hash: "bar" } + }); + }); + + it("removes old groups from the store after fetch", () => { + const store = new AlertStore(["label=value"]); + store.data.groups = { foo: "foo", delete: "me", bar: "bar" }; + expect(Object.keys(store.data.groups)).toHaveLength(3); + + const response = EmptyAPIResponse(); + response.groups = { foo: "foo", bar: "bar" }; + + store.parseAPIResponse(response); + expect(Object.keys(store.data.groups)).toHaveLength(2); + expect(store.data.groups).toMatchObject({ foo: "foo", bar: "bar" }); + }); + + it("updates groups with new hash after fetch", () => { + const store = new AlertStore(["label=value"]); + store.data.groups = { foo: { hash: "foo" }, bar: { hash: "bar" } }; + + const response = EmptyAPIResponse(); + response.groups = { foo: { hash: "newFoo" }, bar: { hash: "newBar" } }; + + store.parseAPIResponse(response); + expect(Object.keys(store.data.groups)).toHaveLength(2); + expect(store.data.groups).toMatchObject({ + foo: { hash: "newFoo" }, + bar: { hash: "newBar" } + }); + }); });