-
Notifications
You must be signed in to change notification settings - Fork 512
CNS-120 In Console, cluster list now pulls data from SUBSCRIBE #38014
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,7 @@ import useLatestOfflineReplica, { | |
| import { AppErrorBoundary } from "~/components/AppErrorBoundary"; | ||
| import { CodeBlock } from "~/components/copyableComponents"; | ||
| import DeleteObjectMenuItem from "~/components/DeleteObjectMenuItem"; | ||
| import ErrorBox from "~/components/ErrorBox"; | ||
| import { LoadingContainer } from "~/components/LoadingContainer"; | ||
| import OverflowMenu, { OVERFLOW_BUTTON_WIDTH } from "~/components/OverflowMenu"; | ||
| import { sortingFunctions } from "~/components/Table/tableColumnBuilders"; | ||
|
|
@@ -50,6 +51,7 @@ import { | |
| } from "~/layouts/listPageComponents"; | ||
| import docUrls from "~/mz-doc-urls.json"; | ||
| import { relativeClusterPath } from "~/platform/routeHelpers"; | ||
| import { useAllClusters } from "~/store/allClusters"; | ||
| import WarningIcon from "~/svg/WarningIcon"; | ||
| import { truncateMaxWidth } from "~/theme/components/Table"; | ||
| import { | ||
|
|
@@ -59,7 +61,7 @@ import { | |
|
|
||
| import AlterClusterMenuItem from "./AlterClusterMenuItem"; | ||
| import { CLUSTERS_FETCH_ERROR_MESSAGE } from "./constants"; | ||
| import { useClusters } from "./queries"; | ||
| import { useOwners } from "./queries"; | ||
| import { useShowSystemObjects } from "./useShowSystemObjects"; | ||
|
|
||
| const createClusterSuggestion = { | ||
|
|
@@ -73,7 +75,6 @@ const createClusterSuggestion = { | |
| * Read from `info.table.options.meta` and cast to this shape inside cells. | ||
| */ | ||
| interface ClusterTableMeta { | ||
| refetchClusters: () => void; | ||
| offlineReplicaMap: LatestOfflineReplicaMap | undefined; | ||
| } | ||
|
|
||
|
|
@@ -137,13 +138,7 @@ const LastStatusChangeCell = ({ | |
| ); | ||
| }; | ||
|
|
||
| const ClusterActionsCell = ({ | ||
| cluster, | ||
| refetchClusters, | ||
| }: { | ||
| cluster: ClusterWithOwnership; | ||
| refetchClusters: () => void; | ||
| }) => ( | ||
| const ClusterActionsCell = ({ cluster }: { cluster: ClusterWithOwnership }) => ( | ||
| <OverflowMenu | ||
| items={[ | ||
| { | ||
|
|
@@ -154,7 +149,8 @@ const ClusterActionsCell = ({ | |
| <DeleteObjectMenuItem | ||
| key="delete-object" | ||
| selectedObject={cluster} | ||
| onSuccessAction={refetchClusters} | ||
| // the subscribe drops the row from our list | ||
| onSuccessAction={() => undefined} | ||
| objectType="CLUSTER" | ||
| /> | ||
| </> | ||
|
|
@@ -210,15 +206,7 @@ const columns = [ | |
| columnHelper.display({ | ||
| id: "actions", | ||
| header: "", | ||
| cell: (info) => { | ||
| const meta = info.table.options.meta as ClusterTableMeta; | ||
| return ( | ||
| <ClusterActionsCell | ||
| cluster={info.row.original} | ||
| refetchClusters={meta.refetchClusters} | ||
| /> | ||
| ); | ||
| }, | ||
| cell: (info) => <ClusterActionsCell cluster={info.row.original} />, | ||
| enableSorting: false, | ||
| size: OVERFLOW_BUTTON_WIDTH, | ||
| }), | ||
|
|
@@ -229,20 +217,42 @@ const ClustersListContent = ({ | |
| }: { | ||
| showSystemObjects: boolean; | ||
| }) => { | ||
| const { data: clusters, refetch } = useClusters({ | ||
| includeSystemObjects: showSystemObjects, | ||
| }); | ||
| const { data: clusters, snapshotComplete, isError } = useAllClusters(); | ||
| const { data: ownersById, isPending: isOwnersPending } = useOwners(); | ||
|
|
||
| const orderedClusters = React.useMemo(() => { | ||
| if (!clusters) return []; | ||
| const systemClusters = clusters.filter((c) => isSystemCluster(c.id)); | ||
| const nonSystemClusters = clusters | ||
| const visibleClusters = clusters | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same filter + sort already exists in ClusterDetailBreadcrumbs. Maybe pull it into a helper in store/allClusters.ts, e.g. visibleClusters(clusters, showSystemObjects)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are a couple of minor differences between the two screens. On the cluster list page, in the default sort state, we push system clusters to the bottom of the list. In the menu on the detail page, we sort by name without separating the 2 cluster types. Also, on the List page, we filter out clusters that aren't owned by the user. We don't filter on the Detail page. I suggest we update the detail page menu to include the filtering and to separate the 2 types of cluster, but that's just my uninformed opinion.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A couple of clarifications: The list doesn't actually filter unowned clusters out. getOwners() returns every role with an isOwner boolean, so no rows are dropped, isOwner only gates the Alter/Delete items in the row's overflow menu. So there's no unowned filter to move up the chain. Moving ownership into the subscribe also isn't possible: On sorting, the difference is smaller than it looks: the table applies initialSorting: name asc, so in the default state rows are re-sorted by name and the list's system-last grouping only shows once a user cycles sorting off. Given that, I'd keep the helper to the shared core (system-visibility filter + name sort) and let the list layer its grouping on top. Grouping system clusters last in this menu too seems fine if you want it, no strong opinion from me either.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahhh gotcha OK. I see. I think I was conflating owned and system clusters. And yep, I realized that filtering higher in the chain didn't make sense after I re-read my comment. Thanks! |
||
| .filter((c) => showSystemObjects || !isSystemCluster(c.id)) | ||
| .map((c) => ({ | ||
| ...c, | ||
| // Treat an in-flight owners query as non-owner so owner-only menu items | ||
| // stay hidden until ownership is known. | ||
| isOwner: !isOwnersPending && (ownersById?.get(c.ownerId) ?? false), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do you think we could move this check into |
||
| })); | ||
| // The subscribe upserts by id, so the atom's order is arbitrary. Sort each | ||
| // group by name and keep system clusters at the end. | ||
| const byName = (a: ClusterWithOwnership, b: ClusterWithOwnership) => | ||
| a.name.localeCompare(b.name); | ||
| const systemClusters = visibleClusters | ||
| .filter((c) => isSystemCluster(c.id)) | ||
| .sort(byName); | ||
| const nonSystemClusters = visibleClusters | ||
| .filter((c) => !isSystemCluster(c.id)) | ||
| .sort((a, b) => a.name.localeCompare(b.name)); | ||
| .sort(byName); | ||
| return [...nonSystemClusters, ...systemClusters]; | ||
| }, [clusters]); | ||
| }, [clusters, isOwnersPending, ownersById, showSystemObjects]); | ||
|
|
||
| if (isError) { | ||
| return <ErrorBox message={CLUSTERS_FETCH_ERROR_MESSAGE} />; | ||
| } | ||
|
|
||
| // The atom starts out empty, so the empty state has to wait for the snapshot | ||
| // or it would flash before the first rows arrive. | ||
| if (!snapshotComplete) { | ||
| return <LoadingContainer />; | ||
| } | ||
|
|
||
| if (clusters !== null && clusters.length === 0) { | ||
| if (orderedClusters.length === 0) { | ||
| return ( | ||
| <EmptyListWrapper> | ||
| <EmptyListHeader> | ||
|
|
@@ -270,19 +280,18 @@ const ClustersListContent = ({ | |
| ); | ||
| } | ||
|
|
||
| return <ClusterTable clusters={orderedClusters} refetchClusters={refetch} />; | ||
| return <ClusterTable clusters={orderedClusters} />; | ||
| }; | ||
|
|
||
| interface ClusterTableProps { | ||
| clusters: ClusterWithOwnership[]; | ||
| refetchClusters: () => void; | ||
| } | ||
|
|
||
| const ClusterTable = ({ clusters, refetchClusters }: ClusterTableProps) => { | ||
| const ClusterTable = ({ clusters }: ClusterTableProps) => { | ||
| const { data: offlineReplicaMap, error: offlineReplicaError } = | ||
| useLatestOfflineReplica(); | ||
|
|
||
| const meta: ClusterTableMeta = { refetchClusters, offlineReplicaMap }; | ||
| const meta: ClusterTableMeta = { offlineReplicaMap }; | ||
|
|
||
| const table = useUniversalTable({ | ||
| data: clusters, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The system-cluster filtering is client-side logic now, so it'd be good to cover it: seed the atom with an s-id cluster plus user clusters and assert it's hidden by default / shown with the toggle.