eeadeef664
CI / checks (pull_request) Successful in 26m59s
Console Home counted every available service through the generic resource list, but MSK and MSK Connect are available through their own pre-SPI pages with no adapter behind /resources, so each answered 501 and the Resources tile read "Resource load failed". Service descriptors now say whether the generic list serves them (listable, true when an adapter is registered), and Console Home counts only those; the MSK cards still link to their pages.
119 lines
4.9 KiB
TypeScript
119 lines
4.9 KiB
TypeScript
import {useMemo} from 'react'
|
|
import {useQueries} from '@tanstack/react-query'
|
|
import {listCloudResources} from '@/api/cloudProxyClient'
|
|
import {
|
|
cloudQueryKeys,
|
|
useCloudServicesQuery,
|
|
useCloudStatusQuery,
|
|
useCloudsQuery,
|
|
} from '@/api/queries/cloudQueries'
|
|
import {
|
|
resourceDetailFor,
|
|
runtimeClassFor,
|
|
runtimeDetailFor,
|
|
runtimeEndpointLabel,
|
|
runtimeLabelFor,
|
|
serviceMetaLabel,
|
|
} from './cloudConsoleHome.utils'
|
|
import type {CloudProvider} from '@/types/cloud'
|
|
import type {ConsoleServiceCard} from './types'
|
|
|
|
/**
|
|
* Console home is driven entirely by `GET /clouds/:cloud/services`.
|
|
*
|
|
* It previously hardcoded three resource queries, spliced in a Secrets Manager
|
|
* card for AWS with a hardcoded "available", and appended two permanent
|
|
* placeholder cards — so it could disagree with both the sidebar and the API.
|
|
*/
|
|
export function useCloudConsoleHomeData(cloud: CloudProvider) {
|
|
const cloudsQuery = useCloudsQuery()
|
|
const servicesQuery = useCloudServicesQuery(cloud)
|
|
const statusQuery = useCloudStatusQuery(cloud)
|
|
const status = statusQuery.data
|
|
const services = useMemo(() => servicesQuery.data ?? [], [servicesQuery.data])
|
|
|
|
// Only a service with a registered adapter has the generic list endpoint. Route
|
|
// shape only decides where the card links: a legacy absolute-route page (AWS
|
|
// Secrets Manager) is still counted through its adapter, while MSK, available
|
|
// through its own pre-SPI page, is not listable and is never counted.
|
|
const countable = useMemo(
|
|
() => services.filter((service) => service.availability === 'available' && service.listable),
|
|
[services],
|
|
)
|
|
|
|
const runtimeReachable = status?.runtime === 'reachable'
|
|
const countQueries = useQueries({
|
|
queries: countable.map((service) => ({
|
|
queryKey: cloudQueryKeys.resources(cloud, service.service),
|
|
queryFn: ({signal}: {signal?: AbortSignal}) =>
|
|
listCloudResources(cloud, service.service, undefined, signal),
|
|
enabled: runtimeReachable,
|
|
staleTime: 30_000,
|
|
})),
|
|
})
|
|
|
|
const countsByService = useMemo(() => {
|
|
const map = new Map<string, {count?: number; isLoading: boolean; isError: boolean}>()
|
|
countable.forEach((service, index) => {
|
|
const query = countQueries[index]
|
|
map.set(service.service, {
|
|
count: query?.data?.length,
|
|
isLoading: query?.isLoading ?? false,
|
|
isError: query?.isError ?? false,
|
|
})
|
|
})
|
|
return map
|
|
}, [countable, countQueries])
|
|
|
|
const serviceCards = useMemo<ConsoleServiceCard[]>(
|
|
() =>
|
|
services.map((service): ConsoleServiceCard => {
|
|
const counts = countsByService.get(service.service)
|
|
const isLegacyPage = service.route.startsWith('/')
|
|
return {
|
|
id: service.service,
|
|
label: service.displayName,
|
|
status: service.availability,
|
|
count: counts?.count,
|
|
cloud,
|
|
iconKey: service.iconKey,
|
|
route:
|
|
service.availability === 'available'
|
|
? isLegacyPage
|
|
? service.route
|
|
: `/cloud-explorer/${cloud}/${service.route}`
|
|
: undefined,
|
|
meta:
|
|
service.availability === 'available'
|
|
? isLegacyPage || !service.listable
|
|
? 'open service'
|
|
: serviceMetaLabel(status, counts?.isLoading ?? false, 'resources')
|
|
: 'coming soon',
|
|
}
|
|
}),
|
|
[cloud, countsByService, services, status],
|
|
)
|
|
|
|
const resourcesLoading = countQueries.some((query) => query.isLoading)
|
|
const resourcesError = countQueries.some((query) => query.isError)
|
|
const resourceCount = countQueries.reduce((total, query) => total + (query.data?.length ?? 0), 0)
|
|
const activeServices = services.filter((service) => service.availability === 'available').length
|
|
|
|
return {
|
|
cloudsQuery,
|
|
status,
|
|
runtimeLabel: runtimeEndpointLabel(status),
|
|
runtimeState: runtimeLabelFor(status, statusQuery.isLoading),
|
|
runtimeClass: runtimeClassFor(status, statusQuery.isLoading),
|
|
runtimeDetail: status?.error ?? runtimeDetailFor(cloud, status),
|
|
activeServices,
|
|
// Counted rather than asserted: the old copy claimed a fixed service list.
|
|
activeServicesDetail: servicesQuery.isSuccess
|
|
? `${activeServices} of ${services.length} services available`
|
|
: 'Loading services',
|
|
resourceCount,
|
|
resourceDetail: resourceDetailFor(status, statusQuery.isLoading, resourcesLoading, resourcesError),
|
|
serviceCards,
|
|
}
|
|
}
|