|
@@ -1,43 +1,54 @@
|
|
|
+import {useState} from 'react';
|
|
|
import styled from '@emotion/styled';
|
|
|
|
|
|
import {Button} from 'sentry/components/button';
|
|
|
import ButtonBar from 'sentry/components/buttonBar';
|
|
|
import SelectControl from 'sentry/components/forms/controls/selectControl';
|
|
|
import InternalStatChart from 'sentry/components/internalStatChart';
|
|
|
+import LoadingError from 'sentry/components/loadingError';
|
|
|
+import LoadingIndicator from 'sentry/components/loadingIndicator';
|
|
|
import Panel from 'sentry/components/panels/panel';
|
|
|
import PanelBody from 'sentry/components/panels/panelBody';
|
|
|
import PanelHeader from 'sentry/components/panels/panelHeader';
|
|
|
-import DeprecatedAsyncView from 'sentry/views/deprecatedAsyncView';
|
|
|
+import {t} from 'sentry/locale';
|
|
|
+import {useApiQuery} from 'sentry/utils/queryClient';
|
|
|
|
|
|
const TIME_WINDOWS = ['1h', '1d', '1w'] as const;
|
|
|
|
|
|
type TimeWindow = (typeof TIME_WINDOWS)[number];
|
|
|
|
|
|
-type State = DeprecatedAsyncView['state'] & {
|
|
|
+type State = {
|
|
|
activeTask: string;
|
|
|
resolution: string;
|
|
|
since: number;
|
|
|
- taskList: string[];
|
|
|
- taskName: string;
|
|
|
timeWindow: TimeWindow;
|
|
|
};
|
|
|
|
|
|
-export default class AdminQueue extends DeprecatedAsyncView<{}, State> {
|
|
|
- getDefaultState() {
|
|
|
- return {
|
|
|
- ...super.getDefaultState(),
|
|
|
- timeWindow: '1w',
|
|
|
- since: new Date().getTime() / 1000 - 3600 * 24 * 7,
|
|
|
- resolution: '1h',
|
|
|
- taskName: null,
|
|
|
- };
|
|
|
+export default function AdminQueue() {
|
|
|
+ const [state, setState] = useState<State>({
|
|
|
+ timeWindow: '1w',
|
|
|
+ since: new Date().getTime() / 1000 - 3600 * 24 * 7,
|
|
|
+ resolution: '1h',
|
|
|
+ activeTask: '',
|
|
|
+ });
|
|
|
+
|
|
|
+ const {
|
|
|
+ data: taskList,
|
|
|
+ isLoading,
|
|
|
+ isError,
|
|
|
+ } = useApiQuery<string[]>(['/internal/queue/tasks/'], {
|
|
|
+ staleTime: 0,
|
|
|
+ });
|
|
|
+
|
|
|
+ if (isError) {
|
|
|
+ return <LoadingError />;
|
|
|
}
|
|
|
|
|
|
- getEndpoints(): ReturnType<DeprecatedAsyncView['getEndpoints']> {
|
|
|
- return [['taskList', '/internal/queue/tasks/']];
|
|
|
+ if (isLoading) {
|
|
|
+ return <LoadingIndicator />;
|
|
|
}
|
|
|
|
|
|
- changeWindow(timeWindow: TimeWindow) {
|
|
|
+ const changeWindow = (timeWindow: TimeWindow) => {
|
|
|
let seconds: number;
|
|
|
if (timeWindow === '1h') {
|
|
|
seconds = 3600;
|
|
@@ -48,94 +59,93 @@ export default class AdminQueue extends DeprecatedAsyncView<{}, State> {
|
|
|
} else {
|
|
|
throw new Error('Invalid time window');
|
|
|
}
|
|
|
- this.setState({
|
|
|
+ setState(prevState => ({
|
|
|
+ ...prevState,
|
|
|
since: new Date().getTime() / 1000 - seconds,
|
|
|
timeWindow,
|
|
|
- });
|
|
|
- }
|
|
|
+ }));
|
|
|
+ };
|
|
|
|
|
|
- changeTask(value: string) {
|
|
|
- this.setState({activeTask: value});
|
|
|
- }
|
|
|
+ const changeTask = (value: string) => {
|
|
|
+ setState(prevState => ({...prevState, activeTask: value}));
|
|
|
+ };
|
|
|
+
|
|
|
+ const {activeTask} = state;
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <Header>
|
|
|
+ <h3>t{'Queue Overview'}</h3>
|
|
|
|
|
|
- renderBody() {
|
|
|
- const {activeTask, taskList} = this.state;
|
|
|
+ <ButtonBar merged active={state.timeWindow}>
|
|
|
+ {TIME_WINDOWS.map(r => (
|
|
|
+ <Button size="sm" barId={r} onClick={() => changeWindow(r)} key={r}>
|
|
|
+ {r}
|
|
|
+ </Button>
|
|
|
+ ))}
|
|
|
+ </ButtonBar>
|
|
|
+ </Header>
|
|
|
+
|
|
|
+ <Panel>
|
|
|
+ <PanelHeader>{t('Global Throughput')}</PanelHeader>
|
|
|
+ <PanelBody withPadding>
|
|
|
+ <InternalStatChart
|
|
|
+ since={state.since}
|
|
|
+ resolution={state.resolution}
|
|
|
+ stat="jobs.all.started"
|
|
|
+ label="jobs started"
|
|
|
+ />
|
|
|
+ </PanelBody>
|
|
|
+ </Panel>
|
|
|
+
|
|
|
+ <h3>t{'Task Details'}</h3>
|
|
|
|
|
|
- return (
|
|
|
<div>
|
|
|
- <Header>
|
|
|
- <h3>Queue Overview</h3>
|
|
|
-
|
|
|
- <ButtonBar merged active={this.state.timeWindow}>
|
|
|
- {TIME_WINDOWS.map(r => (
|
|
|
- <Button size="sm" barId={r} onClick={() => this.changeWindow(r)} key={r}>
|
|
|
- {r}
|
|
|
- </Button>
|
|
|
- ))}
|
|
|
- </ButtonBar>
|
|
|
- </Header>
|
|
|
-
|
|
|
- <Panel>
|
|
|
- <PanelHeader>Global Throughput</PanelHeader>
|
|
|
- <PanelBody withPadding>
|
|
|
- <InternalStatChart
|
|
|
- since={this.state.since}
|
|
|
- resolution={this.state.resolution}
|
|
|
- stat="jobs.all.started"
|
|
|
- label="jobs started"
|
|
|
- />
|
|
|
- </PanelBody>
|
|
|
- </Panel>
|
|
|
-
|
|
|
- <h3>Task Details</h3>
|
|
|
-
|
|
|
- <div>
|
|
|
- <div className="m-b-1">
|
|
|
- <label>Show details for task:</label>
|
|
|
- <SelectControl
|
|
|
- name="task"
|
|
|
- onChange={({value}) => this.changeTask(value)}
|
|
|
- value={activeTask}
|
|
|
- clearable
|
|
|
- options={taskList.map(value => ({value, label: value}))}
|
|
|
- />
|
|
|
- </div>
|
|
|
- {activeTask ? (
|
|
|
- <div>
|
|
|
- <Panel key={`jobs.started.${activeTask}`}>
|
|
|
- <PanelHeader>
|
|
|
- Jobs Started <small>{activeTask}</small>
|
|
|
- </PanelHeader>
|
|
|
- <PanelBody withPadding>
|
|
|
- <InternalStatChart
|
|
|
- since={this.state.since}
|
|
|
- resolution={this.state.resolution}
|
|
|
- stat={`jobs.started.${activeTask}`}
|
|
|
- label="jobs"
|
|
|
- height={100}
|
|
|
- />
|
|
|
- </PanelBody>
|
|
|
- </Panel>
|
|
|
- <Panel key={`jobs.finished.${activeTask}`}>
|
|
|
- <PanelHeader>
|
|
|
- Jobs Finished <small>{activeTask}</small>
|
|
|
- </PanelHeader>
|
|
|
- <PanelBody withPadding>
|
|
|
- <InternalStatChart
|
|
|
- since={this.state.since}
|
|
|
- resolution={this.state.resolution}
|
|
|
- stat={`jobs.finished.${activeTask}`}
|
|
|
- label="jobs"
|
|
|
- height={100}
|
|
|
- />
|
|
|
- </PanelBody>
|
|
|
- </Panel>
|
|
|
- </div>
|
|
|
- ) : null}
|
|
|
+ <div className="m-b-1">
|
|
|
+ <label>t{'Show details for task:'}</label>
|
|
|
+ <SelectControl
|
|
|
+ name="task"
|
|
|
+ onChange={({value}) => changeTask(value)}
|
|
|
+ value={activeTask}
|
|
|
+ clearable
|
|
|
+ options={taskList.map(value => ({value, label: value}))}
|
|
|
+ />
|
|
|
</div>
|
|
|
+ {activeTask ? (
|
|
|
+ <div>
|
|
|
+ <Panel key={`jobs.started.${activeTask}`}>
|
|
|
+ <PanelHeader>
|
|
|
+ {t('Jobs Started')} <small>{activeTask}</small>
|
|
|
+ </PanelHeader>
|
|
|
+ <PanelBody withPadding>
|
|
|
+ <InternalStatChart
|
|
|
+ since={state.since}
|
|
|
+ resolution={state.resolution}
|
|
|
+ stat={`jobs.started.${activeTask}`}
|
|
|
+ label="jobs"
|
|
|
+ height={100}
|
|
|
+ />
|
|
|
+ </PanelBody>
|
|
|
+ </Panel>
|
|
|
+ <Panel key={`jobs.finished.${activeTask}`}>
|
|
|
+ <PanelHeader>
|
|
|
+ {t('Jobs Finished')} <small>{activeTask}</small>
|
|
|
+ </PanelHeader>
|
|
|
+ <PanelBody withPadding>
|
|
|
+ <InternalStatChart
|
|
|
+ since={state.since}
|
|
|
+ resolution={state.resolution}
|
|
|
+ stat={`jobs.finished.${activeTask}`}
|
|
|
+ label="jobs"
|
|
|
+ height={100}
|
|
|
+ />
|
|
|
+ </PanelBody>
|
|
|
+ </Panel>
|
|
|
+ </div>
|
|
|
+ ) : null}
|
|
|
</div>
|
|
|
- );
|
|
|
- }
|
|
|
+ </div>
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
const Header = styled('div')`
|