From 9fa03c7ea199f7bad1131eaffcd3415c208b6b55 Mon Sep 17 00:00:00 2001 From: Josephine Lee Date: Thu, 27 Jan 2022 10:53:43 -0500 Subject: [PATCH] ui: Rename variables and add comments Finishes addressing #75579 This commit adds comments and renames various variables to better reflect their meaning and usage. https://github.com/cockroachlabs/managed-service/pull/7998 also renames `windowEnd` -> `fixedWindowEnd` in the managed-service repo. The following renames were made: - `scaleChanged` -> `shouldUpdateMetricsWindowFromScale` - `useTimeRange` -> `isFixedWindow` - `SET_WINDOW` -> `SET_METRICS_MOVING_WINDOW`, `setTimeWindow` -> `setMetricsMovingWindow` - `SET_RANGE` -> `SET_METRICS_FIXED_WINDOW`, `setTimeRange` -> `setMetricsFixedWindow` - `windowEnd` -> `fixedWindowEnd`, with its type changed from `moment.Moment | null` to `moment.Moment | false`. `false` indicates that the end time is a dynamically moving "now" - `db-console/src/redux/timewindow.ts` -> `db-console/src/redux/timeScale.ts `, same for the sibling test file, `TimeWindowState` -> `TimeScaleState`, `timeWindowReducer` -> `timeScaleReducer`. To make it clear that the `scale` property is the state to use, and that `TimeWindow` is mostly just a type interface (except for the db console metrics page polling usage) - `TimeWindowManager` -> `MetricsTimeManager`, `db-console/src/views/app/containers/timewindow/index.tsx` -> `db-console/src/views/app/containers/metricsTimeManager/index.tsx`, and the sibling test file. To distinguish from the `TimeWindow` type interface, name it for the main manager component in the file, and make it clear that this is just for the metrics page - `db-console/src/views/cluster/containers/timescale/index.tsx` -> `db-console/src/views/cluster/containers/timeScaleDropdownWithSearchParams/index.tsx`, and the sibling `.styl` file Release note: None --- .../statementDetails.fixture.ts | 2 +- .../statementsPage/statementsPage.fixture.ts | 2 +- .../timeScaleDropdown/timeScaleDropdown.tsx | 36 +++--- .../src/timeScaleDropdown/timeScaleTypes.ts | 28 +++-- .../src/timeScaleDropdown/timescale.spec.tsx | 49 ++++++-- .../cluster-ui/src/timeScaleDropdown/utils.ts | 20 ++-- .../transactionDetails.fixture.ts | 2 +- .../transactionsPage/transactions.fixture.ts | 2 +- .../views/clusterviz/containers/map/index.tsx | 2 +- .../workspaces/db-console/src/redux/state.ts | 6 +- .../src/redux/statements/statementsActions.ts | 2 +- .../{timewindow.spec.ts => timeScale.spec.ts} | 52 ++++---- .../src/redux/{timewindow.ts => timeScale.ts} | 111 +++++++++++------- .../src/views/app/containers/layout/index.tsx | 2 +- .../index.tsx | 80 +++++++------ .../metricsTimeManager.spec.tsx} | 18 +-- .../cluster/components/linegraph/index.tsx | 6 +- .../components/linegraph/linegraph.spec.tsx | 4 +- .../cluster/containers/nodeGraphs/index.tsx | 12 +- .../index.tsx | 27 +++-- .../timeScaleDropdownWithSearchParams.styl} | 0 .../src/views/cluster/util/graphs.ts | 4 +- .../containers/raftMessages/index.tsx | 2 +- .../reports/containers/customChart/index.tsx | 12 +- .../shared/components/metricQuery/index.tsx | 4 +- .../containers/metricDataProvider/index.tsx | 6 +- 26 files changed, 284 insertions(+), 207 deletions(-) rename pkg/ui/workspaces/db-console/src/redux/{timewindow.spec.ts => timeScale.spec.ts} (60%) rename pkg/ui/workspaces/db-console/src/redux/{timewindow.ts => timeScale.ts} (66%) rename pkg/ui/workspaces/db-console/src/views/app/containers/{timewindow => metricsTimeManager}/index.tsx (52%) rename pkg/ui/workspaces/db-console/src/views/app/containers/{timewindow/timewindow.spec.tsx => metricsTimeManager/metricsTimeManager.spec.tsx} (90%) rename pkg/ui/workspaces/db-console/src/views/cluster/containers/{timescale => timeScaleDropdownWithSearchParams}/index.tsx (79%) rename pkg/ui/workspaces/db-console/src/views/cluster/containers/{timescale/timescale.styl => timeScaleDropdownWithSearchParams/timeScaleDropdownWithSearchParams.styl} (100%) diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.fixture.ts b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.fixture.ts index e4b28e4dcd62..8efcc957c6ad 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.fixture.ts +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.fixture.ts @@ -146,7 +146,7 @@ export const getStatementDetailsPropsFixture = (): StatementDetailsProps => ({ timeScale: { windowSize: moment.duration(5, "day"), sampleSize: moment.duration(5, "minutes"), - windowEnd: moment.utc("2021.12.12"), + fixedWindowEnd: moment.utc("2021.12.12"), key: "Custom", }, statement: { diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.fixture.ts b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.fixture.ts index a1abf20fff9d..5ae886f7965c 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.fixture.ts +++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.fixture.ts @@ -786,7 +786,7 @@ const statementsPagePropsFixture: StatementsPageProps = { timeScale: { windowSize: moment.duration(5, "day"), sampleSize: moment.duration(5, "minutes"), - windowEnd: moment.utc("2021.12.12"), + fixedWindowEnd: moment.utc("2021.12.12"), key: "Custom" }, apps: ["$ internal", "movr", "$ cockroach demo"], diff --git a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleDropdown.tsx b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleDropdown.tsx index 576268f9f9f5..f767d807243f 100644 --- a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleDropdown.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleDropdown.tsx @@ -124,8 +124,8 @@ export const TimeScaleDropdown: React.FC = ({ setTimeScale, adjustTimeScaleOnChange, }): React.ReactElement => { - const end = currentScale.windowEnd - ? moment.utc(currentScale.windowEnd) + const end = currentScale.fixedWindowEnd + ? moment.utc(currentScale.fixedWindowEnd) : moment().utc(); const currentWindow: TimeWindow = { start: moment.utc(end).subtract(currentScale.windowSize), @@ -136,7 +136,7 @@ export const TimeScaleDropdown: React.FC = ({ let timeScale: TimeScale = { ...options[rangeOption.label], key: rangeOption.label, - windowEnd: null, + fixedWindowEnd: false, }; if (adjustTimeScaleOnChange) { const timeWindow: TimeWindow = { @@ -156,37 +156,43 @@ export const TimeScaleDropdown: React.FC = ({ const seconds = windowSize.asSeconds(); let selected = {}; let key = currentScale.key; - let windowEnd = moment.utc(currentWindow.end); + let endTime = moment.utc(currentWindow.end); switch (direction) { case ArrowDirection.RIGHT: - if (windowEnd) { - windowEnd = windowEnd.add(seconds, "seconds"); - } + endTime = endTime.add(seconds, "seconds"); break; case ArrowDirection.LEFT: - windowEnd = windowEnd.subtract(seconds, "seconds"); + endTime = endTime.subtract(seconds, "seconds"); break; case ArrowDirection.CENTER: // CENTER is used to set the time window to the current time. - windowEnd = moment.utc(); + endTime = moment.utc(); break; default: console.error("Unknown direction: ", direction); } + // If the timescale extends into the future then fallback to a default // timescale. Otherwise set the key to "Custom" so it appears correctly. - if ( - !windowEnd || - windowEnd > moment.utc().subtract(currentScale.windowValid) - ) { + // The first `!endTime` part of the if clause seems unnecessary since endTime is always a specific time. + // If endTime + windowValid > now. Unclear why this uses windowValid instead of windowSize. + if (!endTime || endTime > moment.utc().subtract(currentScale.windowValid)) { const foundTimeScale = Object.entries(options).find( // eslint-disable-next-line @typescript-eslint/no-unused-vars ([_, value]) => value.windowSize.asSeconds() === windowSize.asSeconds(), ); if (foundTimeScale) { + /** + * This code can be hit by: + * - Select a default option, then click the left arrow, then click the right arrow. + * This (or the parent if block) is *not* hit by: + * - Select a default time, click left, select a custom time of the same range, then click right. The arrow is + * not disabled, but the clause doesn't seem to be true. + */ selected = { key: foundTimeScale[0], ...foundTimeScale[1] }; } else { + // This code might not be possible to hit, due to the right arrow being disabled key = "Custom"; } } else { @@ -195,7 +201,7 @@ export const TimeScaleDropdown: React.FC = ({ let timeScale: TimeScale = { ...currentScale, - windowEnd, + fixedWindowEnd: endTime, windowSize, key, ...selected, @@ -225,7 +231,7 @@ export const TimeScaleDropdown: React.FC = ({ let timeScale: TimeScale = { ...findClosestTimeScale(options, seconds), windowSize: moment.duration(end.diff(start)), - windowEnd: end, + fixedWindowEnd: end, key: "Custom", }; if (adjustTimeScaleOnChange) { diff --git a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleTypes.ts b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleTypes.ts index 75af470db504..f26c0bf72fb9 100644 --- a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleTypes.ts +++ b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleTypes.ts @@ -21,38 +21,42 @@ export interface TimeWindow { } /** - * TimeScale describes the requested dimensions of TimeWindows; it - * prescribes a length for the window, along with a period of time that a - * newly created TimeWindow will remain valid. + * TimeScale describes the requested dimensions, from which one can derive concrete TimeWindows using toDateRange. */ export interface TimeScale { - // The key used to index in to the availableTimeScales collection. + /** + * The key used to index in to the defaultTimeScaleOptions collection. + * The key is "Custom" when specifying a custom time that is not one of the default options + */ key?: string; // The size of a global time window. Default is ten minutes. windowSize: moment.Duration; - // The length of time the global time window is valid. The current time window - // is invalid if now > (currentWindow.end + windowValid). Default is ten - // seconds. If windowEnd is set this is ignored. + // The length of time the global time window is valid. Default is ten seconds. windowValid?: moment.Duration; // The expected duration of individual samples for queries at this time scale. sampleSize: moment.Duration; - // The end time of the window, or null if it should be a dynamically moving "now". - windowEnd: moment.Moment | null; + /** + * The fixed end time of the window, or false if it should be a dynamically moving "now". + * Typically, when the `key` property is a default option, `fixedWindowEnd` should be false. + * And when the `key` property is "Custom" `fixedWindowEnd` should be a specific Moment. + * It is unclear if there are legitimate reasons for the two being out of sync. + */ + fixedWindowEnd: moment.Moment | false; } -export class TimeWindowState { +export class TimeScaleState { // Currently selected scale. scale: TimeScale; constructor() { this.scale = { ...defaultTimeScaleOptions["Past 10 Minutes"], - windowEnd: null, + fixedWindowEnd: false, key: "Past 10 Minutes", }; } } -export type TimeScaleOption = Omit; +export type TimeScaleOption = Omit; export interface TimeScaleOptions { [key: string]: TimeScaleOption; diff --git a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timescale.spec.tsx b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timescale.spec.tsx index ff9aef9d5464..a6f517de3590 100644 --- a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timescale.spec.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timescale.spec.tsx @@ -19,7 +19,7 @@ import { TimeScaleDropdown, } from "./timeScaleDropdown"; import { defaultTimeScaleOptions, findClosestTimeScale } from "./utils"; -import * as timewindow from "./timeScaleTypes"; +import * as timescale from "./timeScaleTypes"; import moment from "moment"; import { MemoryRouter } from "react-router"; import TimeFrameControls from "./timeFrameControls"; @@ -51,7 +51,7 @@ describe("", function() { let currentWindow: TimeWindow; const setCurrentWindowFromTimeScale = (timeScale: TimeScale): void => { - const end = timeScale.windowEnd || moment.utc(); + const end = timeScale.fixedWindowEnd || moment.utc(); currentWindow = { start: moment(end).subtract(timeScale.windowSize), end, @@ -69,10 +69,10 @@ describe("", function() { beforeEach(() => { clock = sinon.useFakeTimers(new Date(2020, 5, 1, 9, 28, 30)); - const timewindowState = new timewindow.TimeWindowState(); - setCurrentWindowFromTimeScale(timewindowState.scale); + const timeScaleState = new timescale.TimeScaleState(); + setCurrentWindowFromTimeScale(timeScaleState.scale); state = { - currentScale: timewindowState.scale, + currentScale: timeScaleState.scale, setTimeScale: () => {}, }; }); @@ -93,7 +93,7 @@ describe("", function() { const expected: TimeScale = { key: "Past 10 Minutes", ...defaultTimeScaleOptions["Past 10 Minutes"], - windowEnd: null, + fixedWindowEnd: false, }; assert.deepEqual(wrapper.props().currentScale, expected); }); @@ -143,7 +143,7 @@ describe("", function() { }; const currentScale = { ...state.currentScale, - windowEnd: window.end, + fixedWindowEnd: window.end, windowSize: moment.duration( window.end.diff(window.start, "seconds"), "seconds", @@ -191,7 +191,7 @@ describe("", function() { }; const currentTimeScale = { ...state.currentScale, - windowEnd: window.end, + fixedWindowEnd: window.end, }; const arrows = generateDisabledArrows(window); const wrapper = makeTimeScaleDropdown({ @@ -205,11 +205,21 @@ describe("", function() { describe("timescale utils", (): void => { describe("findClosestTimeScale", () => { - it("should found correctly time scale", () => { + it("should find the correct time scale", () => { + // `seconds` != window size of any of the default options, `startSeconds` not specified. assert.deepEqual(findClosestTimeScale(defaultTimeScaleOptions, 15), { ...defaultTimeScaleOptions["Past 10 Minutes"], key: "Custom", }); + // `seconds` != window size of any of the default options, `startSeconds` not specified. + assert.deepEqual( + findClosestTimeScale( + defaultTimeScaleOptions, + moment.duration(moment().daysInMonth() * 5, "days").asSeconds(), + ), + { ...defaultTimeScaleOptions["Past 2 Months"], key: "Custom" }, + ); + // `seconds` == window size of one of the default options, `startSeconds` not specified. assert.deepEqual( findClosestTimeScale( defaultTimeScaleOptions, @@ -220,6 +230,7 @@ describe("timescale utils", (): void => { key: "Past 10 Minutes", }, ); + // `seconds` == window size of one of the default options, `startSeconds` not specified. assert.deepEqual( findClosestTimeScale( defaultTimeScaleOptions, @@ -230,12 +241,28 @@ describe("timescale utils", (): void => { key: "Past 2 Weeks", }, ); + // `seconds` == window size of one of the default options, `startSeconds` is now. assert.deepEqual( findClosestTimeScale( defaultTimeScaleOptions, - moment.duration(moment().daysInMonth() * 5, "days").asSeconds(), + defaultTimeScaleOptions["Past 10 Minutes"].windowSize.asSeconds(), + moment().unix(), ), - { ...defaultTimeScaleOptions["Past 2 Months"], key: "Custom" }, + { + ...defaultTimeScaleOptions["Past 10 Minutes"], + key: "Past 10 Minutes", + }, + ); + // `seconds` == window size of one of the default options, `startSeconds` is in the past. + assert.deepEqual( + findClosestTimeScale( + defaultTimeScaleOptions, + defaultTimeScaleOptions["Past 10 Minutes"].windowSize.asSeconds(), + moment() + .subtract(1, "day") + .unix(), + ), + { ...defaultTimeScaleOptions["Past 10 Minutes"], key: "Custom" }, ); }); }); diff --git a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts index 54786eb653a0..a3255b266db1 100644 --- a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts +++ b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts @@ -76,11 +76,13 @@ export const defaultTimeScaleOptions: TimeScaleOptions = { export const defaultTimeScaleSelected: TimeScale = { ...defaultTimeScaleOptions["Past 1 Hour"], key: "Past 1 Hour", - windowEnd: null, + fixedWindowEnd: false, }; export const toDateRange = (ts: TimeScale): [moment.Moment, moment.Moment] => { - const end = ts.windowEnd ? moment.utc(ts.windowEnd) : moment().utc(); + const end = ts.fixedWindowEnd + ? moment.utc(ts.fixedWindowEnd) + : moment().utc(); const start = moment.utc(end).subtract(ts.windowSize); return [start, end]; }; @@ -100,22 +102,22 @@ export const findClosestTimeScale = ( Math.abs(seconds - b.windowSize.asSeconds()), ); - const firstTimeScaleOptionSeconds = data[0].windowSize.asSeconds(); + const closestWindowSizeSeconds = data[0].windowSize.asSeconds(); // This logic covers the edge case where drag-to-timerange on a linegraph is of a duration // that exactly matches one of the standard available time scales e.g. selecting June 1 at // 0:00 to June 2 at 0:00 when the date is July 1 at 0:00 should return a custom timescale // instead of past day. - if (startSeconds && firstTimeScaleOptionSeconds === seconds) { - const startWindow = moment() - .subtract(firstTimeScaleOptionSeconds, "seconds") - .unix(); - if (startSeconds < startWindow) { + // If the start is specified, and the window size matches. + if (startSeconds && closestWindowSizeSeconds === seconds) { + // Check if the end is before now. If so, it is a custom time. + const end = moment.unix(startSeconds + seconds); + if (end < moment()) { return { ...data[0], key: "Custom" }; } } - return firstTimeScaleOptionSeconds === seconds + return closestWindowSizeSeconds === seconds ? data[0] : { ...data[0], key: "Custom" }; }; diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.fixture.ts b/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.fixture.ts index 11bdfb825344..e1c99ad8a06b 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.fixture.ts +++ b/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.fixture.ts @@ -628,6 +628,6 @@ export const transactionDetails = { export const timeScale: TimeScale = { windowSize: moment.duration(1, "year"), sampleSize: moment.duration(1, "day"), - windowEnd: moment.utc("2021.12.31"), + fixedWindowEnd: moment.utc("2021.12.31"), key: "Custom", }; diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactions.fixture.ts b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactions.fixture.ts index 25ca126b0ef5..691ef58e8562 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactions.fixture.ts +++ b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactions.fixture.ts @@ -47,7 +47,7 @@ export const columns: string[] = ["all"]; export const timeScale: TimeScale = { windowSize: moment.duration(5, "day"), sampleSize: moment.duration(5, "minutes"), - windowEnd: moment.utc("2021.08.12"), + fixedWindowEnd: moment.utc("2021.08.12"), key: "Custom", }; export const timestamp = new protos.google.protobuf.Timestamp({ diff --git a/pkg/ui/workspaces/db-console/ccl/src/views/clusterviz/containers/map/index.tsx b/pkg/ui/workspaces/db-console/ccl/src/views/clusterviz/containers/map/index.tsx index ea6ffac5a88b..f8718a1795cf 100644 --- a/pkg/ui/workspaces/db-console/ccl/src/views/clusterviz/containers/map/index.tsx +++ b/pkg/ui/workspaces/db-console/ccl/src/views/clusterviz/containers/map/index.tsx @@ -14,7 +14,7 @@ import cn from "classnames"; import { Breadcrumbs } from "src/views/clusterviz/containers/map/breadcrumbs"; import NeedEnterpriseLicense from "src/views/clusterviz/containers/map/needEnterpriseLicense"; import NodeCanvasContainer from "src/views/clusterviz/containers/map/nodeCanvasContainer"; -import TimeScaleDropdown from "src/views/cluster/containers/timescale"; +import TimeScaleDropdown from "src/views/cluster/containers/timeScaleDropdownWithSearchParams"; import swapByLicense from "src/views/shared/containers/licenseSwap"; import { parseLocalityRoute } from "src/util/localities"; import { Loading } from "@cockroachlabs/cluster-ui"; diff --git a/pkg/ui/workspaces/db-console/src/redux/state.ts b/pkg/ui/workspaces/db-console/src/redux/state.ts index a82c5f9a6f10..c800f208ffd8 100644 --- a/pkg/ui/workspaces/db-console/src/redux/state.ts +++ b/pkg/ui/workspaces/db-console/src/redux/state.ts @@ -31,7 +31,7 @@ import { hoverReducer, HoverState } from "./hover"; import { localSettingsReducer, LocalSettingsState } from "./localsettings"; import { metricsReducer, MetricsState } from "./metrics"; import { queryManagerReducer, QueryManagerState } from "./queryManager/reducer"; -import { timeWindowReducer, TimeWindowState } from "./timewindow"; +import { timeScaleReducer, TimeScaleState } from "./timeScale"; import { uiDataReducer, UIDataState } from "./uiData"; import { loginReducer, LoginAPIState } from "./login"; import rootSaga from "./sagas"; @@ -43,7 +43,7 @@ export interface AdminUIState { metrics: MetricsState; queryManager: QueryManagerState; router: RouterState; - timewindow: TimeWindowState; + timeScale: TimeScaleState; uiData: UIDataState; login: LoginAPIState; } @@ -65,7 +65,7 @@ export function createAdminUIStore(historyInst: History) { metrics: metricsReducer, queryManager: queryManagerReducer, router: routerReducer, - timewindow: timeWindowReducer, + timeScale: timeScaleReducer, uiData: uiDataReducer, login: loginReducer, }), diff --git a/pkg/ui/workspaces/db-console/src/redux/statements/statementsActions.ts b/pkg/ui/workspaces/db-console/src/redux/statements/statementsActions.ts index 8347a6668ab6..db2454964daf 100644 --- a/pkg/ui/workspaces/db-console/src/redux/statements/statementsActions.ts +++ b/pkg/ui/workspaces/db-console/src/redux/statements/statementsActions.ts @@ -12,7 +12,7 @@ import { Action } from "redux"; import { PayloadAction } from "src/interfaces/action"; import { google } from "@cockroachlabs/crdb-protobuf-client"; import IDuration = google.protobuf.IDuration; -import { TimeScale } from "src/redux/timewindow"; +import { TimeScale } from "src/redux/timeScale"; export const CREATE_STATEMENT_DIAGNOSTICS_REPORT = "cockroachui/statements/CREATE_STATEMENT_DIAGNOSTICS_REPORT"; diff --git a/pkg/ui/workspaces/db-console/src/redux/timewindow.spec.ts b/pkg/ui/workspaces/db-console/src/redux/timeScale.spec.ts similarity index 60% rename from pkg/ui/workspaces/db-console/src/redux/timewindow.spec.ts rename to pkg/ui/workspaces/db-console/src/redux/timeScale.spec.ts index 905fbcbd8a7c..299ca4470da8 100644 --- a/pkg/ui/workspaces/db-console/src/redux/timewindow.spec.ts +++ b/pkg/ui/workspaces/db-console/src/redux/timeScale.spec.ts @@ -10,36 +10,36 @@ import { assert } from "chai"; import { defaultTimeScaleOptions } from "@cockroachlabs/cluster-ui"; -import * as timewindow from "./timewindow"; +import * as timeScale from "./timeScale"; import moment from "moment"; -describe("time window reducer", function() { +describe("time scale reducer", function() { describe("actions", function() { - it("should create the correct action to set the current time window", function() { + it("should create the correct SET_METRICS_MOVING_WINDOW action to set the current time window", function() { const start = moment(); const end = start.add(10, "s"); const expectedSetting = { - type: timewindow.SET_WINDOW, + type: timeScale.SET_METRICS_MOVING_WINDOW, payload: { start, end, }, }; assert.deepEqual( - timewindow.setTimeWindow({ start, end }), + timeScale.setMetricsMovingWindow({ start, end }), expectedSetting, ); }); - it("should create the correct action to set time window settings", function() { - const payload: timewindow.TimeScale = { + it("should create the correct SET_SCALE action to set time window settings", function() { + const payload: timeScale.TimeScale = { windowSize: moment.duration(10, "s"), windowValid: moment.duration(10, "s"), sampleSize: moment.duration(10, "s"), - windowEnd: null, + fixedWindowEnd: false, }; - assert.deepEqual(timewindow.setTimeScale(payload), { - type: timewindow.SET_SCALE, + assert.deepEqual(timeScale.setTimeScale(payload), { + type: timeScale.SET_SCALE, payload, }); }); @@ -48,57 +48,57 @@ describe("time window reducer", function() { describe("reducer", () => { it("should have the correct default value.", () => { assert.deepEqual( - timewindow.timeWindowReducer(undefined, { type: "unknown" }), - new timewindow.TimeWindowState(), + timeScale.timeScaleReducer(undefined, { type: "unknown" }), + new timeScale.TimeScaleState(), ); - assert.deepEqual(new timewindow.TimeWindowState().scale, { + assert.deepEqual(new timeScale.TimeScaleState().scale, { ...defaultTimeScaleOptions["Past 10 Minutes"], key: "Past 10 Minutes", - windowEnd: null, + fixedWindowEnd: false, }); }); - describe("setTimeWindow", () => { + describe("setMetricsMovingWindow", () => { const start = moment(); const end = start.add(10, "s"); it("should correctly overwrite previous value", () => { - const expected = new timewindow.TimeWindowState(); + const expected = new timeScale.TimeScaleState(); expected.metricsTime.currentWindow = { start, end, }; - expected.metricsTime.scaleChanged = false; + expected.metricsTime.shouldUpdateMetricsWindowFromScale = false; assert.deepEqual( - timewindow.timeWindowReducer( + timeScale.timeScaleReducer( undefined, - timewindow.setTimeWindow({ start, end }), + timeScale.setMetricsMovingWindow({ start, end }), ), expected, ); }); }); - describe("setTimeWindowSettings", () => { + describe("setTimeScale", () => { const newSize = moment.duration(1, "h"); const newValid = moment.duration(1, "m"); const newSample = moment.duration(1, "m"); it("should correctly overwrite previous value", () => { - const expected = new timewindow.TimeWindowState(); + const expected = new timeScale.TimeScaleState(); expected.scale = { windowSize: newSize, windowValid: newValid, sampleSize: newSample, - windowEnd: null, + fixedWindowEnd: false, }; - expected.metricsTime.scaleChanged = true; + expected.metricsTime.shouldUpdateMetricsWindowFromScale = true; assert.deepEqual( - timewindow.timeWindowReducer( + timeScale.timeScaleReducer( undefined, - timewindow.setTimeScale({ + timeScale.setTimeScale({ windowSize: newSize, windowValid: newValid, sampleSize: newSample, - windowEnd: null, + fixedWindowEnd: false, }), ), expected, diff --git a/pkg/ui/workspaces/db-console/src/redux/timewindow.ts b/pkg/ui/workspaces/db-console/src/redux/timeScale.ts similarity index 66% rename from pkg/ui/workspaces/db-console/src/redux/timewindow.ts rename to pkg/ui/workspaces/db-console/src/redux/timeScale.ts index 8c03c1d8315a..cc9e525f4e6a 100644 --- a/pkg/ui/workspaces/db-console/src/redux/timewindow.ts +++ b/pkg/ui/workspaces/db-console/src/redux/timeScale.ts @@ -19,9 +19,11 @@ import _ from "lodash"; import { defaultTimeScaleOptions } from "@cockroachlabs/cluster-ui"; import moment from "moment"; -export const SET_WINDOW = "cockroachui/timewindow/SET_WINDOW"; -export const SET_RANGE = "cockroachui/timewindow/SET_RANGE"; export const SET_SCALE = "cockroachui/timewindow/SET_SCALE"; +export const SET_METRICS_MOVING_WINDOW = + "cockroachui/timewindow/SET_METRICS_MOVING_WINDOW"; +export const SET_METRICS_FIXED_WINDOW = + "cockroachui/timewindow/SET_METRICS_FIXED_WINDOW"; /** * TimeWindow represents an absolute window of time, defined with a start and @@ -38,79 +40,96 @@ export interface TimeWindow { * newly created TimeWindow will remain valid. */ export interface TimeScale { - // The key used to index in to a TimeScaleCollection. + /** + * The key used to index in to the defaultTimeScaleOptions collection. + * The key is "Custom" when specifying a custom time that is not one of the default options + */ key?: string; // The size of a global time window. Default is ten minutes. windowSize: moment.Duration; // The length of time the global time window is valid. The current time window - // is invalid if now > (currentWindow.end + windowValid). Default is ten - // seconds. If windowEnd is set this is ignored. + // is invalid if now > (metricsTime.currentWindow.end + windowValid). Default is ten + // seconds. If fixedWindowEnd is set this is ignored. windowValid?: moment.Duration; // The expected duration of individual samples for queries at this time scale. sampleSize: moment.Duration; - // The end time of the window, or null if it should be a dynamically moving "now". - windowEnd: moment.Moment | null; + /** + * The fixed end time of the window, or false if it should be a dynamically moving "now". + * Typically, when the `key` property is a default option, `fixedWindowEnd` should be false. + * And when the `key` property is "Custom" `fixedWindowEnd` should be a specific Moment. + * It is unclear if there are legitimate reasons for the two being out of sync. + */ + fixedWindowEnd: moment.Moment | false; } -export class TimeWindowState { +export class TimeScaleState { // Currently selected scale. scale: TimeScale; - // Timekeeping for the DB Console metrics page. + /** + * Timekeeping for the db console metrics page. Due to tech debt, this duplicates part of state currently in scale. + * e.g., + * currentWindow.start can be derived from metricsTime.currentWindow.end - scale.windowSize, + * and metricsTime.isFixedWindow can be derived from scale. + * However, the key difference is that metricsTime.currentWindow.end is different from scale.fixedWindowEnd. + * Specifically, when the end is "now", scale.fixedWindowEnd is false and metricsTime.currentWindow.end is a specific + * time that is continually updated in order to do polling. + */ metricsTime: { - // Currently established time window. + // Start and end times to be used for metrics page graphs. currentWindow: TimeWindow; - // True if scale has changed since currentWindow was generated. - scaleChanged: boolean; - useTimeRange: boolean; + // True if scale has changed since currentWindow was generated, and it should be re-generated from scale. + shouldUpdateMetricsWindowFromScale: boolean; + // True if currentWindow should be unchanging. False if it should be updated with new "now" times. + isFixedWindow: boolean; }; constructor() { this.scale = { ...defaultTimeScaleOptions["Past 10 Minutes"], key: "Past 10 Minutes", - windowEnd: null, + fixedWindowEnd: false, }; this.metricsTime = { // This is explicitly initialized as undefined to match the prior implementation while satisfying Typescript. currentWindow: undefined, - useTimeRange: false, + isFixedWindow: false, // This is used to update the metrics time window after the scale is changed, and prevent cycles when directly // updating the metrics time window. - scaleChanged: false, + shouldUpdateMetricsWindowFromScale: false, }; } } -export function timeWindowReducer( - state = new TimeWindowState(), +export function timeScaleReducer( + state = new TimeScaleState(), action: Action, -): TimeWindowState { +): TimeScaleState { switch (action.type) { - case SET_WINDOW: { + case SET_SCALE: { + const { payload: scale } = action as PayloadAction; + state = _.cloneDeep(state); + if (scale.key === "Custom") { + state.metricsTime.isFixedWindow = true; + } else { + state.metricsTime.isFixedWindow = false; + } + state.scale = scale; + state.metricsTime.shouldUpdateMetricsWindowFromScale = true; + return state; + } + case SET_METRICS_MOVING_WINDOW: { const { payload: tw } = action as PayloadAction; state = _.cloneDeep(state); state.metricsTime.currentWindow = tw; - state.metricsTime.scaleChanged = false; + state.metricsTime.shouldUpdateMetricsWindowFromScale = false; return state; } - case SET_RANGE: { + case SET_METRICS_FIXED_WINDOW: { const { payload: data } = action as PayloadAction; state = _.cloneDeep(state); state.metricsTime.currentWindow = data; - state.metricsTime.useTimeRange = true; - state.metricsTime.scaleChanged = false; - return state; - } - case SET_SCALE: { - const { payload: scale } = action as PayloadAction; - state = _.cloneDeep(state); - if (scale.key === "Custom") { - state.metricsTime.useTimeRange = true; - } else { - state.metricsTime.useTimeRange = false; - } - state.scale = scale; - state.metricsTime.scaleChanged = true; + state.metricsTime.isFixedWindow = true; + state.metricsTime.shouldUpdateMetricsWindowFromScale = false; return state; } default: @@ -118,24 +137,28 @@ export function timeWindowReducer( } } -export function setTimeWindow(tw: TimeWindow): PayloadAction { +export function setTimeScale(ts: TimeScale): PayloadAction { return { - type: SET_WINDOW, - payload: tw, + type: SET_SCALE, + payload: ts, }; } -export function setTimeRange(tw: TimeWindow): PayloadAction { +export function setMetricsMovingWindow( + tw: TimeWindow, +): PayloadAction { return { - type: SET_RANGE, + type: SET_METRICS_MOVING_WINDOW, payload: tw, }; } -export function setTimeScale(ts: TimeScale): PayloadAction { +export function setMetricsFixedWindow( + tw: TimeWindow, +): PayloadAction { return { - type: SET_SCALE, - payload: ts, + type: SET_METRICS_FIXED_WINDOW, + payload: tw, }; } diff --git a/pkg/ui/workspaces/db-console/src/views/app/containers/layout/index.tsx b/pkg/ui/workspaces/db-console/src/views/app/containers/layout/index.tsx index 7cf797c863f6..d86430af15e5 100644 --- a/pkg/ui/workspaces/db-console/src/views/app/containers/layout/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/app/containers/layout/index.tsx @@ -15,7 +15,7 @@ import { connect } from "react-redux"; import NavigationBar from "src/views/app/components/layoutSidebar"; import ErrorBoundary from "src/views/app/components/errorMessage/errorBoundary"; -import TimeWindowManager from "src/views/app/containers/timewindow"; +import TimeWindowManager from "src/views/app/containers/metricsTimeManager"; import AlertBanner from "src/views/app/containers/alertBanner"; import RequireLogin from "src/views/login/requireLogin"; import { diff --git a/pkg/ui/workspaces/db-console/src/views/app/containers/timewindow/index.tsx b/pkg/ui/workspaces/db-console/src/views/app/containers/metricsTimeManager/index.tsx similarity index 52% rename from pkg/ui/workspaces/db-console/src/views/app/containers/timewindow/index.tsx rename to pkg/ui/workspaces/db-console/src/views/app/containers/metricsTimeManager/index.tsx index 37a1a312c7cf..bd7b957eb1a7 100644 --- a/pkg/ui/workspaces/db-console/src/views/app/containers/timewindow/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/app/containers/metricsTimeManager/index.tsx @@ -13,45 +13,45 @@ import { connect } from "react-redux"; import moment from "moment"; import { AdminUIState } from "src/redux/state"; -import * as timewindow from "src/redux/timewindow"; +import * as timewindow from "src/redux/timeScale"; import _ from "lodash"; -interface TimeWindowManagerProps { - // The current timewindow redux state. - timeWindow: timewindow.TimeWindowState; +interface MetricsTimeManagerProps { + // The current timescale redux state. + timeScale: timewindow.TimeScaleState; // Callback function used to set a new time window. - setTimeWindow: typeof timewindow.setTimeWindow; + setMetricsMovingWindow: typeof timewindow.setMetricsMovingWindow; // Optional override method to obtain the current time. Used for tests. now?: () => moment.Moment; } -interface TimeWindowManagerState { +interface MetricsTimeManagerState { // Identifier from an outstanding call to setTimeout. timeout: number; } /** - * TimeWindowManager takes responsibility for advancing the current global + * MetricsTimeManager takes responsibility for advancing the current global * time window used by metric graphs. It renders nothing, but will dispatch an * updated time window into the redux store whenever the previous time window is * expired. */ -class TimeWindowManager extends React.Component< - TimeWindowManagerProps, - TimeWindowManagerState +class MetricsTimeManager extends React.Component< + MetricsTimeManagerProps, + MetricsTimeManagerState > { - constructor(props?: TimeWindowManagerProps, context?: any) { + constructor(props?: MetricsTimeManagerProps, context?: any) { super(props, context); this.state = { timeout: null }; } /** - * checkWindow determines when the current time window will expire. If it is + * checkWindow determines when the metrics current time window will expire. If it is * already expired, a new time window is dispatched immediately. Otherwise, * setTimeout is used to asynchronously set a new time window when the current * one expires. */ - checkWindow(props: TimeWindowManagerProps) { + checkWindow(props: MetricsTimeManagerProps) { // Clear any existing timeout. if (this.state.timeout) { clearTimeout(this.state.timeout); @@ -61,22 +61,22 @@ class TimeWindowManager extends React.Component< // If there is no current window, or if scale have changed since this // window was generated, set one immediately. if ( - !props.timeWindow.metricsTime.currentWindow || - props.timeWindow.metricsTime.scaleChanged + !props.timeScale.metricsTime.currentWindow || + props.timeScale.metricsTime.shouldUpdateMetricsWindowFromScale ) { this.setWindow(props); return; } - // Exact time ranges can't expire. - if (props.timeWindow.scale.windowEnd) { + // Fixed time ranges can't expire. + if (props.timeScale.scale.fixedWindowEnd) { // this.setWindow(props); return; } const now = props.now ? props.now() : moment(); - const currentEnd = props.timeWindow.metricsTime.currentWindow.end; - const expires = currentEnd.clone().add(props.timeWindow.scale.windowValid); + const currentEnd = props.timeScale.metricsTime.currentWindow.end; + const expires = currentEnd.clone().add(props.timeScale.scale.windowValid); if (now.isAfter(expires)) { // Current time window is expired, reset it. this.setWindow(props); @@ -94,22 +94,28 @@ class TimeWindowManager extends React.Component< /** * setWindow dispatches a new time window, extending backwards from the - * current time. + * current time, by deriving the time from timeScale. */ - setWindow(props: TimeWindowManagerProps) { - if (!props.timeWindow.scale.windowEnd) { - if (!props.timeWindow.metricsTime.useTimeRange) { + setWindow(props: MetricsTimeManagerProps) { + // The following two `if` blocks check two things that in theory should always be in sync. + // They check if the time selection is a dynamic, moving, now. + if (!props.timeScale.scale.fixedWindowEnd) { + if (!props.timeScale.metricsTime.isFixedWindow) { const now = props.now ? props.now() : moment(); - props.setTimeWindow({ - start: now.clone().subtract(props.timeWindow.scale.windowSize), + // Update the metrics page window with the new "now" time for polling. + props.setMetricsMovingWindow({ + start: now.clone().subtract(props.timeScale.scale.windowSize), end: now, }); } } else { - const windowEnd = props.timeWindow.scale.windowEnd; - props.setTimeWindow({ - start: windowEnd.clone().subtract(props.timeWindow.scale.windowSize), - end: windowEnd, + const fixedWindowEnd = props.timeScale.scale.fixedWindowEnd; + // Update the metrics page window with the fixed, custom end time. + props.setMetricsMovingWindow({ + start: fixedWindowEnd + .clone() + .subtract(props.timeScale.scale.windowSize), + end: fixedWindowEnd, }); } } @@ -118,8 +124,8 @@ class TimeWindowManager extends React.Component< this.checkWindow(this.props); } - componentDidUpdate(prevProps: TimeWindowManagerProps) { - if (!_.isEqual(prevProps.timeWindow, this.props.timeWindow)) { + componentDidUpdate(prevProps: MetricsTimeManagerProps) { + if (!_.isEqual(prevProps.timeScale, this.props.timeScale)) { this.checkWindow(this.props); } } @@ -130,16 +136,16 @@ class TimeWindowManager extends React.Component< } } -const timeWindowManagerConnected = connect( +const metricsTimeManagerConnected = connect( (state: AdminUIState) => { return { - timeWindow: state.timewindow, + timeScale: state.timeScale, }; }, { - setTimeWindow: timewindow.setTimeWindow, + setMetricsMovingWindow: timewindow.setMetricsMovingWindow, }, -)(TimeWindowManager); +)(MetricsTimeManager); -export default timeWindowManagerConnected; -export { TimeWindowManager as TimeWindowManagerUnconnected }; +export default metricsTimeManagerConnected; +export { MetricsTimeManager as MetricsTimeManagerUnconnected }; diff --git a/pkg/ui/workspaces/db-console/src/views/app/containers/timewindow/timewindow.spec.tsx b/pkg/ui/workspaces/db-console/src/views/app/containers/metricsTimeManager/metricsTimeManager.spec.tsx similarity index 90% rename from pkg/ui/workspaces/db-console/src/views/app/containers/timewindow/timewindow.spec.tsx rename to pkg/ui/workspaces/db-console/src/views/app/containers/metricsTimeManager/metricsTimeManager.spec.tsx index 3e6ddc23486f..8e111d0ee05c 100644 --- a/pkg/ui/workspaces/db-console/src/views/app/containers/timewindow/timewindow.spec.tsx +++ b/pkg/ui/workspaces/db-console/src/views/app/containers/metricsTimeManager/metricsTimeManager.spec.tsx @@ -16,24 +16,24 @@ import moment from "moment"; import _ from "lodash"; import "src/enzymeInit"; -import { TimeWindowManagerUnconnected as TimeWindowManager } from "./"; -import * as timewindow from "src/redux/timewindow"; +import { MetricsTimeManagerUnconnected as MetricsTimeManager } from "./"; +import * as timewindow from "src/redux/timeScale"; -describe("", function() { +describe("", function() { let spy: sinon.SinonSpy; - let state: timewindow.TimeWindowState; + let state: timewindow.TimeScaleState; const now = () => moment("11-12-1955 10:04PM -0800", "MM-DD-YYYY hh:mma Z"); beforeEach(function() { spy = sinon.spy(); - state = new timewindow.TimeWindowState(); + state = new timewindow.TimeScaleState(); }); const getManager = () => shallow( - , ); @@ -69,7 +69,7 @@ describe("", function() { start: now().subtract(state.scale.windowSize), end: now(), }; - state.metricsTime.scaleChanged = true; + state.metricsTime.shouldUpdateMetricsWindowFromScale = true; getManager(); assert.isTrue(spy.calledOnce); diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/index.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/index.tsx index 98fa2c2f48b1..1b305d47efcb 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/index.tsx @@ -446,7 +446,7 @@ export class LineGraph extends React.Component { // setNewTimeRange uses code from the TimeScaleDropdown component // to set new start/end ranges in the query params and force a // reload of the rest of the dashboard at new ranges via the props - // `setTimeRange` and `setTimeScale`. + // `setMetricsFixedWindow` and `setTimeScale`. // TODO(davidh): centralize management of query params for time range // TODO(davidh): figure out why the timescale doesn't get more granular // automatically when a narrower time frame is selected. @@ -461,7 +461,7 @@ export class LineGraph extends React.Component { let newTimeScale: TimeScale = { ...findClosestTimeScale(defaultTimeScaleOptions, end - start, start), key: "Custom", - windowEnd: moment.unix(end), + fixedWindowEnd: moment.unix(end), }; if (this.props.adjustTimeScaleOnChange) { newTimeScale = this.props.adjustTimeScaleOnChange( @@ -469,7 +469,7 @@ export class LineGraph extends React.Component { newTimeWindow, ); } - this.props.setTimeRange(newTimeWindow); + this.props.setMetricsFixedWindow(newTimeWindow); this.props.setTimeScale(newTimeScale); const { pathname, search } = this.props.history.location; const urlParams = new URLSearchParams(search); diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/linegraph.spec.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/linegraph.spec.tsx index 85f5df0f5e60..4f5fbcbfde87 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/linegraph.spec.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/linegraph.spec.tsx @@ -17,7 +17,7 @@ import uPlot from "uplot"; import _ from "lodash"; import { fillGaps, LineGraph, LineGraphProps } from "./index"; -import * as timewindow from "src/redux/timewindow"; +import * as timewindow from "src/redux/timeScale"; import * as protos from "src/js/protos"; import { Axis } from "src/views/shared/components/metricQuery"; import { @@ -49,7 +49,7 @@ describe("", function() { end: new Long(2346), sampleDuration: new Long(1), }, - setTimeRange: timewindow.setTimeRange, + setMetricsFixedWindow: timewindow.setMetricsFixedWindow, setTimeScale: timewindow.setTimeScale, history: { length: 0, diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/index.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/index.tsx index c8f36f338042..6443a83f8f94 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/index.tsx @@ -22,7 +22,7 @@ import { PageConfig, PageConfigItem, } from "src/views/shared/components/pageconfig"; -import TimeScaleDropdown from "src/views/cluster/containers/timescale"; +import TimeScaleDropdown from "src/views/cluster/containers/timeScaleDropdownWithSearchParams"; import ClusterSummaryBar from "./summaryBar"; import { AdminUIState } from "src/redux/state"; @@ -64,12 +64,12 @@ import overloadDashboard from "./dashboards/overload"; import { getMatchParamByName } from "src/util/query"; import { PayloadAction } from "src/interfaces/action"; import { - setTimeRange, + setMetricsFixedWindow, setTimeScale, TimeWindow, TimeScale, adjustTimeScale, -} from "src/redux/timewindow"; +} from "src/redux/timeScale"; import { InlineAlert } from "src/components"; import { Anchor } from "@cockroachlabs/cluster-ui"; import { reduceStorageOfTimeSeriesDataOperationalFlags } from "src/util/docs"; @@ -119,7 +119,7 @@ type MapDispatchToProps = { refreshNodeSettings: typeof refreshSettings; hoverOn: typeof hoverOn; hoverOff: typeof hoverOff; - setTimeRange: (tw: TimeWindow) => PayloadAction; + setMetricsFixedWindow: (tw: TimeWindow) => PayloadAction; setTimeScale: (ts: TimeScale) => PayloadAction; }; @@ -305,7 +305,7 @@ export class NodeGraphs extends React.Component< { const setDatesByQueryParams = (dates: Partial) => { const now = moment.utc(); + // `currentWindow` is derived from `scale`, and does not have to do with the `currentWindow` for the metrics page. const currentWindow: TimeWindow = { start: moment(now).subtract(props.currentScale.windowSize), end: now, }; + /** + * Prioritize an end defined in the query params. + * Else, use window end. + * Else, a seemingly unreachable option says otherwise use now, but that should never happen since it is set in + * the line above (and is the same value anyway, always now). + */ const end = dates.end?.utc() || currentWindow.end?.utc() || now; + /** + * Prioritize start as defined in the query params. + * Else, use now minus the window size. + * Else, a final seemingly unreachable option (since start is always set above) is to do ten minutes before now. + */ const start = dates.start?.utc() || currentWindow.start?.utc() || @@ -50,16 +62,13 @@ const TimeScaleDropdownWithSearchParams = ( const timeScale: TimeScale = { ...findClosestTimeScale(defaultTimeScaleOptions, seconds), windowSize: moment.duration(end.diff(start)), - windowEnd: null, + fixedWindowEnd: false, }; // Check if the end is close to now, with "close" defined as being no more than `sampleSize` behind. - if ( - moment.duration(now.diff(end)).asMinutes() > - timeScale.sampleSize.asMinutes() - ) { + if (now > end.subtract(timeScale.sampleSize)) { // The end is far enough away from now, thus this is a custom selection. timeScale.key = "Custom"; - timeScale.windowEnd = end; + timeScale.fixedWindowEnd = end; } props.setTimeScale(timeScale); }; @@ -100,7 +109,7 @@ const TimeScaleDropdownWithSearchParams = ( props.setTimeScale(timeScale); setQueryParamsByDates( timeScale.windowSize, - timeScale.windowEnd || moment.utc(), + timeScale.fixedWindowEnd || moment.utc(), ); }; @@ -108,7 +117,7 @@ const TimeScaleDropdownWithSearchParams = ( }; const scaleSelector = createSelector( - (state: AdminUIState) => state?.timewindow, + (state: AdminUIState) => state?.timeScale, tw => tw?.scale, ); diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/timescale/timescale.styl b/pkg/ui/workspaces/db-console/src/views/cluster/containers/timeScaleDropdownWithSearchParams/timeScaleDropdownWithSearchParams.styl similarity index 100% rename from pkg/ui/workspaces/db-console/src/views/cluster/containers/timescale/timescale.styl rename to pkg/ui/workspaces/db-console/src/views/cluster/containers/timeScaleDropdownWithSearchParams/timeScaleDropdownWithSearchParams.styl diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/util/graphs.ts b/pkg/ui/workspaces/db-console/src/views/cluster/util/graphs.ts index 0dc944551a92..f0f8d7933f17 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/util/graphs.ts +++ b/pkg/ui/workspaces/db-console/src/views/cluster/util/graphs.ts @@ -541,7 +541,7 @@ export function configureUPlotLineChart( metrics: React.ReactElement[], axis: React.ReactElement, data: TSResponse, - setTimeRange: (startMillis: number, endMillis: number) => void, + setMetricsFixedWindow: (startMillis: number, endMillis: number) => void, getLatestXAxisDomain: () => AxisDomain, getLatestYAxisDomain: () => AxisDomain, ): uPlot.Options { @@ -677,7 +677,7 @@ export function configureUPlotLineChart( // From what I understand, `self.select` contains the pixel edges // of the user's selection. Then I use the `posToIdx` to tell me // what the xAxis range is of the pixels. - setTimeRange( + setMetricsFixedWindow( self.data[0][self.posToIdx(self.select.left)], self.data[0][self.posToIdx(self.select.left + self.select.width)], ); diff --git a/pkg/ui/workspaces/db-console/src/views/devtools/containers/raftMessages/index.tsx b/pkg/ui/workspaces/db-console/src/views/devtools/containers/raftMessages/index.tsx index 54d2676c2cb8..c0a087e17902 100644 --- a/pkg/ui/workspaces/db-console/src/views/devtools/containers/raftMessages/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/devtools/containers/raftMessages/index.tsx @@ -28,7 +28,7 @@ import { GraphDashboardProps, storeIDsForNode, } from "src/views/cluster/containers/nodeGraphs/dashboards/dashboardUtils"; -import TimeScaleDropdown from "src/views/cluster/containers/timescale"; +import TimeScaleDropdown from "src/views/cluster/containers/timeScaleDropdownWithSearchParams"; import Dropdown, { DropdownOption } from "src/views/shared/components/dropdown"; import { PageConfig, diff --git a/pkg/ui/workspaces/db-console/src/views/reports/containers/customChart/index.tsx b/pkg/ui/workspaces/db-console/src/views/reports/containers/customChart/index.tsx index 4a0742d80e34..9a83895262ef 100644 --- a/pkg/ui/workspaces/db-console/src/views/reports/containers/customChart/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/reports/containers/customChart/index.tsx @@ -19,7 +19,7 @@ import { refreshMetricMetadata, refreshNodes } from "src/redux/apiReducers"; import { nodesSummarySelector, NodesSummary } from "src/redux/nodes"; import { AdminUIState } from "src/redux/state"; import { LineGraph } from "src/views/cluster/components/linegraph"; -import TimeScaleDropdown from "src/views/cluster/containers/timescale"; +import TimeScaleDropdown from "src/views/cluster/containers/timeScaleDropdownWithSearchParams"; import { DropdownOption } from "src/views/shared/components/dropdown"; import { MetricsDataProvider } from "src/views/shared/containers/metricDataProvider"; import { @@ -44,9 +44,9 @@ import { PayloadAction } from "src/interfaces/action"; import { TimeWindow, TimeScale, - setTimeRange, + setMetricsFixedWindow, setTimeScale, -} from "src/redux/timewindow"; +} from "src/redux/timeScale"; export interface CustomChartProps { refreshNodes: typeof refreshNodes; @@ -54,7 +54,7 @@ export interface CustomChartProps { nodesSummary: NodesSummary; refreshMetricMetadata: typeof refreshMetricMetadata; metricsMetadata: MetricsMetadata; - setTimeRange: (tw: TimeWindow) => PayloadAction; + setMetricsFixedWindow: (tw: TimeWindow) => PayloadAction; setTimeScale: (ts: TimeScale) => PayloadAction; } @@ -205,7 +205,7 @@ export class CustomChart extends React.Component< @@ -328,7 +328,7 @@ const mapStateToProps = (state: AdminUIState) => ({ const mapDispatchToProps = { refreshNodes, refreshMetricMetadata, - setTimeRange, + setMetricsFixedWindow: setMetricsFixedWindow, setTimeScale, }; diff --git a/pkg/ui/workspaces/db-console/src/views/shared/components/metricQuery/index.tsx b/pkg/ui/workspaces/db-console/src/views/shared/components/metricQuery/index.tsx index cb7bb9cb119b..77d22c1389b8 100644 --- a/pkg/ui/workspaces/db-console/src/views/shared/components/metricQuery/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/shared/components/metricQuery/index.tsx @@ -37,7 +37,7 @@ import TimeSeriesQueryAggregator = protos.cockroach.ts.tspb.TimeSeriesQueryAggre import TimeSeriesQueryDerivative = protos.cockroach.ts.tspb.TimeSeriesQueryDerivative; import Long from "long"; import { History } from "history"; -import { TimeWindow, TimeScale } from "src/redux/timewindow"; +import { TimeWindow, TimeScale } from "src/redux/timeScale"; import { PayloadAction } from "src/interfaces/action"; /** @@ -160,7 +160,7 @@ export interface MetricsDataComponentProps { // convenient syntax for a common use case where all metrics on a graph are // are from the same source set. sources?: string[]; - setTimeRange?: (tw: TimeWindow) => PayloadAction; + setMetricsFixedWindow?: (tw: TimeWindow) => PayloadAction; setTimeScale?: (ts: TimeScale) => PayloadAction; history?: History; adjustTimeScaleOnChange?: ( diff --git a/pkg/ui/workspaces/db-console/src/views/shared/containers/metricDataProvider/index.tsx b/pkg/ui/workspaces/db-console/src/views/shared/containers/metricDataProvider/index.tsx index 4eac2393d59a..69b1778a6813 100644 --- a/pkg/ui/workspaces/db-console/src/views/shared/containers/metricDataProvider/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/shared/containers/metricDataProvider/index.tsx @@ -97,7 +97,7 @@ interface MetricsDataProviderConnectProps { timeInfo: QueryTimeInfo; requestMetrics: typeof requestMetricsAction; refreshNodeSettings: typeof refreshSettings; - setTimeRange?: (tw: TimeWindow) => PayloadAction; + setMetricsFixedWindow?: (tw: TimeWindow) => PayloadAction; setTimeScale?: (ts: TimeScale) => PayloadAction; history?: History; } @@ -234,7 +234,7 @@ class MetricsDataProvider extends React.Component< const dataProps: MetricsDataComponentProps = { data: this.getData(), timeInfo: this.props.timeInfo, - setTimeRange: this.props.setTimeRange, + setMetricsFixedWindow: this.props.setMetricsFixedWindow, setTimeScale: this.props.setTimeScale, history: this.props.history, adjustTimeScaleOnChange, @@ -249,7 +249,7 @@ class MetricsDataProvider extends React.Component< // timeInfoSelector converts the current global time window into a set of Long // timestamps, which can be sent with requests to the server. const timeInfoSelector = createSelector( - (state: AdminUIState) => state.timewindow, + (state: AdminUIState) => state.timeScale, tw => { if (!_.isObject(tw.scale)) { return null;