diff --git a/packages/manager/.changeset/pr-10428-tech-stories-1714573890061.md b/packages/manager/.changeset/pr-10428-tech-stories-1714573890061.md new file mode 100644 index 00000000000..48f4d5e9a69 --- /dev/null +++ b/packages/manager/.changeset/pr-10428-tech-stories-1714573890061.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tech Stories +--- + +Query Key Factory for Kubernetes ([#10428](https://github.com/linode/manager/pull/10428)) diff --git a/packages/manager/src/hooks/useInitialRequests.ts b/packages/manager/src/hooks/useInitialRequests.ts index 59d79e63552..25202e587bf 100644 --- a/packages/manager/src/hooks/useInitialRequests.ts +++ b/packages/manager/src/hooks/useInitialRequests.ts @@ -4,7 +4,8 @@ import * as React from 'react'; import { useAuthentication } from 'src/hooks/useAuthentication'; import { usePendingUpload } from 'src/hooks/usePendingUpload'; -import { queries } from 'src/queries'; +import { accountQueries } from 'src/queries/account/queries'; +import { profileQueries } from 'src/queries/profile'; import { redirectToLogin } from 'src/session'; /** @@ -59,13 +60,13 @@ export const useInitialRequests = () => { // Initial Requests: Things we need immediately (before rendering the app) const dataFetchingPromises: Promise[] = [ // Fetch user's account information - queryClient.prefetchQuery(queries.account.account), + queryClient.prefetchQuery(accountQueries.account), // Is a user managed - queryClient.prefetchQuery(queries.account.settings), + queryClient.prefetchQuery(accountQueries.settings), // Username and whether a user is restricted - queryClient.prefetchQuery(queries.profile.profile()), + queryClient.prefetchQuery(profileQueries.profile()), // preferences queryClient.prefetchQuery({ diff --git a/packages/manager/src/queries/index.ts b/packages/manager/src/queries/index.ts deleted file mode 100644 index ed67ce2c700..00000000000 --- a/packages/manager/src/queries/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { mergeQueryKeys } from '@lukemorales/query-key-factory'; - -import { accountQueries } from './account/queries'; -import { betaQueries } from './betas'; -import { profileQueries } from './profile'; -import { regionQueries } from './regions/regions'; - -/** - * `queries` contains *all* query keys for Cloud Manager. - * - * @example - * import { queries } from 'src/queries'; - * - * queryClient.invalidateQueries(queries.account.settings.queryKey); - */ -export const queries = mergeQueryKeys( - profileQueries, - accountQueries, - betaQueries, - regionQueries -); diff --git a/packages/manager/src/queries/kubernetes.ts b/packages/manager/src/queries/kubernetes.ts index 55b58a48b03..dbae81e7de1 100644 --- a/packages/manager/src/queries/kubernetes.ts +++ b/packages/manager/src/queries/kubernetes.ts @@ -31,137 +31,160 @@ import { Params, ResourcePage, } from '@linode/api-v4/lib/types'; +import { createQueryKeys } from '@lukemorales/query-key-factory'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { getAll } from 'src/utilities/getAll'; -import { queryPresets, updateInPaginatedStore } from './base'; +import { queryPresets } from './base'; import { profileQueries } from './profile'; -export const queryKey = `kubernetes`; +export const kubernetesQueries = createQueryKeys('kubernetes', { + cluster: (id: number) => ({ + contextQueries: { + dashboard: { + queryFn: () => getKubernetesClusterDashboard(id), + queryKey: null, + }, + endpoints: { + queryFn: () => getAllAPIEndpointsForCluster(id), + queryKey: null, + }, + kubeconfig: { + queryFn: async () => { + const result = await getKubeConfig(id); + return window.atob(result.kubeconfig); + }, + queryKey: null, + }, + pools: { + queryFn: () => getAllNodePoolsForCluster(id), + queryKey: null, + }, + }, + queryFn: () => getKubernetesCluster(id), + queryKey: [id], + }), + lists: { + contextQueries: { + all: { + queryFn: () => getAllKubernetesClusters(), + queryKey: null, + }, + paginated: (params: Params, filter: Filter) => ({ + queryFn: () => getKubernetesClusters(params, filter), + queryKey: [params, filter], + }), + }, + queryKey: null, + }, + versions: { + queryFn: () => getAllKubernetesVersions(), + queryKey: null, + }, +}); export const useKubernetesClustersQuery = (params: Params, filter: Filter) => { - return useQuery, APIError[]>( - [`${queryKey}-list`, params, filter], - () => getKubernetesClusters(params, filter), - { keepPreviousData: true } - ); + return useQuery, APIError[]>({ + ...kubernetesQueries.lists._ctx.paginated(params, filter), + keepPreviousData: true, + }); }; export const useKubernetesClusterQuery = (id: number) => { - return useQuery( - [queryKey, 'cluster', id], - () => getKubernetesCluster(id) - ); + return useQuery(kubernetesQueries.cluster(id)); }; export const useKubernetesClusterMutation = (id: number) => { const queryClient = useQueryClient(); return useMutation>( - (data) => updateKubernetesCluster(id, data), { + mutationFn: (data) => updateKubernetesCluster(id, data), onSuccess(data) { - updateInPaginatedStore( - [`${queryKey}-list`], - id, - data, - queryClient - ); - queryClient.setQueryData([queryKey, 'cluster', id], data); + queryClient.invalidateQueries({ + queryKey: kubernetesQueries.lists.queryKey, + }); + queryClient.setQueryData(kubernetesQueries.cluster(id).queryKey, data); }, } ); }; export const useAllKubernetesClusterAPIEndpointsQuery = (id: number) => { - return useQuery( - [queryKey, 'cluster', id, 'endpoints'], - () => getAllAPIEndpointsForCluster(id), - { - keepPreviousData: true, - refetchOnMount: true, - retry: true, - retryDelay: 5000, - } - ); + return useQuery({ + ...kubernetesQueries.cluster(id)._ctx.endpoints, + keepPreviousData: true, + refetchOnMount: true, + retry: true, + retryDelay: 5000, + }); }; -const getAllAPIEndpointsForCluster = (clusterId: number) => - getAll((params, filters) => - getKubernetesClusterEndpoints(clusterId, params, filters) - )().then((data) => data.data); - export const useKubenetesKubeConfigQuery = ( clusterId: number, enabled = false ) => - useQuery( - [queryKey, 'cluster', clusterId, 'kubeconfig'], - async () => { - const result = await getKubeConfig(clusterId); - return window.atob(result.kubeconfig); - }, - { - enabled, - refetchOnMount: true, - retry: true, - retryDelay: 5000, - } - ); + useQuery({ + ...kubernetesQueries.cluster(clusterId)._ctx.kubeconfig, + enabled, + refetchOnMount: true, + retry: true, + retryDelay: 5000, + }); export const useResetKubeConfigMutation = () => { const queryClient = useQueryClient(); - return useMutation<{}, APIError[], { id: number }>( - ({ id }) => resetKubeConfig(id), - { - onSuccess(_, { id }) { - queryClient.removeQueries([queryKey, 'cluster', id, 'kubeconfig']); - }, - } - ); + return useMutation<{}, APIError[], { id: number }>({ + mutationFn: ({ id }) => resetKubeConfig(id), + onSuccess(_, { id }) { + queryClient.removeQueries({ + queryKey: kubernetesQueries.cluster(id)._ctx.kubeconfig.queryKey, + }); + }, + }); }; export const useDeleteKubernetesClusterMutation = () => { const queryClient = useQueryClient(); - return useMutation<{}, APIError[], { id: number }>( - ({ id }) => deleteKubernetesCluster(id), - { - onSuccess() { - queryClient.invalidateQueries([`${queryKey}-list`]); - }, - } - ); + return useMutation<{}, APIError[], { id: number }>({ + mutationFn: ({ id }) => deleteKubernetesCluster(id), + onSuccess(data, variables) { + queryClient.removeQueries({ + queryKey: kubernetesQueries.cluster(variables.id).queryKey, + }); + queryClient.invalidateQueries({ + queryKey: kubernetesQueries.lists.queryKey, + }); + }, + }); }; export const useCreateKubernetesClusterMutation = () => { const queryClient = useQueryClient(); - return useMutation( - createKubernetesCluster, - { - onSuccess() { - queryClient.invalidateQueries([`${queryKey}-list`]); - // If a restricted user creates an entity, we must make sure grants are up to date. - queryClient.invalidateQueries(profileQueries.grants.queryKey); - }, - } - ); + return useMutation({ + mutationFn: createKubernetesCluster, + onSuccess() { + queryClient.invalidateQueries({ + queryKey: kubernetesQueries.lists.queryKey, + }); + // If a restricted user creates an entity, we must make sure grants are up to date. + queryClient.invalidateQueries({ + queryKey: profileQueries.grants.queryKey, + }); + }, + }); }; export const useCreateNodePoolMutation = (clusterId: number) => { const queryClient = useQueryClient(); - return useMutation( - (data) => createNodePool(clusterId, data), - { - onSuccess() { - queryClient.invalidateQueries([ - queryKey, - 'cluster', - clusterId, - 'pools', - ]); - }, - } - ); + return useMutation({ + mutationFn: (data) => createNodePool(clusterId, data), + onSuccess() { + queryClient.invalidateQueries({ + queryKey: kubernetesQueries.cluster(clusterId)._ctx.pools.queryKey, + }); + }, + }); }; export const useUpdateNodePoolMutation = ( @@ -173,9 +196,12 @@ export const useUpdateNodePoolMutation = ( KubeNodePoolResponse, APIError[], Partial - >((data) => updateNodePool(clusterId, poolId, data), { + >({ + mutationFn: (data) => updateNodePool(clusterId, poolId, data), onSuccess() { - queryClient.invalidateQueries([queryKey, 'cluster', clusterId, 'pools']); + queryClient.invalidateQueries({ + queryKey: kubernetesQueries.cluster(clusterId)._ctx.pools.queryKey, + }); }, }); }; @@ -185,9 +211,12 @@ export const useDeleteNodePoolMutation = ( poolId: number ) => { const queryClient = useQueryClient(); - return useMutation<{}, APIError[]>(() => deleteNodePool(clusterId, poolId), { + return useMutation<{}, APIError[]>({ + mutationFn: () => deleteNodePool(clusterId, poolId), onSuccess() { - queryClient.invalidateQueries([queryKey, 'cluster', clusterId, 'pools']); + queryClient.invalidateQueries({ + queryKey: kubernetesQueries.cluster(clusterId)._ctx.pools.queryKey, + }); }, }); }; @@ -197,27 +226,36 @@ export const useRecycleNodePoolMutation = ( poolId: number ) => { const queryClient = useQueryClient(); - return useMutation<{}, APIError[]>(() => recycleAllNodes(clusterId, poolId), { + return useMutation<{}, APIError[]>({ + mutationFn: () => recycleAllNodes(clusterId, poolId), onSuccess() { - queryClient.invalidateQueries([queryKey, 'cluster', clusterId, 'pools']); + queryClient.invalidateQueries({ + queryKey: kubernetesQueries.cluster(clusterId)._ctx.pools.queryKey, + }); }, }); }; export const useRecycleNodeMutation = (clusterId: number, nodeId: string) => { const queryClient = useQueryClient(); - return useMutation<{}, APIError[]>(() => recycleNode(clusterId, nodeId), { + return useMutation<{}, APIError[]>({ + mutationFn: () => recycleNode(clusterId, nodeId), onSuccess() { - queryClient.invalidateQueries([queryKey, 'cluster', clusterId, 'pools']); + queryClient.invalidateQueries({ + queryKey: kubernetesQueries.cluster(clusterId)._ctx.pools.queryKey, + }); }, }); }; export const useRecycleClusterMutation = (clusterId: number) => { const queryClient = useQueryClient(); - return useMutation<{}, APIError[]>(() => recycleClusterNodes(clusterId), { + return useMutation<{}, APIError[]>({ + mutationFn: () => recycleClusterNodes(clusterId), onSuccess() { - queryClient.invalidateQueries([queryKey, 'cluster', clusterId, 'pools']); + queryClient.invalidateQueries({ + queryKey: kubernetesQueries.cluster(clusterId)._ctx.pools.queryKey, + }); }, }); }; @@ -226,20 +264,35 @@ export const useAllKubernetesNodePoolQuery = ( clusterId: number, options?: { enabled?: boolean; refetchInterval?: number } ) => { - return useQuery( - [queryKey, 'cluster', clusterId, 'pools'], - () => getAllNodePoolsForCluster(clusterId), - options - ); + return useQuery({ + ...kubernetesQueries.cluster(clusterId)._ctx.pools, + ...options, + }); }; export const useKubernetesDashboardQuery = (clusterId: number) => { return useQuery( - [queryKey, 'cluster', clusterId, 'dashboard'], - () => getKubernetesClusterDashboard(clusterId) + kubernetesQueries.cluster(clusterId)._ctx.dashboard ); }; +export const useKubernetesVersionQuery = () => + useQuery({ + ...kubernetesQueries.versions, + ...queryPresets.oneTimeFetch, + }); + +/** + * Avoiding fetching all Kubernetes Clusters if possible. + * Before you use this, consider implementing infinite scroll insted. + */ +export const useAllKubernetesClustersQuery = (enabled = false) => { + return useQuery({ + ...kubernetesQueries.lists._ctx.all, + enabled, + }); +}; + const getAllNodePoolsForCluster = (clusterId: number) => getAll((params, filters) => getNodePools(clusterId, params, filters) @@ -255,20 +308,7 @@ const getAllKubernetesVersions = () => getKubernetesVersions(params, filters) )().then((data) => data.data); -export const useKubernetesVersionQuery = () => - useQuery( - [queryKey, 'versions'], - getAllKubernetesVersions, - queryPresets.oneTimeFetch - ); - -/** - * Please avoid using thie fetch-all query - */ -export const useAllKubernetesClustersQuery = (enabled = false) => { - return useQuery( - [`${queryKey}-all`], - getAllKubernetesClusters, - { enabled } - ); -}; +const getAllAPIEndpointsForCluster = (clusterId: number) => + getAll((params, filters) => + getKubernetesClusterEndpoints(clusterId, params, filters) + )().then((data) => data.data);