Skip to content

Commit

Permalink
frontend: Use react-query for backend requests, api v2
Browse files Browse the repository at this point in the history
This commit replaces fetch calls with react-query, and introduces v2 of
api client code, simplifying the way we do requests to the backend

Signed-off-by: Oleksandr Dubenko <[email protected]>
  • Loading branch information
sniok committed Sep 2, 2024
1 parent 06f5352 commit e07139c
Show file tree
Hide file tree
Showing 68 changed files with 1,491 additions and 408 deletions.
20 changes: 17 additions & 3 deletions frontend/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,34 @@ import { initialize, mswLoader } from 'msw-storybook-addon';
import '../src/index.css';
import { Title, Subtitle, Description, Primary, Controls } from '@storybook/blocks';
import { baseMocks } from './baseMocks';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

// https://github.com/mswjs/msw-storybook-addon
initialize({
onUnhandledRequest: 'warn',
waitUntilReady: true,
});

export const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnMount: 'always',
staleTime: 0,
retry: false,
gcTime: 0,
},
},
});

const withThemeProvider = (Story: any, context: any) => {
const theme = themesConf[context.globals.backgrounds?.value === '#1f1f1f' ? 'dark' : 'light'];

const ourThemeProvider = (
<ThemeProvider theme={theme}>
<Story {...context} />
</ThemeProvider>
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={theme}>
<Story {...context} />
</ThemeProvider>
</QueryClientProvider>
);
return ourThemeProvider;
};
Expand Down
75 changes: 68 additions & 7 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@
"@mui/lab": "^5.0.0-alpha.152",
"@mui/material": "^5.15.14",
"@mui/system": "^5.15.14",
"@mui/x-date-pickers": "^7.15.0",
"@mui/x-tree-view": "^6.17.0",
"@reduxjs/toolkit": "^1.9.3",
"@storybook/blocks": "^8.2.9",
"@tanstack/react-query": "^5.51.24",
"@tanstack/react-query-devtools": "^5.51.24",
"@testing-library/dom": "^10.1.0",
"@testing-library/react": "^16.0.0",
"@types/glob": "^8.1.0",
Expand Down Expand Up @@ -109,6 +112,11 @@
"vitest": "^2.0.5",
"cheerio": "1.0.0-rc.12"
},
"optionalDependencies": {
"@rollup/rollup-darwin-arm64": "*",
"@rollup/rollup-linux-x64-gnu": "*",
"@rollup/rollup-win32-x64-msvc": "*"
},
"scripts": {
"prestart": "npm run make-version",
"start": "cross-env REACT_APP_HEADLAMP_BACKEND_TOKEN=headlamp vite",
Expand Down
22 changes: 19 additions & 3 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import './i18n/config';
import './components/App/icons';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import React from 'react';
import { I18nextProvider } from 'react-i18next';
import { Provider } from 'react-redux';
Expand Down Expand Up @@ -29,13 +31,27 @@ function AppWithRedux(props: React.PropsWithChildren<{}>) {
);
}

const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 3 * 60_000,
refetchOnWindowFocus: false,
},
},
});
const queryDevtoolsEnabled = false;

function App() {
return (
<ErrorBoundary fallback={<ErrorComponent />}>
<Provider store={store}>
<AppWithRedux>
<AppContainer />
</AppWithRedux>
<QueryClientProvider client={queryClient}>
{queryDevtoolsEnabled && <ReactQueryDevtools initialIsOpen={false} />}

<AppWithRedux>
<AppContainer />
</AppWithRedux>
</QueryClientProvider>
</Provider>
</ErrorBoundary>
);
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/App/Home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ export default function Home() {
return null;
}

return <HomeComponent clusters={clusters} />;
return <HomeComponent clusters={clusters} key={Object.keys(clusters).join('')} />;
}

