mirror of https://github.com/prometheus/prometheus
React UI: Add Starting Screen to Individual Pages (#8909)
* Fix/removed forwarding Signed-off-by: Levi Harrison <git@leviharrison.dev> * Added global 'wasReady' and 'wasUnexpected' Signed-off-by: Levi Harrison <git@leviharrison.dev> * Eslint fixes Signed-off-by: Levi Harrison <git@leviharrison.dev> * Added withStartingIndicator wrapper Signed-off-by: Levi Harrison <git@leviharrison.dev> * Fixed condition Signed-off-by: Levi Harrison <git@leviharrison.dev> * Removed unused import Signed-off-by: Levi Harrison <git@leviharrison.dev> * Moved withStartingIndicator calls to pages index Signed-off-by: Levi Harrison <git@leviharrison.dev> * Fixed withStartingIndicator tests Signed-off-by: Levi Harrison <git@leviharrison.dev> * Fixed eslint (maybe?) Signed-off-by: Levi Harrison <git@leviharrison.dev> * Trailing comma Signed-off-by: Levi Harrison <git@leviharrison.dev> * Added prettier ignore Signed-off-by: Levi Harrison <git@leviharrison.dev> * Fix eslint (pt. 2) Signed-off-by: Levi Harrison <git@leviharrison.dev>pull/8946/head
parent
ea3728bcac
commit
eb8ca06885
|
@ -4,7 +4,17 @@ import App from './App';
|
|||
import Navigation from './Navbar';
|
||||
import { Container } from 'reactstrap';
|
||||
import { Router } from '@reach/router';
|
||||
import { Alerts, Config, Flags, Rules, ServiceDiscovery, Status, Targets, TSDBStatus, PanelList } from './pages';
|
||||
import {
|
||||
AlertsPage,
|
||||
ConfigPage,
|
||||
FlagsPage,
|
||||
RulesPage,
|
||||
ServiceDiscoveryPage,
|
||||
StatusPage,
|
||||
TargetsPage,
|
||||
TSDBStatusPage,
|
||||
PanelListPage,
|
||||
} from './pages';
|
||||
|
||||
describe('App', () => {
|
||||
const app = shallow(<App />);
|
||||
|
@ -13,7 +23,17 @@ describe('App', () => {
|
|||
expect(app.find(Navigation)).toHaveLength(1);
|
||||
});
|
||||
it('routes', () => {
|
||||
[Alerts, Config, Flags, Rules, ServiceDiscovery, Status, Targets, TSDBStatus, PanelList].forEach(component => {
|
||||
[
|
||||
AlertsPage,
|
||||
ConfigPage,
|
||||
FlagsPage,
|
||||
RulesPage,
|
||||
ServiceDiscoveryPage,
|
||||
StatusPage,
|
||||
TargetsPage,
|
||||
TSDBStatusPage,
|
||||
PanelListPage,
|
||||
].forEach(component => {
|
||||
const c = app.find(component);
|
||||
expect(c).toHaveLength(1);
|
||||
});
|
||||
|
|
|
@ -2,15 +2,23 @@ import React, { FC } from 'react';
|
|||
import Navigation from './Navbar';
|
||||
import { Container } from 'reactstrap';
|
||||
|
||||
import { Router, Redirect, navigate } from '@reach/router';
|
||||
import { Router, Redirect } from '@reach/router';
|
||||
import useMedia from 'use-media';
|
||||
import { Alerts, Config, Flags, Rules, ServiceDiscovery, Status, Targets, TSDBStatus, PanelList, Starting } from './pages';
|
||||
import {
|
||||
AlertsPage,
|
||||
ConfigPage,
|
||||
FlagsPage,
|
||||
RulesPage,
|
||||
ServiceDiscoveryPage,
|
||||
StatusPage,
|
||||
TargetsPage,
|
||||
TSDBStatusPage,
|
||||
PanelListPage,
|
||||
} from './pages';
|
||||
import { PathPrefixContext } from './contexts/PathPrefixContext';
|
||||
import { ThemeContext, themeName, themeSetting } from './contexts/ThemeContext';
|
||||
import { Theme, themeLocalStorageKey } from './Theme';
|
||||
import { useLocalStorage } from './hooks/useLocalStorage';
|
||||
import { useFetchReady } from './hooks/useFetch';
|
||||
import { usePathPrefix } from './contexts/PathPrefixContext';
|
||||
|
||||
interface AppProps {
|
||||
consolesLink: string | null;
|
||||
|
@ -31,7 +39,6 @@ const App: FC<AppProps> = ({ consolesLink }) => {
|
|||
'/rules',
|
||||
'/targets',
|
||||
'/service-discovery',
|
||||
'/starting',
|
||||
];
|
||||
if (basePath.endsWith('/')) {
|
||||
basePath = basePath.slice(0, -1);
|
||||
|
@ -45,14 +52,6 @@ const App: FC<AppProps> = ({ consolesLink }) => {
|
|||
}
|
||||
}
|
||||
|
||||
const pathPrefix = usePathPrefix();
|
||||
const { ready, isLoading, isUnexpected } = useFetchReady(pathPrefix);
|
||||
if (basePath !== '/starting') {
|
||||
if (!ready && !isLoading && !isUnexpected) {
|
||||
navigate('/starting');
|
||||
}
|
||||
}
|
||||
|
||||
const [userTheme, setUserTheme] = useLocalStorage<themeSetting>(themeLocalStorageKey, 'auto');
|
||||
const browserHasThemes = useMedia('(prefers-color-scheme)');
|
||||
const browserWantsDarkTheme = useMedia('(prefers-color-scheme: dark)');
|
||||
|
@ -78,16 +77,15 @@ const App: FC<AppProps> = ({ consolesLink }) => {
|
|||
NOTE: Any route added here needs to also be added to the list of
|
||||
React-handled router paths ("reactRouterPaths") in /web/web.go.
|
||||
*/}
|
||||
<PanelList path="/graph" />
|
||||
<Alerts path="/alerts" />
|
||||
<Config path="/config" />
|
||||
<Flags path="/flags" />
|
||||
<Rules path="/rules" />
|
||||
<ServiceDiscovery path="/service-discovery" />
|
||||
<Status path="/status" />
|
||||
<TSDBStatus path="/tsdb-status" />
|
||||
<Targets path="/targets" />
|
||||
<Starting path="/starting" />
|
||||
<PanelListPage path="/graph" />
|
||||
<AlertsPage path="/alerts" />
|
||||
<ConfigPage path="/config" />
|
||||
<FlagsPage path="/flags" />
|
||||
<RulesPage path="/rules" />
|
||||
<ServiceDiscoveryPage path="/service-discovery" />
|
||||
<StatusPage path="/status" />
|
||||
<TSDBStatusPage path="/tsdb-status" />
|
||||
<TargetsPage path="/targets" />
|
||||
</Router>
|
||||
</Container>
|
||||
</PathPrefixContext.Provider>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { WALReplayData } from '../../types/types';
|
||||
import { StartingContent } from './Starting';
|
||||
import { WALReplayData } from '../types/types';
|
||||
import { StartingContent } from './withStartingIndicator';
|
||||
import { Progress } from 'reactstrap';
|
||||
|
||||
describe('Starting', () => {
|
|
@ -1,10 +1,9 @@
|
|||
import React, { FC, useEffect } from 'react';
|
||||
import { RouteComponentProps, navigate } from '@reach/router';
|
||||
import React, { FC, ComponentType } from 'react';
|
||||
import { Progress, Alert } from 'reactstrap';
|
||||
|
||||
import { useFetchReadyInterval } from '../../hooks/useFetch';
|
||||
import { WALReplayData } from '../../types/types';
|
||||
import { usePathPrefix } from '../../contexts/PathPrefixContext';
|
||||
import { useFetchReadyInterval } from '../hooks/useFetch';
|
||||
import { WALReplayData } from '../types/types';
|
||||
import { usePathPrefix } from '../contexts/PathPrefixContext';
|
||||
|
||||
interface StartingContentProps {
|
||||
isUnexpected: boolean;
|
||||
|
@ -44,17 +43,13 @@ export const StartingContent: FC<StartingContentProps> = ({ status, isUnexpected
|
|||
);
|
||||
};
|
||||
|
||||
const Starting: FC<RouteComponentProps> = () => {
|
||||
export const withStartingIndicator = <T extends {}>(Page: ComponentType<T>): FC<T> => ({ ...rest }) => {
|
||||
const pathPrefix = usePathPrefix();
|
||||
const { ready, walReplayStatus, isUnexpected } = useFetchReadyInterval(pathPrefix);
|
||||
|
||||
useEffect(() => {
|
||||
if (ready) {
|
||||
navigate('/');
|
||||
}
|
||||
}, [ready]);
|
||||
if (ready || isUnexpected) {
|
||||
return <Page {...(rest as T)} />;
|
||||
}
|
||||
|
||||
return <StartingContent isUnexpected={isUnexpected} status={walReplayStatus.data} />;
|
||||
};
|
||||
|
||||
export default Starting;
|
|
@ -47,36 +47,7 @@ export const useFetch = <T extends {}>(url: string, options?: RequestInit): Fetc
|
|||
return { response, error, isLoading };
|
||||
};
|
||||
|
||||
export const useFetchReady = (pathPrefix: string, options?: RequestInit): FetchStateReady => {
|
||||
const [ready, setReady] = useState<boolean>(false);
|
||||
const [isUnexpected, setIsUnexpected] = useState<boolean>(false);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const res = await fetch(`${pathPrefix}/-/ready`, { cache: 'no-store', credentials: 'same-origin', ...options });
|
||||
if (res.status === 200) {
|
||||
setReady(true);
|
||||
}
|
||||
// The server sends back a 503 if it isn't ready,
|
||||
// if we get back anything else that means something has gone wrong.
|
||||
if (res.status !== 503) {
|
||||
setIsUnexpected(true);
|
||||
} else {
|
||||
setIsUnexpected(false);
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
} catch (error) {
|
||||
setIsUnexpected(true);
|
||||
}
|
||||
};
|
||||
fetchData();
|
||||
}, [pathPrefix, options]);
|
||||
return { ready, isUnexpected, isLoading };
|
||||
};
|
||||
let wasReady = false;
|
||||
|
||||
// This is used on the starting page to periodically check if the server is ready yet,
|
||||
// and check the status of the WAL replay.
|
||||
|
@ -86,33 +57,60 @@ export const useFetchReadyInterval = (pathPrefix: string, options?: RequestInit)
|
|||
const [walReplayStatus, setWALReplayStatus] = useState<WALReplayStatus>({} as any);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
let res = await fetch(`${pathPrefix}/-/ready`, { cache: 'no-store', credentials: 'same-origin', ...options });
|
||||
if (res.status === 200) {
|
||||
setReady(true);
|
||||
if (wasReady) {
|
||||
setReady(true);
|
||||
} else {
|
||||
// This helps avoid a memory leak.
|
||||
let mounted = true;
|
||||
|
||||
const fetchStatus = async () => {
|
||||
try {
|
||||
let res = await fetch(`${pathPrefix}/-/ready`, { cache: 'no-store', credentials: 'same-origin', ...options });
|
||||
if (res.status === 200) {
|
||||
if (mounted) {
|
||||
setReady(true);
|
||||
}
|
||||
wasReady = true;
|
||||
clearInterval(interval);
|
||||
} else if (res.status !== 503) {
|
||||
if (mounted) {
|
||||
setIsUnexpected(true);
|
||||
}
|
||||
clearInterval(interval);
|
||||
return;
|
||||
} else {
|
||||
if (mounted) {
|
||||
setIsUnexpected(false);
|
||||
}
|
||||
|
||||
res = await fetch(`${pathPrefix}/${API_PATH}/status/walreplay`, {
|
||||
cache: 'no-store',
|
||||
credentials: 'same-origin',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = (await res.json()) as WALReplayStatus;
|
||||
if (mounted) {
|
||||
setWALReplayStatus(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (mounted) {
|
||||
setIsUnexpected(true);
|
||||
}
|
||||
clearInterval(interval);
|
||||
return;
|
||||
}
|
||||
if (res.status !== 503) {
|
||||
setIsUnexpected(true);
|
||||
setWALReplayStatus({ data: { last: 0, first: 0 } } as any);
|
||||
} else {
|
||||
setIsUnexpected(false);
|
||||
};
|
||||
|
||||
res = await fetch(`${pathPrefix}/${API_PATH}/status/walreplay`, { cache: 'no-store', credentials: 'same-origin' });
|
||||
if (res.ok) {
|
||||
const data = (await res.json()) as WALReplayStatus;
|
||||
setWALReplayStatus(data);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
setIsUnexpected(true);
|
||||
setWALReplayStatus({ data: { last: 0, first: 0 } } as any);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
fetchStatus();
|
||||
const interval = setInterval(fetchStatus, 1000);
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
mounted = false;
|
||||
};
|
||||
}
|
||||
}, [pathPrefix, options]);
|
||||
|
||||
return { ready, isUnexpected, walReplayStatus };
|
||||
};
|
||||
|
|
|
@ -7,6 +7,27 @@ import Status from './status/Status';
|
|||
import Targets from './targets/Targets';
|
||||
import PanelList from './graph/PanelList';
|
||||
import TSDBStatus from './tsdbStatus/TSDBStatus';
|
||||
import Starting from './starting/Starting';
|
||||
import { withStartingIndicator } from '../components/withStartingIndicator';
|
||||
|
||||
export { Alerts, Config, Flags, Rules, ServiceDiscovery, Status, Targets, TSDBStatus, PanelList, Starting };
|
||||
const AlertsPage = withStartingIndicator(Alerts);
|
||||
const ConfigPage = withStartingIndicator(Config);
|
||||
const FlagsPage = withStartingIndicator(Flags);
|
||||
const RulesPage = withStartingIndicator(Rules);
|
||||
const ServiceDiscoveryPage = withStartingIndicator(ServiceDiscovery);
|
||||
const StatusPage = withStartingIndicator(Status);
|
||||
const TSDBStatusPage = withStartingIndicator(TSDBStatus);
|
||||
const TargetsPage = withStartingIndicator(Targets);
|
||||
const PanelListPage = withStartingIndicator(PanelList);
|
||||
|
||||
// prettier-ignore
|
||||
export {
|
||||
AlertsPage,
|
||||
ConfigPage,
|
||||
FlagsPage,
|
||||
RulesPage,
|
||||
ServiceDiscoveryPage,
|
||||
StatusPage,
|
||||
TSDBStatusPage,
|
||||
TargetsPage,
|
||||
PanelListPage
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue