From bee7b2cce92613a4948d26d98d77f56bccfba167 Mon Sep 17 00:00:00 2001 From: Muslimjon Date: Wed, 10 Jan 2024 12:38:55 +0100 Subject: [PATCH 1/2] create useFetchBatched fook to fetch data with concurrent requests --- packages/utils/src/index.ts | 2 + packages/utils/src/useFetchBatched/index.ts | 1 + .../src/useFetchBatched/useFetchBatched.ts | 30 ++++++++++++ packages/utils/src/usePromiseQueue/index.ts | 1 + .../src/usePromiseQueue/usePromiseQueue.ts | 46 +++++++++++++++++++ 5 files changed, 80 insertions(+) create mode 100644 packages/utils/src/useFetchBatched/index.ts create mode 100644 packages/utils/src/useFetchBatched/useFetchBatched.ts create mode 100644 packages/utils/src/usePromiseQueue/index.ts create mode 100644 packages/utils/src/usePromiseQueue/usePromiseQueue.ts diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 3b023bd8c..0fe443812 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -18,3 +18,5 @@ export { useInventory } from './useInventory'; export * from './CypressUtils'; export * from './useInsightsNavigate'; export * from './useExportPDF'; +export * from './usePromiseQueue'; +export * from './useFetchBatched'; diff --git a/packages/utils/src/useFetchBatched/index.ts b/packages/utils/src/useFetchBatched/index.ts new file mode 100644 index 000000000..da45607b1 --- /dev/null +++ b/packages/utils/src/useFetchBatched/index.ts @@ -0,0 +1 @@ +export { default } from './useFetchBatched'; diff --git a/packages/utils/src/useFetchBatched/useFetchBatched.ts b/packages/utils/src/useFetchBatched/useFetchBatched.ts new file mode 100644 index 000000000..80d20dd2a --- /dev/null +++ b/packages/utils/src/useFetchBatched/useFetchBatched.ts @@ -0,0 +1,30 @@ +import usePromiseQueue from '../usePromiseQueue'; + +type FetchFunctionType = (filter: object | Array, options?: object) => void; + +//hook to enable fetching a lot of data from the API in concurrent API calls +const useFetchBatched = () => { + const { isResolving: isLoading, resolve } = usePromiseQueue(); + + return { + isLoading, + fetchBatched: (fetchFunction: FetchFunctionType, total: number, filter: object, batchSize = 50) => { + const pages = Math.ceil(total / batchSize) || 1; + + const results = resolve([...new Array(pages)].map((_, pageIdx) => () => fetchFunction(filter, { page: pageIdx + 1, per_page: batchSize }))); + + return results; + }, + fetchBatchedInline: (fetchFunction: FetchFunctionType, list: Array, batchSize = 20) => { + const pages = Math.ceil(list.length / batchSize) || 1; + + const results = resolve( + [...new Array(pages)].map((_, pageIdx) => () => fetchFunction(list.slice(batchSize * pageIdx, batchSize * (pageIdx + 1)))) + ); + + return results; + }, + }; +}; + +export default useFetchBatched; diff --git a/packages/utils/src/usePromiseQueue/index.ts b/packages/utils/src/usePromiseQueue/index.ts new file mode 100644 index 000000000..56a903a2c --- /dev/null +++ b/packages/utils/src/usePromiseQueue/index.ts @@ -0,0 +1 @@ +export { default } from './usePromiseQueue'; diff --git a/packages/utils/src/usePromiseQueue/usePromiseQueue.ts b/packages/utils/src/usePromiseQueue/usePromiseQueue.ts new file mode 100644 index 000000000..2474f49ee --- /dev/null +++ b/packages/utils/src/usePromiseQueue/usePromiseQueue.ts @@ -0,0 +1,46 @@ +import { useCallback, useState } from 'react'; +import pAll from 'p-all'; + +const DEFAULT_CONCURRENT_PROMISES = 2; + +type PromiseQueueStateType = { + isResolving: boolean; + //undeterministic result type by p-all package + promiseResults: any; +}; + +// hook that provides queued promises with state +const usePromiseQueue = (concurrency = DEFAULT_CONCURRENT_PROMISES) => { + const defaultState: PromiseQueueStateType = { + isResolving: false, + promiseResults: undefined, + }; + const [results, setResults] = useState(defaultState); + + const resolve = useCallback( + async (fns: any) => { + setResults((state) => ({ + ...state, + isResolving: true, + })); + const results: any = await pAll(fns, { + concurrency, + }); + setResults({ + isResolving: false, + promiseResults: results, + }); + + return results; + }, + [concurrency] + ); + + return { + isResolving: results.isResolving, + results: results.promiseResults, + resolve, + }; +}; + +export default usePromiseQueue; From b38e178d5dfa977ce246653b6ca69ed4c806ce77 Mon Sep 17 00:00:00 2001 From: Muslimjon Date: Tue, 30 Jan 2024 15:42:49 +0100 Subject: [PATCH 2/2] install p-all to /package/utils --- package-lock.json | 47 ++++++++++++++++++++++++++++++++++--- packages/utils/package.json | 4 ++-- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 27168d2ff..060e2401e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32651,6 +32651,31 @@ "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==" }, + "node_modules/p-all": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-all/-/p-all-5.0.0.tgz", + "integrity": "sha512-pofqu/1FhCVa+78xNAptCGc9V45exFz2pvBRyIvgXkNM0Rh18Py7j8pQuSjA+zpabI46v9hRjNWmL9EAFcEbpw==", + "dependencies": { + "p-map": "^6.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-all/node_modules/p-map": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-6.0.0.tgz", + "integrity": "sha512-T8BatKGY+k5rU+Q/GTYgrEf2r4xRMevAN5mtXc2aPc4rS1j3s+vWTaO2Wag94neXuCAUAs8cxBL9EeB5EA6diw==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -42484,7 +42509,7 @@ }, "packages/chrome": { "name": "@redhat-cloud-services/chrome", - "version": "1.0.8", + "version": "1.0.9", "license": "Apache-2.0", "dependencies": { "lodash": "^4.17.21" @@ -43745,7 +43770,7 @@ }, "packages/utils": { "name": "@redhat-cloud-services/frontend-components-utilities", - "version": "4.0.6", + "version": "4.0.7", "license": "Apache-2.0", "dependencies": { "@redhat-cloud-services/rbac-client": "^1.0.100", @@ -43755,6 +43780,7 @@ "axios": "^0.28.0", "commander": "^2.20.3", "mkdirp": "^1.0.4", + "p-all": "^5.0.0", "react-content-loader": "^6.2.0" }, "devDependencies": { @@ -43764,7 +43790,6 @@ "peerDependencies": { "@patternfly/react-core": "^5.0.0", "@patternfly/react-table": "^5.0.0", - "cypress": ">=12.0.0 < 14.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": ">=7.0.0", @@ -50671,6 +50696,7 @@ "commander": "^2.20.3", "glob": "10.3.3", "mkdirp": "^1.0.4", + "p-all": "^5.0.0", "react-content-loader": "^6.2.0" }, "dependencies": { @@ -68304,6 +68330,21 @@ "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==" }, + "p-all": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-all/-/p-all-5.0.0.tgz", + "integrity": "sha512-pofqu/1FhCVa+78xNAptCGc9V45exFz2pvBRyIvgXkNM0Rh18Py7j8pQuSjA+zpabI46v9hRjNWmL9EAFcEbpw==", + "requires": { + "p-map": "^6.0.0" + }, + "dependencies": { + "p-map": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-6.0.0.tgz", + "integrity": "sha512-T8BatKGY+k5rU+Q/GTYgrEf2r4xRMevAN5mtXc2aPc4rS1j3s+vWTaO2Wag94neXuCAUAs8cxBL9EeB5EA6diw==" + } + } + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", diff --git a/packages/utils/package.json b/packages/utils/package.json index 74bdf8a95..be5ecaef7 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -32,8 +32,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": ">=7.0.0", - "react-router-dom": "^5.0.0 || ^6.0.0", - "cypress": ">=12.0.0 < 14.0.0" + "react-router-dom": "^5.0.0 || ^6.0.0" }, "dependencies": { "@redhat-cloud-services/rbac-client": "^1.0.100", @@ -43,6 +42,7 @@ "axios": "^0.28.0", "commander": "^2.20.3", "mkdirp": "^1.0.4", + "p-all": "^5.0.0", "react-content-loader": "^6.2.0" }, "devDependencies": {