diff --git a/packages/forklift-console-plugin/src/modules/Providers/hooks/useProvidersInventoryList.ts b/packages/forklift-console-plugin/src/modules/Providers/hooks/useProvidersInventoryList.ts index 3ad506af0..63f64cd01 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/hooks/useProvidersInventoryList.ts +++ b/packages/forklift-console-plugin/src/modules/Providers/hooks/useProvidersInventoryList.ts @@ -1,7 +1,7 @@ import { useEffect, useRef, useState } from 'react'; -import { ProviderInventory, ProvidersInventoryList } from '@kubev2v/types'; -import { consoleFetchJSON } from '@openshift-console/dynamic-plugin-sdk'; +import { ProviderInventory, ProvidersInventoryList, V1beta1Provider } from '@kubev2v/types'; +import { consoleFetchJSON, useFlag } from '@openshift-console/dynamic-plugin-sdk'; import { getInventoryApiUrl, hasObjectChangedInGivenFields } from '../utils/helpers'; @@ -12,9 +12,11 @@ const INVENTORY_TYPES: string[] = ['openshift', 'openstack', 'ovirt', 'vsphere', /** * Configuration parameters for useProvidersInventoryList hook. * @interface + * @property {V1beta1Provider[]} currNamespaceProviders - list of namespace's providers to fetch inventory data for. * @property {number} interval - Polling interval in milliseconds. */ interface UseInventoryParams { + currNamespaceProviders?: V1beta1Provider[]; interval?: number; // Polling interval in milliseconds } @@ -36,11 +38,13 @@ interface UseInventoryResult { * It fetches data on mount and then at the specified interval. * * @param {UseInventoryParams} params - Configuration parameters for the hook. + * @param {V1beta1Provider[]} currNamespaceProviders - list of namespace's providers to fetch inventory data for. * @param {number} [params.interval=10000] - Interval (in milliseconds) to fetch new data at. * * @returns {UseInventoryResult} result - Contains the inventory data, the loading state, and the error state. */ export const useProvidersInventoryList = ({ + currNamespaceProviders = null, interval = 20000, }: UseInventoryParams): UseInventoryResult => { const [inventory, setInventory] = useState(null); @@ -48,15 +52,28 @@ export const useProvidersInventoryList = ({ const [error, setError] = useState(null); const oldDataRef = useRef(null); const oldErrorRef = useRef(null); + const canList: boolean = useFlag('CAN_LIST_NS'); useEffect(() => { const fetchData = async () => { try { - const newInventory: ProvidersInventoryList = await consoleFetchJSON( - getInventoryApiUrl(`providers?detail=1`), - ); + // Fetch all providers + if (canList) { + const newInventory: ProvidersInventoryList = await consoleFetchJSON( + getInventoryApiUrl(`providers?detail=1`), + ); - updateInventoryIfChanged(newInventory, DEFAULT_FIELDS_TO_COMPARE); + updateInventoryIfChanged(newInventory, DEFAULT_FIELDS_TO_COMPARE); + } + // Fetch current namespace's providers + else { + currNamespaceProviders?.forEach(async (provider) => { + const newProviderInventory = await consoleFetchJSON( + getInventoryApiUrl(`providers/${provider.spec.type}/${provider.metadata.uid}`), + ); + updateInventoryProviderIfChanged(newProviderInventory, DEFAULT_FIELDS_TO_COMPARE); + }); + } handleError(null); } catch (e) { handleError(e); @@ -67,7 +84,7 @@ export const useProvidersInventoryList = ({ const intervalId = setInterval(fetchData, interval); return () => clearInterval(intervalId); - }, [interval]); + }, [interval, currNamespaceProviders]); /** * Handles any errors thrown when trying to fetch the inventory. @@ -148,6 +165,58 @@ export const useProvidersInventoryList = ({ } } + function updateInventoryProviderIfChanged( + newProviderInventory: ProviderInventory, + fieldsToCompare: string[], + ): void { + if ( + !newProviderInventory || + newProviderInventory.uid == null || + newProviderInventory.type == null + ) { + setLoading(false); + return; + } + + let needReRender = false; + // Initialize the new inventory list based on an empty list or the current one + const newInventoryList: ProvidersInventoryList = + oldDataRef.current?.inventoryList == null + ? { openshift: [], openstack: [], ovirt: [], vsphere: [], ova: [] } + : oldDataRef.current?.inventoryList; + + // Create a map of old inventory, using 'uid' as the key and search for current inventory updated item. + // If a matching item is not found in the new list, or the item has changed, we need to update the inventory list and re-render. + const oldFlatInventory = INVENTORY_TYPES.flatMap( + (type) => oldDataRef.current?.inventoryList?.[type] || [], + ); + const oldInventoryMap = new Map(oldFlatInventory.map((item) => [item.uid, item])); + const oldProviderInventory = oldInventoryMap.get(newProviderInventory.uid); + + if (!oldProviderInventory) { + newInventoryList[newProviderInventory.type].push(newProviderInventory); + needReRender = true; + } else if ( + hasObjectChangedInGivenFields({ + oldObject: oldProviderInventory, + newObject: newProviderInventory, + fieldsToCompare, + }) + ) { + newInventoryList[newProviderInventory.type].forEach((item, i) => { + if (item === oldProviderInventory) + newInventoryList[newProviderInventory.type][i] = newProviderInventory; + }); + needReRender = true; + } + + if (needReRender) { + setInventory(newInventoryList); + setLoading(false); + oldDataRef.current = { inventoryList: newInventoryList }; + } + } + return { inventory, loading, error }; }; diff --git a/packages/forklift-console-plugin/src/modules/Providers/views/list/ProvidersListPage.tsx b/packages/forklift-console-plugin/src/modules/Providers/views/list/ProvidersListPage.tsx index af4b8036d..911e478d8 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/views/list/ProvidersListPage.tsx +++ b/packages/forklift-console-plugin/src/modules/Providers/views/list/ProvidersListPage.tsx @@ -179,11 +179,16 @@ const ProvidersListPage: React.FC<{ namespace, }); + // A list of providers belong to the active namespace + const currNamespaceProviders: V1beta1Provider[] = providers.filter( + (provider) => provider.metadata?.namespace === namespace, + ); + const { inventory, loading: inventoryLoading, error: inventoryError, - } = useProvidersInventoryList({}); + } = useProvidersInventoryList({ currNamespaceProviders }); const permissions = useGetDeleteAndEditAccessReview({ model: ProviderModel,