UNPKG

@openshift-console/dynamic-plugin-sdk

Version:

Provides core APIs, types and utilities used by dynamic plugins at runtime.

114 lines (113 loc) 5.42 kB
import * as React from 'react'; import { Map as ImmutableMap } from 'immutable'; import { useSelector, useDispatch } from 'react-redux'; import { createSelectorCreator, defaultMemoize } from 'reselect'; import * as k8sActions from '../../../app/k8s/actions/k8s'; import { transformGroupVersionKindToReference, getReferenceForModel, getGroupVersionKindForReference, } from '../k8s-ref'; import { getIDAndDispatch, getReduxData, NoModelError } from './k8s-watcher'; import { useDeepCompareMemoize } from './useDeepCompareMemoize'; import { getK8sModel } from './useK8sModel'; import { useModelsLoaded } from './useModelsLoaded'; import { usePrevious } from './usePrevious'; /** * Hook that retrieves the Kubernetes resources along with their respective status for loaded and error. * @param initResources resources need to be watched as key-value pair, wherein key will be unique to resource and value will be options needed to watch for the respective resource. * @returns A map where keys are as provided in initResouces and value has three properties data, loaded and error. * @example * ```ts * const Component: React.FC = () => { * const watchResources = { 'deployment': {...}, 'pod': {...} ... } * const {deployment, pod} = useK8sWatchResources(watchResources) * return ... * } * ``` */ export const useK8sWatchResources = (initResources) => { const resources = useDeepCompareMemoize(initResources, true); const modelsLoaded = useModelsLoaded(); const allK8sModels = useSelector((state) => state.k8s.getIn(['RESOURCES', 'models'])); const prevK8sModels = usePrevious(allK8sModels); const prevResources = usePrevious(resources); const k8sModelsRef = React.useRef(ImmutableMap()); if (prevResources !== resources || (prevK8sModels !== allK8sModels && Object.values(resources).some((r) => { const modelReference = transformGroupVersionKindToReference(r.groupVersionKind || r.kind); return (getK8sModel(prevK8sModels, modelReference) !== getK8sModel(allK8sModels, modelReference)); }))) { const requiredModels = Object.values(resources).map((r) => transformGroupVersionKindToReference(r.groupVersionKind || r.kind)); k8sModelsRef.current = allK8sModels.filter((model) => requiredModels.includes(getReferenceForModel(model)) || requiredModels.includes(model.kind)); } const k8sModels = k8sModelsRef.current; const reduxIDs = React.useMemo(() => modelsLoaded ? Object.keys(resources).reduce((ids, key) => { const modelReference = transformGroupVersionKindToReference(resources[key].groupVersionKind || resources[key].kind); const resourceModel = modelReference && (k8sModels.get(modelReference) || k8sModels.get(getGroupVersionKindForReference(modelReference).kind)); if (!resourceModel) { ids[key] = { noModel: true, }; } else { const idAndDispatch = getIDAndDispatch(resources[key], resourceModel); if (idAndDispatch) { ids[key] = idAndDispatch; } } return ids; }, {}) : null, [k8sModels, modelsLoaded, resources]); const dispatch = useDispatch(); React.useEffect(() => { const reduxIDKeys = Object.keys(reduxIDs || {}); reduxIDKeys.forEach((k) => { if (reduxIDs[k].dispatch) { dispatch(reduxIDs[k].dispatch); } }); return () => { reduxIDKeys.forEach((k) => { if (reduxIDs[k].dispatch) { dispatch(k8sActions.stopK8sWatch(reduxIDs[k].id)); } }); }; }, [dispatch, reduxIDs]); const resourceK8sSelectorCreator = React.useMemo(() => createSelectorCreator( // specifying createSelectorCreator<ImmutableMap<string, K8sKind>> throws type error defaultMemoize, (oldK8s, newK8s) => Object.keys(reduxIDs || {}) .filter((k) => !reduxIDs[k].noModel) .every((k) => oldK8s.get(reduxIDs[k].id) === newK8s.get(reduxIDs[k].id))), [reduxIDs]); const resourceK8sSelector = React.useMemo(() => resourceK8sSelectorCreator((state) => state.k8s, (k8s) => k8s), [resourceK8sSelectorCreator]); const resourceK8s = useSelector(resourceK8sSelector); const results = React.useMemo(() => Object.keys(resources).reduce((acc, key) => { if (reduxIDs?.[key].noModel) { acc[key] = { data: resources[key].isList ? [] : {}, loaded: true, loadError: new NoModelError(), }; } else if (resourceK8s.has(reduxIDs?.[key].id)) { const data = getReduxData(resourceK8s.getIn([reduxIDs[key].id, 'data']), resources[key]); const loaded = resourceK8s.getIn([reduxIDs[key].id, 'loaded']); const loadError = resourceK8s.getIn([reduxIDs[key].id, 'loadError']); acc[key] = { data, loaded, loadError }; } else { acc[key] = { data: resources[key].isList ? [] : {}, loaded: false, loadError: undefined, }; } return acc; }, {}), [resources, reduxIDs, resourceK8s]); return results; };