diff --git a/ui/src/Components/Fetcher/index.js b/ui/src/Components/Fetcher/index.js
index e20f0332a..1361825a5 100644
--- a/ui/src/Components/Fetcher/index.js
+++ b/ui/src/Components/Fetcher/index.js
@@ -1,10 +1,12 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
-import { toJS } from "mobx";
+import { observable, action } from "mobx";
import { observer } from "mobx-react";
-import { AlertStore } from "Stores/AlertStore";
+import moment from "moment";
+
+import { AlertStore, AlertStoreStatuses } from "Stores/AlertStore";
import { Settings } from "Stores/Settings";
const Fetcher = observer(
@@ -14,40 +16,53 @@ const Fetcher = observer(
settingsStore: PropTypes.instanceOf(Settings).isRequired
};
- timer = null;
+ lastTick = observable(
+ {
+ time: moment(0),
+ update() {
+ this.time = moment();
+ }
+ },
+ {
+ update: action
+ }
+ );
- interval = null;
-
- setTimer() {
+ fetchIfIdle = () => {
const { alertStore, settingsStore } = this.props;
- const newInterval = toJS(settingsStore.fetchConfig.config.interval);
+ const nextTick = moment(this.lastTick.time).add(
+ settingsStore.fetchConfig.config.interval,
+ "seconds"
+ );
- if (this.interval !== newInterval) {
- if (this.timer !== null) clearInterval(this.timer);
+ const pastDeadline = moment().isSameOrAfter(nextTick);
- this.interval = newInterval;
- this.timer = setInterval(
- () => alertStore.fetchWithThrottle(),
- this.interval * 1000
- );
+ const status = alertStore.status.value.toString();
+ const updateInProgress =
+ status === AlertStoreStatuses.Fetching.toString() ||
+ status === AlertStoreStatuses.Processing.toString();
+
+ if (pastDeadline && !updateInProgress) {
+ this.lastTick.update();
+ alertStore.fetchWithThrottle();
}
+ };
+
+ timerTick = () => {
+ this.fetchIfIdle();
+ };
+
+ componentDidMount() {
+ this.fetchIfIdle();
+ this.timer = setInterval(this.timerTick, 1000);
}
componentDidUpdate() {
const { alertStore } = this.props;
+ this.lastTick.update();
alertStore.fetchWithThrottle();
-
- this.setTimer();
- }
-
- componentDidMount() {
- const { alertStore } = this.props;
-
- alertStore.fetchWithThrottle();
-
- this.setTimer();
}
componentWillUnmount() {
diff --git a/ui/src/Components/Fetcher/index.test.js b/ui/src/Components/Fetcher/index.test.js
index b10db3132..2f4679602 100644
--- a/ui/src/Components/Fetcher/index.test.js
+++ b/ui/src/Components/Fetcher/index.test.js
@@ -2,6 +2,8 @@ import React from "react";
import { mount } from "enzyme";
+import { advanceTo, advanceBy, clear } from "jest-date-mock";
+
import { EmptyAPIResponse } from "__mocks__/Fetch";
import { AlertStore } from "Stores/AlertStore";
@@ -9,24 +11,30 @@ import { Settings } from "Stores/Settings";
import { Fetcher } from ".";
+let alertStore;
+let settingsStore;
+let fetchSpy;
+
beforeAll(() => {
jest.useFakeTimers();
});
-let alertStore;
-let settingsStore;
-
beforeEach(() => {
- fetch.mockResponse(JSON.stringify(EmptyAPIResponse()));
+ advanceTo(new Date(2000, 1, 1, 0, 0, 0));
alertStore = new AlertStore(["label=value"]);
+ fetchSpy = jest
+ .spyOn(alertStore, "fetchWithThrottle")
+ .mockImplementation(() => {});
+
settingsStore = new Settings();
+ settingsStore.fetchConfig.config.interval = 30;
});
afterEach(() => {
jest.clearAllTimers();
-
- global.fetch.mockRestore();
+ jest.clearAllMocks();
+ clear();
});
const MockEmptyAPIResponseWithoutFilters = () => {
@@ -57,6 +65,30 @@ describe("", () => {
expect(tree.html()).toBe(FetcherSpan("label=value", 60));
});
+ it("changing interval changes how often fetch is called", () => {
+ settingsStore.fetchConfig.config.interval = 1;
+ MountedFetcher();
+ expect(fetchSpy).toHaveBeenCalledTimes(1);
+
+ settingsStore.fetchConfig.config.interval = 600;
+
+ advanceBy(3 * 1000);
+ jest.runOnlyPendingTimers();
+ expect(fetchSpy).toHaveBeenCalledTimes(2);
+
+ advanceBy(32 * 1000);
+ jest.runOnlyPendingTimers();
+ expect(fetchSpy).toHaveBeenCalledTimes(2);
+
+ advanceBy(62 * 1000);
+ jest.runOnlyPendingTimers();
+ expect(fetchSpy).toHaveBeenCalledTimes(2);
+
+ advanceBy(602 * 1000);
+ jest.runOnlyPendingTimers();
+ expect(fetchSpy).toHaveBeenCalledTimes(3);
+ });
+
it("re-renders on filters change", () => {
MockEmptyAPIResponseWithoutFilters();
const tree = MountedFetcher();
@@ -86,13 +118,19 @@ describe("", () => {
expect(fetchSpy).toHaveBeenCalledTimes(2);
});
- it("keeps calling alertStore.fetchWithThrottle after running pending timers", () => {
- const fetchSpy = jest.spyOn(alertStore, "fetchWithThrottle");
+ it("keeps calling alertStore.fetchWithThrottle every minute", () => {
MountedFetcher();
+ expect(fetchSpy).toHaveBeenCalledTimes(1);
+
+ advanceBy(62 * 1000);
jest.runOnlyPendingTimers();
expect(fetchSpy).toHaveBeenCalledTimes(2);
+
+ advanceBy(62 * 1000);
jest.runOnlyPendingTimers();
expect(fetchSpy).toHaveBeenCalledTimes(3);
+
+ advanceBy(62 * 1000);
jest.runOnlyPendingTimers();
expect(fetchSpy).toHaveBeenCalledTimes(4);
});