github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/src/redux/timewindow.ts (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 /** 12 * This module maintains a globally-available time window, currently used by all 13 * metrics graphs in the ui. 14 */ 15 16 import { Action } from "redux"; 17 import { PayloadAction } from "src/interfaces/action"; 18 import _ from "lodash"; 19 import moment from "moment"; 20 21 export const SET_WINDOW = "cockroachui/timewindow/SET_WINDOW"; 22 export const SET_RANGE = "cockroachui/timewindow/SET_RANGE"; 23 export const SET_SCALE = "cockroachui/timewindow/SET_SCALE"; 24 25 /** 26 * TimeWindow represents an absolute window of time, defined with a start and 27 * end time. 28 */ 29 export interface TimeWindow { 30 start: moment.Moment; 31 end: moment.Moment; 32 } 33 34 /** 35 * TimeScale describes the requested dimensions of TimeWindows; it 36 * prescribes a length for the window, along with a period of time that a 37 * newly created TimeWindow will remain valid. 38 */ 39 export interface TimeScale { 40 // The key used to index in to the availableTimeScales collection. 41 key?: string; 42 // The size of a global time window. Default is ten minutes. 43 windowSize: moment.Duration; 44 // The length of time the global time window is valid. The current time window 45 // is invalid if now > (currentWindow.end + windowValid). Default is ten 46 // seconds. If windowEnd is set this is ignored. 47 windowValid?: moment.Duration; 48 // The expected duration of individual samples for queries at this time scale. 49 sampleSize: moment.Duration; 50 // The end time of the window if it isn't the present 51 windowEnd?: moment.Moment; 52 } 53 54 export interface TimeScaleCollection { 55 [key: string]: TimeScale; 56 } 57 58 /** 59 * availableTimeScales is a preconfigured set of time scales that can be 60 * selected by the user. 61 */ 62 export let availableTimeScales: TimeScaleCollection = _.mapValues( 63 { 64 "Past 10 Minutes": { 65 windowSize: moment.duration(10, "minutes"), 66 windowValid: moment.duration(10, "seconds"), 67 sampleSize: moment.duration(10, "seconds"), 68 }, 69 "Past 1 Hour": { 70 windowSize: moment.duration(1, "hour"), 71 windowValid: moment.duration(1, "minute"), 72 sampleSize: moment.duration(30, "seconds"), 73 }, 74 "Past 6 Hours": { 75 windowSize: moment.duration(6, "hours"), 76 windowValid: moment.duration(5, "minutes"), 77 sampleSize: moment.duration(1, "minutes"), 78 }, 79 "Past 1 Day": { 80 windowSize: moment.duration(1, "day"), 81 windowValid: moment.duration(10, "minutes"), 82 sampleSize: moment.duration(5, "minutes"), 83 }, 84 "Past 2 Days": { 85 windowSize: moment.duration(2, "day"), 86 windowValid: moment.duration(10, "minutes"), 87 sampleSize: moment.duration(5, "minutes"), 88 }, 89 "Past 3 Days": { 90 windowSize: moment.duration(3, "day"), 91 windowValid: moment.duration(10, "minutes"), 92 sampleSize: moment.duration(5, "minutes"), 93 }, 94 "Past Week": { 95 windowSize: moment.duration(7, "days"), 96 windowValid: moment.duration(10, "minutes"), 97 sampleSize: moment.duration(30, "minutes"), 98 }, 99 "Past 2 Weeks": { 100 windowSize: moment.duration(14, "days"), 101 windowValid: moment.duration(10, "minutes"), 102 sampleSize: moment.duration(30, "minutes"), 103 }, 104 "Past Month": { 105 windowSize: moment.duration(moment().daysInMonth(), "days"), 106 windowValid: moment.duration(20, "minutes"), 107 sampleSize: moment.duration(1, "hour"), 108 }, 109 "Past 2 Months": { 110 windowSize: moment.duration(moment().daysInMonth() * 2, "days"), 111 windowValid: moment.duration(20, "minutes"), 112 sampleSize: moment.duration(1, "hour"), 113 }, 114 }, 115 (v, k) => { 116 // This weirdness is to work around an apparent issue in TypeScript: 117 // https://github.com/Microsoft/TypeScript/issues/20305 118 const result: TimeScale = v; 119 // Set the "key" attribute. 120 result.key = k; 121 return result; 122 }, 123 ); 124 125 export const findClosestTimeScale = (seconds: number) => { 126 const data: TimeScale[] = []; 127 Object.keys(availableTimeScales).forEach((val) => data.push(availableTimeScales[val])); 128 data.sort( (a, b) => (Math.abs(seconds - a.windowSize.asSeconds()) - Math.abs(seconds - b.windowSize.asSeconds())) ); 129 return data[0].windowSize.asSeconds() === seconds ? data[0] : { ...data[0], key: "Custom" }; 130 }; 131 132 export class TimeWindowState { 133 // Currently selected scale. 134 scale: TimeScale; 135 // Currently established time window. 136 currentWindow: TimeWindow; 137 // True if scale has changed since currentWindow was generated. 138 scaleChanged: boolean; 139 useTimeRange: boolean; 140 constructor() { 141 this.scale = availableTimeScales["Past 10 Minutes"]; 142 this.useTimeRange = false; 143 this.scaleChanged = false; 144 } 145 } 146 147 export function timeWindowReducer(state = new TimeWindowState(), action: Action): TimeWindowState { 148 switch (action.type) { 149 case SET_WINDOW: 150 const { payload: tw } = action as PayloadAction<TimeWindow>; 151 state = _.clone(state); 152 state.currentWindow = tw; 153 state.scaleChanged = false; 154 return state; 155 case SET_RANGE: 156 const { payload: data } = action as PayloadAction<TimeWindow>; 157 state = _.clone(state); 158 state.currentWindow = data; 159 state.useTimeRange = true; 160 state.scaleChanged = false; 161 return state; 162 case SET_SCALE: 163 const { payload: scale } = action as PayloadAction<TimeScale>; 164 state = _.clone(state); 165 if (scale.key === "Custom") { 166 state.useTimeRange = true; 167 } else { 168 state.useTimeRange = false; 169 } 170 state.scale = scale; 171 state.scaleChanged = true; 172 return state; 173 default: 174 return state; 175 } 176 } 177 178 export function setTimeWindow(tw: TimeWindow): PayloadAction<TimeWindow> { 179 return { 180 type: SET_WINDOW, 181 payload: tw, 182 }; 183 } 184 185 export function setTimeRange(tw: TimeWindow): PayloadAction<TimeWindow> { 186 return { 187 type: SET_RANGE, 188 payload: tw, 189 }; 190 } 191 192 export function setTimeScale(ts: TimeScale): PayloadAction<TimeScale> { 193 return { 194 type: SET_SCALE, 195 payload: ts, 196 }; 197 }