diff --git a/src/PresentationalComponents/PatchSetWrapper/PatchSetWrapper.js b/src/PresentationalComponents/PatchSetWrapper/PatchSetWrapper.js index a72525c5..532c5a8a 100644 --- a/src/PresentationalComponents/PatchSetWrapper/PatchSetWrapper.js +++ b/src/PresentationalComponents/PatchSetWrapper/PatchSetWrapper.js @@ -5,17 +5,19 @@ import PatchSetWizard from '../../SmartComponents/PatchSetWizard/PatchSetWizard' import UnassignSystemsModal from '../../SmartComponents/Modals/UnassignSystemsModal'; import AssignSystemsModal from '../../SmartComponents/Modals/AssignSystemsModal'; -const PatchSetWrapper = ({ patchSetState, setPatchSetState }) => { +const PatchSetWrapper = ({ patchSetState, setPatchSetState, totalItems }) => { return (<> {(patchSetState.isUnassignSystemsModalOpen) && } {(patchSetState.isPatchSetWizardOpen) && } @@ -24,6 +26,7 @@ const PatchSetWrapper = ({ patchSetState, setPatchSetState }) => { PatchSetWrapper.propTypes = { patchSetState: propTypes.object, - setPatchSetState: propTypes.func + setPatchSetState: propTypes.func, + totalItems: propTypes.number }; export default PatchSetWrapper; diff --git a/src/SmartComponents/Modals/AssignSystemsModal.js b/src/SmartComponents/Modals/AssignSystemsModal.js index f711daf1..d77b697a 100644 --- a/src/SmartComponents/Modals/AssignSystemsModal.js +++ b/src/SmartComponents/Modals/AssignSystemsModal.js @@ -10,8 +10,10 @@ import { addNotification } from '@redhat-cloud-services/frontend-components-noti import { patchSetAssignSystemsNotifications } from '../PatchSet/PatchSetAssets'; import { filterSelectedActiveSystemIDs } from '../../Utilities/Helpers'; import { filterSatelliteManagedSystems } from './Helpers'; +import { useFetchBatched } from '../../Utilities/hooks'; +import isEmpty from 'lodash/isEmpty'; -const AssignSystemsModal = ({ patchSetState = {}, setPatchSetState, intl }) => { +const AssignSystemsModal = ({ patchSetState = {}, setPatchSetState, intl, totalItems }) => { const dispatch = useDispatch(); const { systemsIDs, isAssignSystemsModalOpen } = patchSetState; @@ -20,6 +22,7 @@ const AssignSystemsModal = ({ patchSetState = {}, setPatchSetState, intl }) => { const [systemsNotManagedBySatellite, setSystemsNotManagedBySatellite] = useState([]); const [systemsLoading, setSystemsLoading] = useState(true); + const { fetchBatched } = useFetchBatched(); const closeModal = () => { setPatchSetState({ @@ -66,10 +69,13 @@ const AssignSystemsModal = ({ patchSetState = {}, setPatchSetState, intl }) => { }; useEffect(() => { - if (systemsIDs) { + if (systemsIDs && !isEmpty(systemsIDs)) { setSystemsLoading(true); - - filterSatelliteManagedSystems(Object.keys(systemsIDs)).then(result => { + filterSatelliteManagedSystems( + Object.keys(systemsIDs), + fetchBatched, + totalItems + ).then(result => { setSystemsNotManagedBySatellite(result); setSystemsLoading(false); }); @@ -143,7 +149,8 @@ const AssignSystemsModal = ({ patchSetState = {}, setPatchSetState, intl }) => { AssignSystemsModal.propTypes = { intl: propTypes.any, setPatchSetState: propTypes.func, - patchSetState: propTypes.object + patchSetState: propTypes.object, + totalItems: propTypes.number }; export default injectIntl(AssignSystemsModal); diff --git a/src/SmartComponents/Modals/Helpers.js b/src/SmartComponents/Modals/Helpers.js index 8b1ea864..127c1b62 100644 --- a/src/SmartComponents/Modals/Helpers.js +++ b/src/SmartComponents/Modals/Helpers.js @@ -2,28 +2,37 @@ import React from 'react'; import { GridItem } from '@patternfly/react-core'; import messages from '../../Messages'; -import { fetchIDs, fetchSystems } from '../../Utilities/api'; +import { fetchIDs } from '../../Utilities/api'; -export const filterSystemsWithoutSets = (systemsIDs) => { - return fetchSystems({ - limit: -1, 'filter[baseline_name]': 'neq:', - filter: { stale: [true, false] } - }).then((allSystemsWithPatchSet) => { - return systemsIDs.filter(systemID => - allSystemsWithPatchSet?.data?.some(system => system.id === systemID) +const filterChosenSystems = (urlFilter, systemsIDs, fetchBatched, totalItems) => { + return fetchBatched( + (filter) => fetchIDs( + '/ids/systems', + filter + ), + { + ...urlFilter, + filter: { stale: [true, false] } + }, + totalItems, + 100 + ).then((systemsNotManagedBySatellite) => { + const aggregatedResult = systemsNotManagedBySatellite.flatMap(({ data }) => data); + return systemsIDs.filter(systemID =>{ + return aggregatedResult?.some(system => system.id === systemID); + } ); }); }; -export const filterSatelliteManagedSystems = (systemsIDs) => { - return fetchIDs('/ids/systems', { - limit: -1, 'filter[satellite_managed]': 'false', - filter: { stale: [true, false] } - }).then((systemsNotManagedBySatellite) => { - return systemsIDs.filter(systemID => - systemsNotManagedBySatellite?.data?.some(system => system.id === systemID) - ); - }); +export const filterSystemsWithoutSets = (systemsIDs, fetchBatched, totalItems) => { + const urlFilter = { 'filter[baseline_name]': 'neq:' }; + return filterChosenSystems(urlFilter, systemsIDs, fetchBatched, totalItems); +}; + +export const filterSatelliteManagedSystems = (systemsIDs, fetchBatched, totalItems) => { + const urlFilter = { 'filter[satellite_managed]': 'false' }; + return filterChosenSystems(urlFilter, systemsIDs, fetchBatched, totalItems); }; export const renderUnassignModalMessages = (bodyMessage, systemsCount, intl) => ( diff --git a/src/SmartComponents/Modals/UnassignSystemsModal.js b/src/SmartComponents/Modals/UnassignSystemsModal.js index d53717a5..09d2ad7e 100644 --- a/src/SmartComponents/Modals/UnassignSystemsModal.js +++ b/src/SmartComponents/Modals/UnassignSystemsModal.js @@ -6,11 +6,13 @@ import { injectIntl } from 'react-intl'; import messages from '../../Messages'; import { useUnassignSystemsHook } from './useUnassignSystemsHook'; import { renderUnassignModalMessages, filterSystemsWithoutSets } from './Helpers'; +import { useFetchBatched } from '../../Utilities/hooks'; -const UnassignSystemsModal = ({ unassignSystemsModalState = {}, setUnassignSystemsModalOpen, intl }) => { +const UnassignSystemsModal = ({ unassignSystemsModalState = {}, setUnassignSystemsModalOpen, intl, totalItems }) => { const { systemsIDs, isUnassignSystemsModalOpen } = unassignSystemsModalState; const [systemsWithPatchSet, setSystemWithPatchSet] = useState([]); const [systemsLoading, setSystemsLoading] = useState(true); + const { fetchBatched } = useFetchBatched(); const handleModalToggle = (shouldRefresh) => { setUnassignSystemsModalOpen({ @@ -29,7 +31,12 @@ const UnassignSystemsModal = ({ unassignSystemsModalState = {}, setUnassignSyste useEffect(() => { setSystemsLoading(true); - filterSystemsWithoutSets(systemsIDs).then(result => { + filterSystemsWithoutSets( + systemsIDs, + fetchBatched, + totalItems + ) + .then(result => { setSystemWithPatchSet(result); setSystemsLoading(false); }); @@ -81,6 +88,7 @@ const UnassignSystemsModal = ({ unassignSystemsModalState = {}, setUnassignSyste UnassignSystemsModal.propTypes = { intl: propTypes.any, setUnassignSystemsModalOpen: propTypes.func, - unassignSystemsModalState: propTypes.object + unassignSystemsModalState: propTypes.object, + totalItems: propTypes.number }; export default injectIntl(UnassignSystemsModal); diff --git a/src/SmartComponents/Systems/SystemsListAssets.js b/src/SmartComponents/Systems/SystemsListAssets.js index 15d17dd4..cacb631c 100644 --- a/src/SmartComponents/Systems/SystemsListAssets.js +++ b/src/SmartComponents/Systems/SystemsListAssets.js @@ -180,8 +180,8 @@ export const useActivateRemediationModal = (setRemediationIssues, setRemediation fetchBatched( (__, pagination) => fetchApplicableSystemAdvisoriesApi({ ...filter, ...pagination }), - totalCount, - filter + filter, + totalCount ).then(response => { const advisories = response.flatMap(({ data }) => data); const remediationIssues = remediationProvider( diff --git a/src/SmartComponents/Systems/SystemsTable.js b/src/SmartComponents/Systems/SystemsTable.js index 6211669b..c3a3a3e9 100644 --- a/src/SmartComponents/Systems/SystemsTable.js +++ b/src/SmartComponents/Systems/SystemsTable.js @@ -85,7 +85,8 @@ const SystemsTable = ({ { endpoint: ID_API_ENDPOINTS.systems, queryParams, - selectionDispatcher: systemSelectAction + selectionDispatcher: systemSelectAction, + totalItems } ); diff --git a/src/Utilities/RemediationPairs.js b/src/Utilities/RemediationPairs.js index c7080b0b..95832575 100644 --- a/src/Utilities/RemediationPairs.js +++ b/src/Utilities/RemediationPairs.js @@ -1,7 +1,7 @@ import chunk from 'lodash/chunk'; -const REQUEST_CHUNK_SIZE = 1000; -const BATCH_REQUEST_SIZE = 5; +const REQUEST_CHUNK_SIZE = 100; +const BATCH_REQUEST_SIZE = 10; const REQUEST_INTERVAL = 15000; //15 seconds. AKAMAI allowed life for 5 API calls const fetchDataCallback = (endpoint, authToken) => (input) => { diff --git a/src/Utilities/hooks/useFetchBatched.js b/src/Utilities/hooks/useFetchBatched.js index 63fd1fa8..87679d2d 100644 --- a/src/Utilities/hooks/useFetchBatched.js +++ b/src/Utilities/hooks/useFetchBatched.js @@ -5,14 +5,25 @@ export const useFetchBatched = () => { return { isLoading, - fetchBatched: (fetchFunction, total, filter, batchSize = 50) => { + fetchBatched: async (fetchFunction, filter, total, batchSize = 50) => { + if (!total) { + total = await fetchFunction({ limit: 1 }).then( + response => response?.meta?.total_items || 0 + ); + } + const pages = Math.ceil(total / batchSize) || 1; const results = resolve( [...new Array(pages)].map( // eslint-disable-next-line camelcase - (_, pageIdx) => () => - fetchFunction(filter, { offset: pageIdx + 1, limit: batchSize }) + (_, pageIdx) => () => { + return fetchFunction({ + ...filter, + offset: pageIdx * batchSize, + limit: batchSize + }); + } ) ); diff --git a/src/Utilities/hooks/useOnSelect.js b/src/Utilities/hooks/useOnSelect.js index 0c1f9c72..b0c8205f 100644 --- a/src/Utilities/hooks/useOnSelect.js +++ b/src/Utilities/hooks/useOnSelect.js @@ -3,6 +3,7 @@ import { useDispatch } from 'react-redux'; import { fetchIDs } from '../api'; import { toggleAllSelectedAction } from '../../store/Actions/Actions'; import { isObject } from '../Helpers'; +import { useFetchBatched } from './useFetchBatched'; export const ID_API_ENDPOINTS = { advisories: '/ids/advisories', @@ -17,15 +18,21 @@ export const ID_API_ENDPOINTS = { const useFetchAllIDs = ( endpoint, - apiResponseTransformer -) => - useCallback((queryParams) => - fetchIDs(endpoint, { ...queryParams, limit: -1 }) + apiResponseTransformer, + totalItems +) => { + const { fetchBatched } = useFetchBatched(); + return useCallback((queryParams) => + fetchBatched( + (__, pagination) => fetchIDs(endpoint, { ...queryParams, ...pagination }), + totalItems, + queryParams + ) .then(response => apiResponseTransformer ? apiResponseTransformer(response) : response ), - [] - ); + [totalItems, endpoint]); +}; const useCreateSelectedRow = (transformKey, constructFilename) => useCallback((rows, toSelect = []) => { @@ -103,11 +110,12 @@ export const useOnSelect = (rawData, selectedRows, config) => { transformKey, apiResponseTransformer, //TODO: get rid of this custom selector - customSelector + customSelector, + totalItems } = config; const dispatch = useDispatch(); - const fetchIDs = useFetchAllIDs(endpoint, apiResponseTransformer); + const fetchIDs = useFetchAllIDs(endpoint, apiResponseTransformer, totalItems); const createSelectedRow = useCreateSelectedRow(transformKey, constructFilename); const toggleAllSystemsSelected = (flagState) => { diff --git a/src/Utilities/hooks/usePatchSetState.js b/src/Utilities/hooks/usePatchSetState.js index 0251b3b5..b730c0fb 100644 --- a/src/Utilities/hooks/usePatchSetState.js +++ b/src/Utilities/hooks/usePatchSetState.js @@ -14,7 +14,7 @@ export const usePatchSetState = (selectedRows) => { isUnassignSystemsModalOpen: false, isAssignSystemsModalOpen: false, shouldRefresh: false, - systemsIDs: [] + systemsIDs: {} }); const openPatchSetAssignWizard = (systemID) => {