diff --git a/frontend/packages/console-app/src/components/nodes/NodeTerminal.tsx b/frontend/packages/console-app/src/components/nodes/NodeTerminal.tsx index 3fc679fcad..8b4de42f0f 100644 --- a/frontend/packages/console-app/src/components/nodes/NodeTerminal.tsx +++ b/frontend/packages/console-app/src/components/nodes/NodeTerminal.tsx @@ -1,11 +1,10 @@ import type { ReactNode, FC } from 'react'; -import { useState, useEffect } from 'react'; +import { useState, useEffect, useMemo } from 'react'; import { Alert } from '@patternfly/react-core'; import { useTranslation, Trans } from 'react-i18next'; import { PodConnectLoader } from '@console/internal/components/pod'; -import { Firehose } from '@console/internal/components/utils/firehose'; +import { useK8sWatchResource } from '@console/internal/components/utils/k8s-watch-hook'; import { LoadingBox } from '@console/internal/components/utils/status-box'; -import type { FirehoseResource, FirehoseResult } from '@console/internal/components/utils/types'; import { ImageStreamTagModel, NamespaceModel, PodModel } from '@console/internal/models'; import type { NodeKind, PodKind } from '@console/internal/module/k8s'; import { k8sCreate, k8sGet, k8sKillByName } from '@console/internal/module/k8s'; @@ -16,7 +15,9 @@ type NodeTerminalErrorProps = { }; type NodeTerminalInnerProps = { - obj?: FirehoseResult; + pod?: PodKind; + loaded: boolean; + loadError?: unknown; }; type NodeTerminalProps = { @@ -125,7 +126,7 @@ const NodeTerminalError: FC = ({ error }) => { ); }; -const NodeTerminalInner: FC = ({ obj }) => { +const NodeTerminalInner: FC = ({ pod, loaded, loadError }) => { const { t } = useTranslation(); const message = ( @@ -134,32 +135,57 @@ const NodeTerminalInner: FC = ({ obj }) => {

); - switch (obj?.data?.status?.phase) { + + if (loadError instanceof Error) { + return ; + } + + if (!loaded || !pod) { + return ; + } + + switch (pod?.status?.phase) { case 'Failed': return ( {t('console-app~The debug pod failed. ')} - {obj?.data?.status?.containerStatuses?.[0]?.state?.terminated?.message || - obj?.data?.status?.message} + {pod?.status?.containerStatuses?.[0]?.state?.terminated?.message || + pod?.status?.message} } /> ); case 'Running': - return ; + return ; default: return ; } }; const NodeTerminal: FC = ({ obj: node }) => { - const [resources, setResources] = useState([]); + const [podName, setPodName] = useState(''); + const [podNamespace, setPodNamespace] = useState(''); const [errorMessage, setErrorMessage] = useState(''); const nodeName = node.metadata.name; const isWindows = node.status?.nodeInfo?.operatingSystem === 'windows'; + const watchResource = useMemo( + () => + podName && podNamespace + ? { + isList: false, + kind: 'Pod', + name: podName, + namespace: podNamespace, + } + : null, + [podName, podNamespace], + ); + + const [pod, loaded, loadError] = useK8sWatchResource(watchResource); + useEffect(() => { let namespace; const name = `${nodeName?.replace(/\./g, '-')}-debug`; @@ -197,15 +223,8 @@ const NodeTerminal: FC = ({ obj: node }) => { await new Promise((resolve) => setTimeout(resolve, 1000)); const debugPod = await k8sCreate(PodModel, podToCreate); if (debugPod) { - setResources([ - { - isList: false, - kind: 'Pod', - name, - namespace: namespace.metadata.name, - prop: 'obj', - }, - ]); + setPodName(name); + setPodNamespace(namespace.metadata.name); } } catch (e) { setErrorMessage(e.message); @@ -225,9 +244,7 @@ const NodeTerminal: FC = ({ obj: node }) => { return errorMessage ? ( ) : ( - - - + ); }; diff --git a/frontend/packages/container-security/src/components/image-manifest-vuln.tsx b/frontend/packages/container-security/src/components/image-manifest-vuln.tsx index 83d15f4bea..8ce2c7ff5f 100644 --- a/frontend/packages/container-security/src/components/image-manifest-vuln.tsx +++ b/frontend/packages/container-security/src/components/image-manifest-vuln.tsx @@ -25,16 +25,15 @@ import { ListPage, } from '@console/internal/components/factory'; import { ContainerLink } from '@console/internal/components/pod'; -import type { FirehoseResult } from '@console/internal/components/utils'; import { ResourceLink, navFactory, SectionHeading, ResourceSummary, DetailsItem, - Firehose, Loading, } from '@console/internal/components/utils'; +import { useK8sWatchResource } from '@console/internal/components/utils/k8s-watch-hook'; import type { PodKind, ContainerStatus } from '@console/internal/module/k8s'; import { referenceForModel } from '@console/internal/module/k8s'; import { EmptyStateResourceBadge, GreenCheckCircleIcon } from '@console/shared/'; @@ -356,7 +355,7 @@ export const ContainerVulnerabilities: FC = (prop {props.pod.spec.containers.find((c) => c.name === status.name).image} - {props.loaded ? ( + {props.imageManifestVuln.loaded ? ( withVuln( vulnFor(status), (vuln) => ( @@ -400,30 +399,30 @@ export const ContainerVulnerabilities: FC = (prop export const ImageManifestVulnPodTab: FC = (props) => { const params = useParams(); + const [imageManifestVuln, loaded, loadError] = useK8sWatchResource({ + isList: true, + kind: referenceForModel(ImageManifestVulnModel), + namespace: params.ns, + selector: { + matchLabels: { [podKey(props.obj)]: 'true' }, + }, + }); + return ( - - {/* FIXME(alecmerdler): Hack because `Firehose` injects props without TypeScript knowing about it */} - - + ); }; export type ContainerVulnerabilitiesProps = { - loaded: boolean; pod: PodKind; - imageManifestVuln: FirehoseResult; + imageManifestVuln: { + data: ImageManifestVuln[]; + loaded: boolean; + loadError?: unknown; + }; }; export type ImageManifestVulnPageProps = { diff --git a/frontend/packages/operator-lifecycle-manager/src/components/__tests__/catalog-source.spec.tsx b/frontend/packages/operator-lifecycle-manager/src/components/__tests__/catalog-source.spec.tsx index d3886e9a18..483b54cbe6 100644 --- a/frontend/packages/operator-lifecycle-manager/src/components/__tests__/catalog-source.spec.tsx +++ b/frontend/packages/operator-lifecycle-manager/src/components/__tests__/catalog-source.spec.tsx @@ -1,10 +1,11 @@ -import { cloneElement } from 'react'; import { screen } from '@testing-library/react'; import * as _ from 'lodash'; import * as Router from 'react-router-dom-v5-compat'; import { DetailsPage } from '@console/internal/components/factory'; -import { Firehose } from '@console/internal/components/utils'; -import { useK8sWatchResource } from '@console/internal/components/utils/k8s-watch-hook'; +import { + useK8sWatchResource, + useK8sWatchResources, +} from '@console/internal/components/utils/k8s-watch-hook'; import { referenceForModel } from '@console/internal/module/k8s'; import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils'; import { testCatalogSource, testPackageManifest, dummyPackageManifest } from '../../../mocks'; @@ -18,6 +19,7 @@ import { jest.mock('@console/internal/components/utils/k8s-watch-hook', () => ({ useK8sWatchResource: jest.fn(), + useK8sWatchResources: jest.fn(), })); jest.mock('react-router-dom-v5-compat', () => ({ @@ -35,10 +37,6 @@ jest.mock('@console/internal/components/factory', () => ({ jest.mock('@console/internal/components/utils', () => ({ ...jest.requireActual('@console/internal/components/utils'), - Firehose: jest.fn(({ children }) => { - const props = { packageManifest: { loaded: false } }; - return typeof children === 'function' ? children(props) : children; - }), LoadingBox: jest.fn(() => 'Loading...'), ResourceSummary: jest.fn(() => null), SectionHeading: jest.fn(() => null), @@ -86,8 +84,8 @@ jest.mock('@patternfly/react-core', () => ({ })); const mockDetailsPage = (DetailsPage as unknown) as jest.Mock; -const mockFirehose = (Firehose as unknown) as jest.Mock; const mockUseK8sWatchResource = useK8sWatchResource as jest.Mock; +const mockUseK8sWatchResources = useK8sWatchResources as jest.Mock; describe('CatalogSourceDetails', () => { let obj; @@ -169,7 +167,7 @@ describe('CreateSubscriptionYAML', () => { hash: '', key: 'default', }); - mockFirehose.mockClear(); + mockUseK8sWatchResources.mockClear(); }); afterEach(() => { @@ -177,12 +175,9 @@ describe('CreateSubscriptionYAML', () => { }); it('displays package name in the subscription YAML when loaded', () => { - mockFirehose.mockImplementationOnce((firehoseProps) => { - const childElement = firehoseProps.children; - return cloneElement(childElement, { - packageManifest: { loaded: true, data: testPackageManifest }, - operatorGroup: { loaded: true, data: [] }, - }); + mockUseK8sWatchResources.mockReturnValue({ + packageManifest: { loaded: true, data: testPackageManifest, loadError: null }, + operatorGroup: { loaded: true, data: [], loadError: null }, }); renderWithProviders(); @@ -191,12 +186,9 @@ describe('CreateSubscriptionYAML', () => { }); it('displays loading indicator when package manifest is not yet loaded', () => { - mockFirehose.mockImplementationOnce((firehoseProps) => { - const childElement = firehoseProps.children; - return cloneElement(childElement, { - packageManifest: { loaded: false }, - operatorGroup: { loaded: false }, - }); + mockUseK8sWatchResources.mockReturnValue({ + packageManifest: { loaded: false, data: undefined, loadError: null }, + operatorGroup: { loaded: false, data: undefined, loadError: null }, }); renderWithProviders(); @@ -205,12 +197,9 @@ describe('CreateSubscriptionYAML', () => { }); it('displays subscription YAML with default channel information', () => { - mockFirehose.mockImplementationOnce((firehoseProps) => { - const childElement = firehoseProps.children; - return cloneElement(childElement, { - packageManifest: { loaded: true, data: testPackageManifest }, - operatorGroup: { loaded: true, data: [] }, - }); + mockUseK8sWatchResources.mockReturnValue({ + packageManifest: { loaded: true, data: testPackageManifest, loadError: null }, + operatorGroup: { loaded: true, data: [], loadError: null }, }); renderWithProviders(); diff --git a/frontend/packages/operator-lifecycle-manager/src/components/catalog-source.tsx b/frontend/packages/operator-lifecycle-manager/src/components/catalog-source.tsx index 959d28f20d..44db6831e0 100644 --- a/frontend/packages/operator-lifecycle-manager/src/components/catalog-source.tsx +++ b/frontend/packages/operator-lifecycle-manager/src/components/catalog-source.tsx @@ -15,9 +15,7 @@ import type { RowFunctionArgs, } from '@console/internal/components/factory'; import { DetailsPage, Table, TableData, MultiListPage } from '@console/internal/components/factory'; -import type { FirehoseResult } from '@console/internal/components/utils'; import { - Firehose, LoadingBox, ConsoleEmptyState, navFactory, @@ -27,6 +25,7 @@ import { ResourceSummary, DetailsItem, } from '@console/internal/components/utils'; +import { useK8sWatchResources } from '@console/internal/components/utils/k8s-watch-hook'; import i18n from '@console/internal/i18n'; import { ConfigMapModel } from '@console/internal/models'; import type { K8sKind, K8sModel } from '@console/internal/module/k8s'; @@ -213,12 +212,30 @@ export const CatalogSourceDetailsPage: FC = (props) => { export const CreateSubscriptionYAML: FC = (props) => { type CreateProps = { - packageManifest: { loaded: boolean; data?: PackageManifestKind }; - operatorGroup: { loaded: boolean; data?: OperatorGroupKind[] }; + packageManifest: { loaded: boolean; data?: PackageManifestKind; loadError?: unknown }; + operatorGroup: { loaded: boolean; data?: OperatorGroupKind[]; loadError?: unknown }; }; const { t } = useTranslation(); const params = useParams(); const location = useLocation(); + + const resources = useK8sWatchResources<{ + packageManifest: PackageManifestKind; + operatorGroup: OperatorGroupKind[]; + }>({ + packageManifest: { + kind: referenceForModel(PackageManifestModel), + isList: false, + name: new URLSearchParams(location.search).get('pkg'), + namespace: new URLSearchParams(location.search).get('catalogNamespace'), + }, + operatorGroup: { + kind: referenceForModel(OperatorGroupModel), + isList: true, + namespace: params.ns, + }, + }); + const Create = requireOperatorGroup( withFallback( (createProps) => { @@ -256,26 +273,11 @@ export const CreateSubscriptionYAML: FC = (props) => { ); return ( - - {/* FIXME(alecmerdler): Hack because `Firehose` injects props without TypeScript knowing about it */} - - + ); }; @@ -546,8 +548,8 @@ type DisabledPopoverProps = { }; type FlattenArgType = { - catalogSources?: FirehoseResult; - packageManifests?: FirehoseResult; + catalogSources?: { data?: CatalogSourceKind[]; loaded?: boolean; loadError?: unknown }; + packageManifests?: { data?: PackageManifestKind[]; loaded?: boolean; loadError?: unknown }; operatorHub: OperatorHubKind; }; diff --git a/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-page.tsx b/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-page.tsx index 9e93b08d51..4fb03b0236 100644 --- a/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-page.tsx +++ b/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-page.tsx @@ -4,7 +4,8 @@ import * as _ from 'lodash'; import { Trans, useTranslation } from 'react-i18next'; import { useParams, Link } from 'react-router-dom-v5-compat'; import { OPERATOR_BACKED_SERVICE_CATALOG_TYPE_ID } from '@console/dev-console/src/const'; -import { Firehose, skeletonCatalog, StatusBox } from '@console/internal/components/utils'; +import { skeletonCatalog, StatusBox } from '@console/internal/components/utils'; +import { useK8sWatchResources } from '@console/internal/components/utils/k8s-watch-hook'; import type { CloudCredentialKind, InfrastructureKind, @@ -247,12 +248,69 @@ export const OperatorHubList: FC = ({ ); }; -export const OperatorHubPage = withFallback((props) => { +export const OperatorHubPage = withFallback(() => { const params = useParams(); const isSoftwareCatalogEnabled = useIsSoftwareCatalogEnabled(); const isOperatorBackedServiceEnabled = isCatalogTypeEnabled( OPERATOR_BACKED_SERVICE_CATALOG_TYPE_ID, ); + + const resources = useK8sWatchResources<{ + operatorGroups: OperatorGroupKind[]; + marketplacePackageManifests: PackageManifestKind[]; + packageManifests: PackageManifestKind[]; + subscriptions: SubscriptionKind[]; + clusterServiceVersions: ClusterServiceVersionKind[]; + cloudCredentials: CloudCredentialKind; + infrastructure: InfrastructureKind; + authentication: AuthenticationKind; + }>({ + operatorGroups: { + isList: true, + kind: referenceForModel(OperatorGroupModel), + }, + marketplacePackageManifests: { + isList: true, + kind: referenceForModel(PackageManifestModel), + namespace: params.ns, + selector: { matchLabels: { 'openshift-marketplace': 'true' } }, + }, + packageManifests: { + isList: true, + kind: referenceForModel(PackageManifestModel), + namespace: params.ns, + selector: fromRequirements([ + { key: 'opsrc-owner-name', operator: 'DoesNotExist' }, + { key: 'csc-owner-name', operator: 'DoesNotExist' }, + ]), + }, + subscriptions: { + isList: true, + kind: referenceForModel(SubscriptionModel), + }, + clusterServiceVersions: { + kind: referenceForModel(ClusterServiceVersionModel), + namespaced: true, + isList: true, + namespace: params.ns, + }, + cloudCredentials: { + kind: referenceForModel(CloudCredentialModel), + name: 'cluster', + }, + infrastructure: { + kind: referenceForModel(InfrastructureModel), + name: 'cluster', + }, + authentication: { + kind: referenceForModel(AuthenticationModel), + name: 'cluster', + }, + }); + + const loaded = Object.values(resources).every((r) => r.loaded); + const loadError = Object.values(resources).find((r) => r.loadError)?.loadError; + return ( <> OperatorHub @@ -278,62 +336,12 @@ export const OperatorHubPage = withFallback((props) => { ) } /> - - {/* FIXME(alecmerdler): Hack because `Firehose` injects props without TypeScript knowing about it */} - - + ); diff --git a/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-subscribe.tsx b/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-subscribe.tsx index 31584038fd..94ea45b97c 100644 --- a/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-subscribe.tsx +++ b/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-subscribe.tsx @@ -22,7 +22,6 @@ import { RadioGroup } from '@console/internal/components/radio'; import { documentationURLs, FieldLevelHelp, - Firehose, getDocumentationURL, getURLSearchParams, history, @@ -33,7 +32,10 @@ import { resourcePathFromModel, StatusBox, } from '@console/internal/components/utils'; -import { useK8sWatchResource } from '@console/internal/components/utils/k8s-watch-hook'; +import { + useK8sWatchResource, + useK8sWatchResources, +} from '@console/internal/components/utils/k8s-watch-hook'; import { useAccessReview } from '@console/internal/components/utils/rbac'; import { ConsoleOperatorConfigModel, @@ -1220,40 +1222,45 @@ const OperatorHubSubscribe: FC = (props) => ( ); -export const OperatorHubSubscribePage: FC = (props) => { - return ( - { + const [activeNamespace] = useActiveNamespace(); + const resources = useK8sWatchResources<{ + operatorGroup: OperatorGroupKind[]; + packageManifest: PackageManifestKind[]; + subscription: SubscriptionKind[]; + }>({ + operatorGroup: { + isList: true, + kind: referenceForModel(OperatorGroupModel), + }, + packageManifest: { + isList: true, + kind: referenceForModel(PackageManifestModel), + namespace: new URLSearchParams(window.location.search).get('catalogNamespace'), + fieldSelector: `metadata.name=${new URLSearchParams(window.location.search).get('pkg')}`, + selector: { + matchLabels: { + catalog: new URLSearchParams(window.location.search).get('catalog'), }, - ]} - > - {/* FIXME(alecmerdler): Hack because `Firehose` injects props without TypeScript knowing about it */} - - + }, + }, + subscription: { + isList: true, + kind: referenceForModel(SubscriptionModel), + }, + }); + + const loaded = Object.values(resources).every((r) => r.loaded); + const loadError = Object.values(resources).find((r) => r.loadError)?.loadError; + + return ( + ); }; diff --git a/frontend/packages/operator-lifecycle-manager/src/components/operator-install-page.tsx b/frontend/packages/operator-lifecycle-manager/src/components/operator-install-page.tsx index 15dfb7ade1..f90b6af861 100644 --- a/frontend/packages/operator-lifecycle-manager/src/components/operator-install-page.tsx +++ b/frontend/packages/operator-lifecycle-manager/src/components/operator-install-page.tsx @@ -13,19 +13,19 @@ import { import { useTranslation } from 'react-i18next'; import type { LinkProps } from 'react-router-dom-v5-compat'; import { useParams, Link } from 'react-router-dom-v5-compat'; +import type { WatchK8sResultsObject } from '@console/dynamic-plugin-sdk'; import { ResourceStatus, StatusIconAndText } from '@console/dynamic-plugin-sdk'; import { useK8sWatchResource } from '@console/dynamic-plugin-sdk/src/api/core-api'; import { useOverlay } from '@console/dynamic-plugin-sdk/src/app/modal-support/useOverlay'; import { SyncMarkdownView } from '@console/internal/components/markdown-view'; import { ErrorModal } from '@console/internal/components/modals/error-modal'; -import type { FirehoseResult } from '@console/internal/components/utils'; import { - Firehose, LoadingInline, ResourceLink, resourcePathFromModel, useAccessReview, } from '@console/internal/components/utils'; +import { useK8sWatchResources } from '@console/internal/components/utils/k8s-watch-hook'; import type { K8sResourceKind } from '@console/internal/module/k8s'; import { k8sPatch, referenceForModel, referenceFor } from '@console/internal/module/k8s'; import { DocumentTitle } from '@console/shared/src/components/document-title/DocumentTitle'; @@ -512,46 +512,43 @@ const OperatorInstallStatus: FC = ({ resources }) => { export const OperatorInstallStatusPage: FC = () => { const { pkg, currentCSV, targetNamespace } = useParams(); - const installPageResources = [ - { + const resources = useK8sWatchResources<{ + clusterServiceVersion: ClusterServiceVersionKind; + subscription: SubscriptionKind; + installPlans: InstallPlanKind[]; + }>({ + clusterServiceVersion: { kind: referenceForModel(ClusterServiceVersionModel), namespaced: true, isList: false, name: currentCSV, namespace: targetNamespace, - prop: 'clusterServiceVersion', }, - { + subscription: { kind: referenceForModel(SubscriptionModel), namespaced: true, isList: false, name: pkg, namespace: targetNamespace, optional: true, - prop: 'subscription', }, - { + installPlans: { kind: referenceForModel(InstallPlanModel), - prop: 'installPlans', namespaced: true, namespace: targetNamespace, isList: true, optional: true, }, - ]; + }); - return ( - - - - ); + return ; }; export type OperatorInstallPageProps = { resources?: { - clusterServiceVersion: FirehoseResult; - subscription: FirehoseResult; - installPlans: FirehoseResult; + clusterServiceVersion: WatchK8sResultsObject; + subscription: WatchK8sResultsObject; + installPlans: WatchK8sResultsObject; }; }; type InstallSuccededMessageProps = { diff --git a/frontend/packages/operator-lifecycle-manager/src/components/topology/sidebar/TopologyOperatorBackedResources.tsx b/frontend/packages/operator-lifecycle-manager/src/components/topology/sidebar/TopologyOperatorBackedResources.tsx index 2ab169267d..3219e0cd0d 100644 --- a/frontend/packages/operator-lifecycle-manager/src/components/topology/sidebar/TopologyOperatorBackedResources.tsx +++ b/frontend/packages/operator-lifecycle-manager/src/components/topology/sidebar/TopologyOperatorBackedResources.tsx @@ -1,8 +1,10 @@ import type { ReactElement, FC } from 'react'; +import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom-v5-compat'; import type { TopologyDataObject } from '@console/dynamic-plugin-sdk/src/extensions/topology-types'; -import { Firehose, ResourceIcon, StatusBox } from '@console/internal/components/utils'; +import { ResourceIcon, StatusBox } from '@console/internal/components/utils'; +import { useK8sWatchResources } from '@console/internal/components/utils/k8s-watch-hook'; import type { GroupVersionKind, K8sResourceKind } from '@console/internal/module/k8s'; import { modelFor, @@ -27,11 +29,9 @@ import type { OperatorGroupData } from './types'; type OperatorResourcesProps = { namespace: string; - resources?: { - [kind: string]: { data: K8sResourceKind[] }; + resources: { + [kind: string]: { data: K8sResourceKind[]; loaded: boolean; loadError?: unknown }; }; - loaded?: boolean; - loadError?: string; flatten: (resources: { [kind: string]: { data: K8sResourceKind[] } }) => K8sResourceKind[]; linkForResource?: (obj: K8sResourceKind) => ReactElement; }; @@ -39,12 +39,12 @@ type OperatorResourcesProps = { const OperatorResources: FC = ({ namespace, resources, - loaded, - loadError, flatten, linkForResource, }) => { const { t } = useTranslation(); + const loaded = Object.values(resources).every((r) => r.loaded); + const loadError = Object.values(resources).find((r) => r.loadError)?.loadError; const manifestResources = flatten(resources); return ( = ({ kind, })) as CRDDescription['resources']); - const firehoseResources = resourcesToGet.reduce((acc, descriptor) => { - const { name, kind, version } = descriptor; - const group = name ? name.substring(name.indexOf('.') + 1) : ''; - const reference = group ? referenceForGroupVersionKind(group)(version)(kind) : kind; - const model = modelFor(reference); - acc.push({ - prop: kind, - kind: model && !model.crd ? kind : reference, - namespaced: model ? model.namespaced : true, - namespace, - isList: true, - optional: true, - }); - return acc; - }, []); + const watchedResources = useMemo(() => { + return resourcesToGet.reduce((acc, descriptor) => { + const { name, kind, version } = descriptor; + const group = name ? name.substring(name.indexOf('.') + 1) : ''; + const reference = group ? referenceForGroupVersionKind(group)(version)(kind) : kind; + const model = modelFor(reference); + acc[kind] = { + kind: model && !model.crd ? kind : reference, + namespaced: model ? model.namespaced : true, + namespace, + isList: true, + optional: true, + }; + return acc; + }, {}); + }, [namespace, resourcesToGet]); + + const resources = useK8sWatchResources<{ [key: string]: K8sResourceKind[] }>(watchedResources); return ( - - - + ); }; diff --git a/frontend/packages/shipwright-plugin/src/components/build-form/PushSecretDropdown.tsx b/frontend/packages/shipwright-plugin/src/components/build-form/PushSecretDropdown.tsx index ea579a3c7c..369485665d 100644 --- a/frontend/packages/shipwright-plugin/src/components/build-form/PushSecretDropdown.tsx +++ b/frontend/packages/shipwright-plugin/src/components/build-form/PushSecretDropdown.tsx @@ -1,8 +1,9 @@ import type { ReactNode, FC } from 'react'; import * as fuzzy from 'fuzzysearch'; import { useTranslation } from 'react-i18next'; -import { Firehose } from '@console/internal/components/utils'; +import { useK8sWatchResource } from '@console/internal/components/utils/k8s-watch-hook'; import { SecretModel } from '@console/internal/models'; +import type { SecretKind } from '@console/internal/module/k8s'; import type { ResourceDropdownProps } from '@console/shared/src/components/dropdown/ResourceDropdown'; import { ResourceDropdown } from '@console/shared/src/components/dropdown/ResourceDropdown'; @@ -25,26 +26,32 @@ const PushSecretDropdown: FC = (props) => { item.type === 'kubernetes.io/dockercfg' || item.type === 'kubernetes.io/dockerconfigjson' ); }; + const [secrets, loaded, loadError] = useK8sWatchResource({ + isList: true, + kind: SecretModel.kind, + namespace: props.namespace, + optional: true, + }); + const resources = [ { - isList: true, + data: secrets, + loaded, + loadError, kind: SecretModel.kind, - namespace: props.namespace, - prop: SecretModel.id, - optional: true, }, ]; + return ( - - - + ); }; diff --git a/frontend/packages/topology/src/components/dropdowns/ApplicationDropdown.tsx b/frontend/packages/topology/src/components/dropdowns/ApplicationDropdown.tsx index 338e34c1cf..7da36fd71e 100644 --- a/frontend/packages/topology/src/components/dropdowns/ApplicationDropdown.tsx +++ b/frontend/packages/topology/src/components/dropdowns/ApplicationDropdown.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { Firehose } from '@console/internal/components/utils'; +import { useK8sWatchResources } from '@console/internal/components/utils/k8s-watch-hook'; import { ResourceDropdown } from '@console/shared'; import type { ResourceDropdownProps } from '../../../../console-shared/src/components/dropdown/ResourceDropdown'; import { getBaseWatchedResources } from '../../data-transforms/transform-utils'; @@ -13,24 +13,17 @@ type ApplicationDropdownProps = Omit = ({ namespace, ...props }) => { const { t } = useTranslation(); - const resources = useMemo(() => { - // Use only base watched resources since dynamic factories are handled separately - // and ApplicationDropdown primarily needs the base resources for application labels - const watchedBaseResources = getBaseWatchedResources(namespace); - return Object.keys(watchedBaseResources).map((key) => ({ - ...watchedBaseResources[key], - prop: key, - })); - }, [namespace]); + const watchedBaseResources = useMemo(() => getBaseWatchedResources(namespace), [namespace]); + + const resources = useK8sWatchResources(watchedBaseResources); return ( - - - + ); };