interface HomeComponentProps {
Expand All @@ -183,7 +183,7 @@ function HomeComponent(props: HomeComponentProps) {
const { t } = useTranslation(['translation', 'glossary']);
const [versions, errors] = useClustersVersion(Object.values(clusters));
const maxWarnings = 50;
const warningsMap = Event.useWarningList(Object.values(customNameClusters).map(c => c.name));
const warningsMap = Event.useWarningListQuery(Object.values(customNameClusters).map(c => c.name));

function renderWarningsText(clusterName: string) {
const numWarnings =
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/App/Notifications/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export default function Notifications() {
const notifications = useTypedSelector(state => state.notifications.notifications);
const dispatch = useDispatch();
const clusters = useClustersConf();
const warnings = Event.useWarningList(
const warnings = Event.useWarningListQuery(
Object.values(clusters ?? {})?.map(c => c.name, {
queryParams: {
limit: defaultMaxNotificationsStored,
Expand Down
91 changes: 33 additions & 58 deletions frontend/src/components/Sidebar/VersionButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import { styled, useTheme } from '@mui/system';
import { useQuery } from '@tanstack/react-query';
import { useSnackbar } from 'notistack';
import React from 'react';
import { useTranslation } from 'react-i18next';
Expand All @@ -27,7 +28,6 @@ const VersionIcon = styled(Icon)({
export default function VersionButton() {
const isSidebarOpen = useTypedSelector(state => state.sidebar.isSidebarOpen);
const { enqueueSnackbar } = useSnackbar();
const [clusterVersion, setClusterVersion] = React.useState<StringDict | null>(null);
const cluster = useCluster();
const theme = useTheme();
const [open, setOpen] = React.useState(false);
Expand Down Expand Up @@ -62,68 +62,43 @@ export default function VersionButton() {
];
}

React.useEffect(
() => {
let stillAlive = true;
function fetchVersion() {
getVersion()
.then((results: StringDict) => {
if (!stillAlive) {
return;
}

setClusterVersion(results);
let versionChange = 0;
if (clusterVersion && results && results.gitVersion) {
versionChange = semver.compare(results.gitVersion, clusterVersion.gitVersion);
const { data: clusterVersion } = useQuery({
placeholderData: null as any,
queryKey: ['version', cluster ?? ''],
queryFn: () => {
return getVersion()
.then((results: StringDict) => {
let versionChange = 0;
if (clusterVersion && results && results.gitVersion) {
versionChange = semver.compare(results.gitVersion, clusterVersion.gitVersion);

let msg = '';
if (versionChange > 0) {
msg = t('translation|Cluster version upgraded to {{ gitVersion }}', {
gitVersion: results.gitVersion,
});
} else if (versionChange < 0) {
msg = t('translation|Cluster version downgraded to {{ gitVersion }}', {
gitVersion: results.gitVersion,
});
}

if (msg) {
enqueueSnackbar(msg, {
key: 'version',
preventDuplicate: true,
autoHideDuration: versionSnackbarHideTimeout,
variant: 'info',
});
}
let msg = '';
if (versionChange > 0) {
msg = t('translation|Cluster version upgraded to {{ gitVersion }}', {
gitVersion: results.gitVersion,
});
} else if (versionChange < 0) {
msg = t('translation|Cluster version downgraded to {{ gitVersion }}', {
gitVersion: results.gitVersion,
});
}
})
.catch((error: Error) => console.error('Getting the cluster version:', error));
}

if (!clusterVersion) {
fetchVersion();
}

const intervalHandler = setInterval(() => {
fetchVersion();
}, versionFetchInterval);
if (msg) {
enqueueSnackbar(msg, {
key: 'version',
preventDuplicate: true,
autoHideDuration: versionSnackbarHideTimeout,
variant: 'info',
});
}
}

return function cleanup() {
stillAlive = false;
clearInterval(intervalHandler);
};
return results;
})
.catch((error: Error) => console.error('Getting the cluster version:', error));
},
// eslint-disable-next-line
[clusterVersion]
);

// Use the location to make sure the version is changed, as it depends on the cluster
// (defined in the URL ATM).
// @todo: Update this if the active cluster management is changed.
React.useEffect(() => {
setClusterVersion(null);
}, [cluster]);
refetchInterval: versionFetchInterval,
});

function handleClose() {
setOpen(false);
Expand Down
10 changes: 6 additions & 4 deletions frontend/src/components/cluster/Overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import {
export default function Overview() {
const { t } = useTranslation(['translation']);

const [pods] = Pod.useList();
const [nodes] = Node.useList();
const { items: pods } = Pod.useListQuery();
const { items: nodes } = Node.useListQuery();

const [nodeMetrics, metricsError] = Node.useMetrics();

Expand Down Expand Up @@ -74,7 +74,9 @@ function EventsSection() {
)
)
);
const [events, eventsError] = Event.useList({ limit: Event.maxLimit });
const { items: events, error: eventsError } = Event.useListQuery({
queryParams: { limit: Event.maxLimit },
});

const warningActionFilterFunc = (event: Event, search?: string) => {
if (!filterFunc(event, search)) {
Expand Down Expand Up @@ -138,7 +140,7 @@ function EventsSection() {
}}
defaultGlobalFilter={eventsFilter ?? undefined}
data={events}
errorMessage={Event.getErrorMessage(eventsError)}
errorMessage={Event.getErrorMessage(eventsError as any)}
columns={[
{
label: t('Type'),
Expand Down
Loading

0 comments on commit e07139c

Please sign in to comment.