github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/src/views/app/containers/timewindow/index.tsx (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  import React from "react";
    12  import { connect } from "react-redux";
    13  import moment from "moment";
    14  
    15  import { AdminUIState } from "src/redux/state";
    16  import * as timewindow from "src/redux/timewindow";
    17  import _ from "lodash";
    18  
    19  interface TimeWindowManagerProps {
    20    // The current timewindow redux state.
    21    timeWindow: timewindow.TimeWindowState;
    22    // Callback function used to set a new time window.
    23    setTimeWindow: typeof timewindow.setTimeWindow;
    24    // Optional override method to obtain the current time. Used for tests.
    25    now?: () => moment.Moment;
    26  }
    27  
    28  interface TimeWindowManagerState {
    29    // Identifier from an outstanding call to setTimeout.
    30    timeout: number;
    31  }
    32  
    33  /**
    34   * TimeWindowManager takes responsibility for advancing the current global
    35   * time window used by metric graphs. It renders nothing, but will dispatch an
    36   * updated time window into the redux store whenever the previous time window is
    37   * expired.
    38   */
    39  class TimeWindowManager extends React.Component<TimeWindowManagerProps, TimeWindowManagerState> {
    40    constructor(props?: TimeWindowManagerProps, context?: any) {
    41      super(props, context);
    42      this.state = { timeout: null };
    43    }
    44  
    45    /**
    46     * checkWindow determines when the current time window will expire. If it is
    47     * already expired, a new time window is dispatched immediately. Otherwise,
    48     * setTimeout is used to asynchronously set a new time window when the current
    49     * one expires.
    50     */
    51    checkWindow(props: TimeWindowManagerProps) {
    52      // Clear any existing timeout.
    53      if (this.state.timeout) {
    54        clearTimeout(this.state.timeout);
    55        this.setState({ timeout: null });
    56      }
    57  
    58      // If there is no current window, or if scale have changed since this
    59      // window was generated, set one immediately.
    60      if (!props.timeWindow.currentWindow || props.timeWindow.scaleChanged) {
    61        this.setWindow(props);
    62        return;
    63      }
    64  
    65      // Exact time ranges can't expire.
    66      if (props.timeWindow.scale.windowEnd) {
    67        // this.setWindow(props);
    68        return;
    69      }
    70  
    71      const now = props.now ? props.now() : moment();
    72      const currentEnd = props.timeWindow.currentWindow.end;
    73      const expires = currentEnd.clone().add(props.timeWindow.scale.windowValid);
    74      if (now.isAfter(expires))  {
    75        // Current time window is expired, reset it.
    76        this.setWindow(props);
    77      } else {
    78        // Set a timeout to reset the window when the current window expires.
    79        const newTimeout = window.setTimeout(() => this.setWindow(props), expires.diff(now).valueOf());
    80        this.setState({
    81          timeout: newTimeout,
    82        });
    83      }
    84    }
    85  
    86    /**
    87     * setWindow dispatches a new time window, extending backwards from the
    88     * current time.
    89     */
    90    setWindow(props: TimeWindowManagerProps) {
    91      if (!props.timeWindow.scale.windowEnd) {
    92        if (!props.timeWindow.useTimeRange) {
    93          const now = props.now ? props.now() : moment();
    94          props.setTimeWindow({
    95            start: now.clone().subtract(props.timeWindow.scale.windowSize),
    96            end: now,
    97          });
    98        }
    99      } else {
   100        const windowEnd = props.timeWindow.scale.windowEnd;
   101        props.setTimeWindow({
   102          start: windowEnd.clone().subtract(props.timeWindow.scale.windowSize),
   103          end: windowEnd,
   104        });
   105      }
   106    }
   107  
   108    componentDidMount() {
   109      this.checkWindow(this.props);
   110    }
   111  
   112    componentDidUpdate(prevProps: TimeWindowManagerProps) {
   113      if (!_.isEqual(prevProps.timeWindow, this.props.timeWindow)) {
   114        this.checkWindow(this.props);
   115      }
   116    }
   117  
   118    render(): any {
   119      // Render nothing.
   120      return null;
   121    }
   122  }
   123  
   124  const timeWindowManagerConnected = connect(
   125    (state: AdminUIState) => {
   126      return {
   127        timeWindow: state.timewindow,
   128      };
   129    },
   130    {
   131      setTimeWindow: timewindow.setTimeWindow,
   132    },
   133  )(TimeWindowManager);
   134  
   135  export default timeWindowManagerConnected;
   136  export { TimeWindowManager as TimeWindowManagerUnconnected };