From 4034bb1fe8ab3a112d299fdd13be480eee9d3591 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Feria Vila <2582866+carlosthe19916@users.noreply.github.com> Date: Mon, 19 Apr 2021 11:33:35 +0200 Subject: [PATCH 01/10] useFetch --- src/shared/hooks/useFetch/index.ts | 1 + src/shared/hooks/useFetch/useFetch.test.tsx | 127 ++++++++++++++++++++ src/shared/hooks/useFetch/useFetch.ts | 118 ++++++++++++++++++ 3 files changed, 246 insertions(+) create mode 100644 src/shared/hooks/useFetch/index.ts create mode 100644 src/shared/hooks/useFetch/useFetch.test.tsx create mode 100644 src/shared/hooks/useFetch/useFetch.ts diff --git a/src/shared/hooks/useFetch/index.ts b/src/shared/hooks/useFetch/index.ts new file mode 100644 index 00000000..2e6140d3 --- /dev/null +++ b/src/shared/hooks/useFetch/index.ts @@ -0,0 +1 @@ +export { useFetchBusinessServices } from "./useFetch"; diff --git a/src/shared/hooks/useFetch/useFetch.test.tsx b/src/shared/hooks/useFetch/useFetch.test.tsx new file mode 100644 index 00000000..f08a60eb --- /dev/null +++ b/src/shared/hooks/useFetch/useFetch.test.tsx @@ -0,0 +1,127 @@ +import axios from "axios"; +import MockAdapter from "axios-mock-adapter"; +import { renderHook, act } from "@testing-library/react-hooks"; +import { useFetchBusinessServices } from "./useFetch"; +import { BusinessServicePage } from "api/models"; +import { BUSINESS_SERVICES } from "api/rest"; + +describe("useFetchBusinessServices", () => { + it("Fetch error due to no REST API found", async () => { + // Mock REST API + new MockAdapter(axios).onGet(BUSINESS_SERVICES).networkError(); + + // Use hook + const { result, waitForNextUpdate } = renderHook(() => + useFetchBusinessServices() + ); + + const { + businessServices, + isFetching, + fetchError, + fetchBusinessServices, + } = result.current; + + expect(isFetching).toBe(false); + expect(businessServices).toBeUndefined(); + expect(fetchError).toBeUndefined(); + + // Init fetch + act(() => fetchBusinessServices({}, { page: 2, perPage: 50 })); + expect(result.current.isFetching).toBe(true); + + // Fetch finished + await waitForNextUpdate(); + expect(result.current.isFetching).toBe(false); + expect(result.current.businessServices).toBeUndefined(); + expect(result.current.fetchError).not.toBeUndefined(); + }); + + it("Fetch success", async () => { + // Mock REST API + const data: BusinessServicePage = { + _embedded: { + "business-service": [], + }, + total_count: 0, + }; + + new MockAdapter(axios) + .onGet(`${BUSINESS_SERVICES}?page=0&size=10&name=something`) + .reply(200, data); + + // Use hook + const { result, waitForNextUpdate } = renderHook(() => + useFetchBusinessServices() + ); + + const { + businessServices, + isFetching, + fetchError, + fetchBusinessServices, + } = result.current; + + expect(isFetching).toBe(false); + expect(businessServices).toBeUndefined(); + expect(fetchError).toBeUndefined(); + + // Init fetch + act(() => + fetchBusinessServices({ name: ["something"] }, { page: 1, perPage: 10 }) + ); + expect(result.current.isFetching).toBe(true); + + // Fetch finished + await waitForNextUpdate(); + expect(result.current.isFetching).toBe(false); + expect(result.current.businessServices).toMatchObject({ + data: [], + meta: { count: 0 }, + }); + expect(result.current.fetchError).toBeUndefined(); + }); + + it("Fetch all", async () => { + // Mock REST API + const data: BusinessServicePage = { + _embedded: { + "business-service": [], + }, + total_count: 0, + }; + + new MockAdapter(axios) + .onGet(`${BUSINESS_SERVICES}?page=0&size=1000&sort=name`) + .reply(200, data); + + // Use hook + const { result, waitForNextUpdate } = renderHook(() => + useFetchBusinessServices() + ); + + const { + businessServices: items, + isFetching, + fetchError, + fetchAllBusinessServices: fetchAll, + } = result.current; + + expect(isFetching).toBe(false); + expect(items).toBeUndefined(); + expect(fetchError).toBeUndefined(); + + // Init fetch + act(() => fetchAll()); + expect(result.current.isFetching).toBe(true); + + // Fetch finished + await waitForNextUpdate(); + expect(result.current.isFetching).toBe(false); + expect(result.current.businessServices).toMatchObject({ + data: [], + meta: { count: 0 }, + }); + expect(result.current.fetchError).toBeUndefined(); + }); +}); diff --git a/src/shared/hooks/useFetch/useFetch.ts b/src/shared/hooks/useFetch/useFetch.ts new file mode 100644 index 00000000..26099f60 --- /dev/null +++ b/src/shared/hooks/useFetch/useFetch.ts @@ -0,0 +1,118 @@ +import { useCallback, useReducer } from "react"; +import { AxiosError, AxiosPromise } from "axios"; +import { ActionType, createAsyncAction, getType } from "typesafe-actions"; + +import { + getBusinessServices, + BusinessServiceSortByQuery, + BusinessServiceSortBy, +} from "api/rest"; +import { PageRepresentation, BusinessService, PageQuery } from "api/models"; + +export const { + request: fetchRequest, + success: fetchSuccess, + failure: fetchFailure, +} = createAsyncAction( + "useFetch/fetch/request", + "useFetch/fetch/success", + "useFetch/fetch/failure" +)(); + +type State = Readonly<{ + isFetching: boolean; + data?: any; + fetchError?: AxiosError; + fetchCount: number; +}>; + +const defaultState: State = { + isFetching: false, + data: undefined, + fetchError: undefined, + fetchCount: 0, +}; + +type Action = ActionType< + typeof fetchRequest | typeof fetchSuccess | typeof fetchFailure +>; + +const initReducer = (isFetching: boolean): State => { + return { + ...defaultState, + isFetching, + }; +}; + +const reducer = (state: State, action: Action): State => { + switch (action.type) { + case getType(fetchRequest): + return { + ...state, + isFetching: true, + }; + case getType(fetchSuccess): + return { + ...state, + isFetching: false, + fetchError: undefined, + data: action.payload, + fetchCount: state.fetchCount + 1, + }; + case getType(fetchFailure): + return { + ...state, + isFetching: false, + fetchError: action.payload, + fetchCount: state.fetchCount + 1, + }; + default: + return state; + } +}; + +export interface IArgs { + defaultIsFetching?: boolean; + onFetch: () => AxiosPromise; +} + +export interface IState { + data?: T; + isFetching: boolean; + fetchError?: AxiosError; + fetchCount: number; + requestFetch: () => void; +} + +export const useFetchBusinessServices = ({ + defaultIsFetching = false, + onFetch, +}: IArgs): IState => { + const [state, dispatch] = useReducer(reducer, defaultIsFetching, initReducer); + + const fetchHandler = () => { + dispatch(fetchRequest()); + + onFetch() + .then(({ data }) => { + dispatch( + fetchSuccess({ + data, + }) + ); + }) + .catch((error: AxiosError) => { + dispatch(fetchFailure(error)); + }); + }; + + return { + data: state.data, + isFetching: state.isFetching, + fetchError: state.fetchError, + fetchCount: state.fetchCount, + requestFetch: fetchHandler, + }; +}; + +export default useFetchBusinessServices; From 09e09199fd96e13649c135e6d764f1b3c6de6281 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Feria Vila <2582866+carlosthe19916@users.noreply.github.com> Date: Mon, 19 Apr 2021 16:21:09 +0200 Subject: [PATCH 02/10] add test --- src/shared/hooks/index.ts | 1 + src/shared/hooks/useFetch/index.ts | 2 +- src/shared/hooks/useFetch/useFetch.test.tsx | 102 ++++---------------- src/shared/hooks/useFetch/useFetch.ts | 19 +--- 4 files changed, 26 insertions(+), 98 deletions(-) diff --git a/src/shared/hooks/index.ts b/src/shared/hooks/index.ts index 19ca294d..269e4fbb 100644 --- a/src/shared/hooks/index.ts +++ b/src/shared/hooks/index.ts @@ -2,6 +2,7 @@ export { useDeleteApplication } from "./useDeleteApplication"; export { useDeleteBusinessService } from "./useDeleteBusinessService"; export { useDeleteStakeholder } from "./useDeleteStakeholder"; export { useDeleteStakeholderGroup } from "./useDeleteStakeholderGroup"; +export { useFetch } from "./useFetch"; export { useFetchApplications } from "./useFetchApplications"; export { useFetchBusinessServices } from "./useFetchBusinessServices"; export { useFetchJobFunctions } from "./useFetchJobFunctions"; diff --git a/src/shared/hooks/useFetch/index.ts b/src/shared/hooks/useFetch/index.ts index 2e6140d3..4ee6de51 100644 --- a/src/shared/hooks/useFetch/index.ts +++ b/src/shared/hooks/useFetch/index.ts @@ -1 +1 @@ -export { useFetchBusinessServices } from "./useFetch"; +export { useFetch } from "./useFetch"; diff --git a/src/shared/hooks/useFetch/useFetch.test.tsx b/src/shared/hooks/useFetch/useFetch.test.tsx index f08a60eb..9c9a3c01 100644 --- a/src/shared/hooks/useFetch/useFetch.test.tsx +++ b/src/shared/hooks/useFetch/useFetch.test.tsx @@ -1,127 +1,65 @@ import axios from "axios"; import MockAdapter from "axios-mock-adapter"; import { renderHook, act } from "@testing-library/react-hooks"; -import { useFetchBusinessServices } from "./useFetch"; -import { BusinessServicePage } from "api/models"; -import { BUSINESS_SERVICES } from "api/rest"; -describe("useFetchBusinessServices", () => { +import { useFetch } from "./useFetch"; + +describe("useFetch", () => { it("Fetch error due to no REST API found", async () => { // Mock REST API - new MockAdapter(axios).onGet(BUSINESS_SERVICES).networkError(); + new MockAdapter(axios).onGet("/myendpoint").networkError(); // Use hook const { result, waitForNextUpdate } = renderHook(() => - useFetchBusinessServices() + useFetch({ onFetch: () => axios.get("/myendpoint") }) ); - const { - businessServices, - isFetching, - fetchError, - fetchBusinessServices, - } = result.current; + const { data, isFetching, fetchError, requestFetch } = result.current; expect(isFetching).toBe(false); - expect(businessServices).toBeUndefined(); + expect(data).toBeUndefined(); expect(fetchError).toBeUndefined(); // Init fetch - act(() => fetchBusinessServices({}, { page: 2, perPage: 50 })); + act(() => requestFetch()); expect(result.current.isFetching).toBe(true); // Fetch finished await waitForNextUpdate(); expect(result.current.isFetching).toBe(false); - expect(result.current.businessServices).toBeUndefined(); + expect(result.current.data).toBeUndefined(); expect(result.current.fetchError).not.toBeUndefined(); }); it("Fetch success", async () => { // Mock REST API - const data: BusinessServicePage = { - _embedded: { - "business-service": [], - }, - total_count: 0, - }; - - new MockAdapter(axios) - .onGet(`${BUSINESS_SERVICES}?page=0&size=10&name=something`) - .reply(200, data); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => - useFetchBusinessServices() - ); - - const { - businessServices, - isFetching, - fetchError, - fetchBusinessServices, - } = result.current; - - expect(isFetching).toBe(false); - expect(businessServices).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => - fetchBusinessServices({ name: ["something"] }, { page: 1, perPage: 10 }) - ); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.businessServices).toMatchObject({ - data: [], - meta: { count: 0 }, - }); - expect(result.current.fetchError).toBeUndefined(); - }); - - it("Fetch all", async () => { - // Mock REST API - const data: BusinessServicePage = { - _embedded: { - "business-service": [], - }, - total_count: 0, - }; + const responseData = "hello world!"; - new MockAdapter(axios) - .onGet(`${BUSINESS_SERVICES}?page=0&size=1000&sort=name`) - .reply(200, data); + new MockAdapter(axios).onGet("/myendpoint").reply(200, responseData); // Use hook const { result, waitForNextUpdate } = renderHook(() => - useFetchBusinessServices() + useFetch({ + onFetch: () => { + return axios.get("/myendpoint"); + }, + }) ); - const { - businessServices: items, - isFetching, - fetchError, - fetchAllBusinessServices: fetchAll, - } = result.current; + const { data, isFetching, fetchError, requestFetch } = result.current; expect(isFetching).toBe(false); - expect(items).toBeUndefined(); + expect(data).toBeUndefined(); expect(fetchError).toBeUndefined(); // Init fetch - act(() => fetchAll()); + act(() => requestFetch()); expect(result.current.isFetching).toBe(true); // Fetch finished await waitForNextUpdate(); expect(result.current.isFetching).toBe(false); - expect(result.current.businessServices).toMatchObject({ - data: [], - meta: { count: 0 }, - }); + expect(result.current.data).toBe(responseData); expect(result.current.fetchError).toBeUndefined(); }); }); diff --git a/src/shared/hooks/useFetch/useFetch.ts b/src/shared/hooks/useFetch/useFetch.ts index 26099f60..29a00d2d 100644 --- a/src/shared/hooks/useFetch/useFetch.ts +++ b/src/shared/hooks/useFetch/useFetch.ts @@ -1,14 +1,7 @@ -import { useCallback, useReducer } from "react"; +import { useReducer } from "react"; import { AxiosError, AxiosPromise } from "axios"; import { ActionType, createAsyncAction, getType } from "typesafe-actions"; -import { - getBusinessServices, - BusinessServiceSortByQuery, - BusinessServiceSortBy, -} from "api/rest"; -import { PageRepresentation, BusinessService, PageQuery } from "api/models"; - export const { request: fetchRequest, success: fetchSuccess, @@ -84,7 +77,7 @@ export interface IState { requestFetch: () => void; } -export const useFetchBusinessServices = ({ +export const useFetch = ({ defaultIsFetching = false, onFetch, }: IArgs): IState => { @@ -95,11 +88,7 @@ export const useFetchBusinessServices = ({ onFetch() .then(({ data }) => { - dispatch( - fetchSuccess({ - data, - }) - ); + dispatch(fetchSuccess(data)); }) .catch((error: AxiosError) => { dispatch(fetchFailure(error)); @@ -115,4 +104,4 @@ export const useFetchBusinessServices = ({ }; }; -export default useFetchBusinessServices; +export default useFetch; From 163a01d7c94ba6159d13900db86efac1b36623eb Mon Sep 17 00:00:00 2001 From: Carlos Esteban Feria Vila <2582866+carlosthe19916@users.noreply.github.com> Date: Mon, 19 Apr 2021 18:53:35 +0200 Subject: [PATCH 03/10] Replace old fetchs by new hook useFetch --- src/api/apiUtils.ts | 94 ++++++++++ .../application-inventory.tsx | 73 ++++---- .../application-form/application-form.tsx | 23 ++- .../select-business-service-filter.tsx | 22 ++- .../business-services/business-services.tsx | 52 +++--- .../business-service-form.tsx | 20 ++- .../stakeholder-group-form.tsx | 20 ++- .../stakeholder-groups/stakeholder-groups.tsx | 69 ++++---- .../stakeholder-form/stakeholder-form.tsx | 37 +++- .../controls/stakeholders/stakeholders.tsx | 73 ++++---- src/shared/hooks/index.ts | 5 - src/shared/hooks/useFetch/useFetch.test.tsx | 29 ++- src/shared/hooks/useFetch/useFetch.ts | 20 ++- .../hooks/useFetchApplications/index.ts | 1 - .../useFetchApplications.test.tsx | 82 --------- .../useFetchApplications.ts | 133 -------------- .../hooks/useFetchBusinessServices/index.ts | 1 - .../useFetchBusinessServices.test.tsx | 127 ------------- .../useFetchBusinessServices.ts | 161 ----------------- .../hooks/useFetchJobFunctions/index.ts | 1 - .../useFetchJobFunctions.test.tsx | 51 ------ .../useFetchJobFunctions.ts | 118 ------------- .../hooks/useFetchStakeholderGroups/index.ts | 1 - .../useFetchStakeholderGroups.test.tsx | 125 ------------- .../useFetchStakeholderGroups.ts | 165 ----------------- .../hooks/useFetchStakeholders/index.ts | 1 - .../useFetchStakeholders.test.tsx | 125 ------------- .../useFetchStakeholders.ts | 167 ------------------ 28 files changed, 365 insertions(+), 1431 deletions(-) create mode 100644 src/api/apiUtils.ts delete mode 100644 src/shared/hooks/useFetchApplications/index.ts delete mode 100644 src/shared/hooks/useFetchApplications/useFetchApplications.test.tsx delete mode 100644 src/shared/hooks/useFetchApplications/useFetchApplications.ts delete mode 100644 src/shared/hooks/useFetchBusinessServices/index.ts delete mode 100644 src/shared/hooks/useFetchBusinessServices/useFetchBusinessServices.test.tsx delete mode 100644 src/shared/hooks/useFetchBusinessServices/useFetchBusinessServices.ts delete mode 100644 src/shared/hooks/useFetchJobFunctions/index.ts delete mode 100644 src/shared/hooks/useFetchJobFunctions/useFetchJobFunctions.test.tsx delete mode 100644 src/shared/hooks/useFetchJobFunctions/useFetchJobFunctions.ts delete mode 100644 src/shared/hooks/useFetchStakeholderGroups/index.ts delete mode 100644 src/shared/hooks/useFetchStakeholderGroups/useFetchStakeholderGroups.test.tsx delete mode 100644 src/shared/hooks/useFetchStakeholderGroups/useFetchStakeholderGroups.ts delete mode 100644 src/shared/hooks/useFetchStakeholders/index.ts delete mode 100644 src/shared/hooks/useFetchStakeholders/useFetchStakeholders.test.tsx delete mode 100644 src/shared/hooks/useFetchStakeholders/useFetchStakeholders.ts diff --git a/src/api/apiUtils.ts b/src/api/apiUtils.ts new file mode 100644 index 00000000..f0e12e37 --- /dev/null +++ b/src/api/apiUtils.ts @@ -0,0 +1,94 @@ +import { + Application, + ApplicationPage, + BusinessService, + BusinessServicePage, + JobFunction, + JobFunctionPage, + PageRepresentation, + Stakeholder, + StakeholderGroup, + StakeholderGroupPage, + StakeholderPage, +} from "./models"; +import { + BusinessServiceSortBy, + getBusinessServices, + getJobFunctions, + getStakeholderGroups, + getStakeholders, + JobFunctionSortBy, + StakeholderGroupSortBy, + StakeholderSortBy, +} from "./rest"; + +export const getAllBusinessServices = () => { + return getBusinessServices( + {}, + { page: 1, perPage: 1000 }, + { field: BusinessServiceSortBy.NAME } + ); +}; + +export const getAllStakeholders = () => { + return getStakeholders( + {}, + { page: 1, perPage: 1000 }, + { field: StakeholderSortBy.DISPLAY_NAME } + ); +}; + +export const getAllStakeholderGroups = () => { + return getStakeholderGroups( + {}, + { page: 1, perPage: 1000 }, + { field: StakeholderGroupSortBy.NAME } + ); +}; + +export const getAllJobFunctions = () => { + return getJobFunctions( + {}, + { page: 1, perPage: 1000 }, + { field: JobFunctionSortBy.ROLE } + ); +}; + +// + +export const stakeholderPageMapper = ( + page: StakeholderPage +): PageRepresentation => ({ + meta: { count: page.total_count }, + data: page._embedded.stakeholder, +}); + +export const stakeholderGroupPageMapper = ( + page: StakeholderGroupPage +): PageRepresentation => ({ + meta: { count: page.total_count }, + data: page._embedded["stakeholder-group"], +}); + +export const bussinessServicePageMapper = ( + page: BusinessServicePage +): PageRepresentation => ({ + meta: { count: page.total_count }, + data: page._embedded["business-service"], +}); + +export const jobFunctionPageMapper = ( + page: JobFunctionPage +): PageRepresentation => ({ + meta: { count: page.total_count }, + data: page._embedded["job-function"], +}); + +// + +export const applicationPageMapper = ( + page: ApplicationPage +): PageRepresentation => ({ + meta: { count: page.total_count }, + data: page._embedded.application, +}); diff --git a/src/pages/application-inventory/application-inventory.tsx b/src/pages/application-inventory/application-inventory.tsx index 26877860..d1cfe0aa 100644 --- a/src/pages/application-inventory/application-inventory.tsx +++ b/src/pages/application-inventory/application-inventory.tsx @@ -41,14 +41,21 @@ import { ConditionalRender, NoDataEmptyState, } from "shared/components"; +import { useDeleteApplication, useTableControls, useFetch } from "shared/hooks"; + +import { + Application, + ApplicationPage, + PageRepresentation, + SortByQuery, +} from "api/models"; import { - useDeleteApplication, - useTableControls, - useFetchApplications, -} from "shared/hooks"; + ApplicationSortBy, + ApplicationSortByQuery, + getApplications, +} from "api/rest"; +import { applicationPageMapper } from "api/apiUtils"; -import { Application, SortByQuery } from "api/models"; -import { ApplicationSortBy, ApplicationSortByQuery } from "api/rest"; import { getAxiosErrorMessage } from "utils/utils"; import { NewApplicationModal } from "./components/new-application-modal"; @@ -120,13 +127,6 @@ export const ApplicationInventory: React.FC = () => { const { deleteApplication } = useDeleteApplication(); - const { - applications, - isFetching, - fetchError, - fetchApplications, - } = useFetchApplications(true); - const { paginationQuery, sortByQuery, @@ -136,16 +136,8 @@ export const ApplicationInventory: React.FC = () => { sortByQuery: { direction: "asc", index: 1 }, }); - const { - isItemSelected: isItemExpanded, - toggleItemSelected: toggleItemExpanded, - } = useSelectionState({ - items: applications?.data || [], - isEqual: (a, b) => a.id === b.id, - }); - - const refreshTable = useCallback(() => { - fetchApplications( + const fetchApplications = useCallback(() => { + return getApplications( { name: filtersValue.get(FilterKey.NAME)?.map((f) => f.key), description: filtersValue.get(FilterKey.DESCRIPTION)?.map((f) => f.key), @@ -156,21 +148,30 @@ export const ApplicationInventory: React.FC = () => { paginationQuery, toSortByQuery(sortByQuery) ); - }, [filtersValue, paginationQuery, sortByQuery, fetchApplications]); + }, [filtersValue, paginationQuery, sortByQuery]); + + const { + data: applications, + isFetching, + fetchError, + requestFetch: refreshTable, + } = useFetch>({ + defaultIsFetching: true, + onFetch: fetchApplications, + mapper: applicationPageMapper, + }); useEffect(() => { - fetchApplications( - { - name: filtersValue.get(FilterKey.NAME)?.map((f) => f.key), - description: filtersValue.get(FilterKey.DESCRIPTION)?.map((f) => f.key), - businessService: filtersValue - .get(FilterKey.BUSINESS_SERVICE) - ?.map((f) => f.key), - }, - paginationQuery, - toSortByQuery(sortByQuery) - ); - }, [filtersValue, paginationQuery, sortByQuery, fetchApplications]); + refreshTable(); + }, [filtersValue, paginationQuery, sortByQuery, refreshTable]); + + const { + isItemSelected: isItemExpanded, + toggleItemSelected: toggleItemExpanded, + } = useSelectionState({ + items: applications?.data || [], + isEqual: (a, b) => a.id === b.id, + }); const columns: ICell[] = [ { diff --git a/src/pages/application-inventory/components/application-form/application-form.tsx b/src/pages/application-inventory/components/application-form/application-form.tsx index 1a18dd2c..ac1675d1 100644 --- a/src/pages/application-inventory/components/application-form/application-form.tsx +++ b/src/pages/application-inventory/components/application-form/application-form.tsx @@ -19,16 +19,25 @@ import { SingleSelectFetchFormikField, OptionWithValue, } from "shared/components"; -import { useFetchBusinessServices } from "shared/hooks"; +import { useFetch } from "shared/hooks"; import { DEFAULT_SELECT_MAX_HEIGHT } from "Constants"; import { createApplication, updateApplication } from "api/rest"; -import { Application, BusinessService } from "api/models"; +import { + Application, + BusinessService, + BusinessServicePage, + PageRepresentation, +} from "api/models"; import { getAxiosErrorMessage, getValidatedFromError, getValidatedFromErrorTouched, } from "utils/utils"; +import { + bussinessServicePageMapper, + getAllBusinessServices, +} from "api/apiUtils"; const businesServiceToOption = ( value: BusinessService @@ -60,11 +69,15 @@ export const ApplicationForm: React.FC = ({ const [error, setError] = useState(); const { - businessServices, + data: businessServices, isFetching: isFetchingBusinessServices, fetchError: fetchErrorBusinessServices, - fetchAllBusinessServices, - } = useFetchBusinessServices(); + requestFetch: fetchAllBusinessServices, + } = useFetch>({ + defaultIsFetching: true, + onFetch: getAllBusinessServices, + mapper: bussinessServicePageMapper, + }); useEffect(() => { fetchAllBusinessServices(); diff --git a/src/pages/application-inventory/components/toolbar-search-filter/select-business-service-filter/select-business-service-filter.tsx b/src/pages/application-inventory/components/toolbar-search-filter/select-business-service-filter/select-business-service-filter.tsx index 3a1d8526..3fb8f7de 100644 --- a/src/pages/application-inventory/components/toolbar-search-filter/select-business-service-filter/select-business-service-filter.tsx +++ b/src/pages/application-inventory/components/toolbar-search-filter/select-business-service-filter/select-business-service-filter.tsx @@ -4,9 +4,17 @@ import { useTranslation } from "react-i18next"; import { SelectVariant, ToolbarChip } from "@patternfly/react-core"; import { SimpleSelectFetch, OptionWithValue } from "shared/components"; -import { useFetchBusinessServices } from "shared/hooks"; +import { useFetch } from "shared/hooks"; -import { BusinessService } from "api/models"; +import { + BusinessService, + BusinessServicePage, + PageRepresentation, +} from "api/models"; +import { + bussinessServicePageMapper, + getAllBusinessServices, +} from "api/apiUtils"; import { DEFAULT_SELECT_MAX_HEIGHT } from "Constants"; const businessServiceToToolbarChip = (value: BusinessService): ToolbarChip => ({ @@ -46,11 +54,15 @@ export const SelectBusinessServiceFilter: React.FC = const { t } = useTranslation(); const { - businessServices, + data: businessServices, isFetching: isFetchingBusinessServices, fetchError: fetchErrorBusinessServices, - fetchAllBusinessServices, - } = useFetchBusinessServices(); + requestFetch: fetchAllBusinessServices, + } = useFetch>({ + defaultIsFetching: true, + onFetch: getAllBusinessServices, + mapper: bussinessServicePageMapper, + }); useEffect(() => { fetchAllBusinessServices(); diff --git a/src/pages/controls/business-services/business-services.tsx b/src/pages/controls/business-services/business-services.tsx index 0070e504..94a2cbc0 100644 --- a/src/pages/controls/business-services/business-services.tsx +++ b/src/pages/controls/business-services/business-services.tsx @@ -32,12 +32,22 @@ import { } from "shared/components"; import { useTableControls, - useFetchBusinessServices, + useFetch, useDeleteBusinessService, } from "shared/hooks"; -import { BusinessService, SortByQuery } from "api/models"; -import { BusinessServiceSortBy, BusinessServiceSortByQuery } from "api/rest"; +import { + BusinessService, + BusinessServicePage, + PageRepresentation, + SortByQuery, +} from "api/models"; +import { + BusinessServiceSortBy, + BusinessServiceSortByQuery, + getBusinessServices, +} from "api/rest"; +import { bussinessServicePageMapper } from "api/apiUtils"; import { getAxiosErrorMessage } from "utils/utils"; import { NewBusinessServiceModal } from "./components/new-business-service-modal"; @@ -107,13 +117,6 @@ export const BusinessServices: React.FC = () => { const { deleteBusinessService } = useDeleteBusinessService(); - const { - businessServices, - isFetching, - fetchError, - fetchBusinessServices, - } = useFetchBusinessServices(true); - const { paginationQuery, sortByQuery, @@ -123,8 +126,8 @@ export const BusinessServices: React.FC = () => { sortByQuery: { direction: "asc", index: 0 }, }); - const refreshTable = useCallback(() => { - fetchBusinessServices( + const fetchBusinessServices = useCallback(() => { + return getBusinessServices( { name: filtersValue.get(FilterKey.NAME), description: filtersValue.get(FilterKey.DESCRIPTION), @@ -133,19 +136,22 @@ export const BusinessServices: React.FC = () => { paginationQuery, toSortByQuery(sortByQuery) ); - }, [filtersValue, paginationQuery, sortByQuery, fetchBusinessServices]); + }, [filtersValue, paginationQuery, sortByQuery]); + + const { + data: businessServices, + isFetching, + fetchError, + requestFetch: refreshTable, + } = useFetch>({ + defaultIsFetching: true, + onFetch: fetchBusinessServices, + mapper: bussinessServicePageMapper, + }); useEffect(() => { - fetchBusinessServices( - { - name: filtersValue.get(FilterKey.NAME), - description: filtersValue.get(FilterKey.DESCRIPTION), - owner: filtersValue.get(FilterKey.OWNER), - }, - paginationQuery, - toSortByQuery(sortByQuery) - ); - }, [filtersValue, paginationQuery, sortByQuery, fetchBusinessServices]); + refreshTable(); + }, [filtersValue, paginationQuery, sortByQuery, refreshTable]); const columns: ICell[] = [ { title: t("terms.name"), transforms: [sortable, cellWidth(25)] }, diff --git a/src/pages/controls/business-services/components/business-service-form/business-service-form.tsx b/src/pages/controls/business-services/components/business-service-form/business-service-form.tsx index 1b4f99f2..f5b8533f 100644 --- a/src/pages/controls/business-services/components/business-service-form/business-service-form.tsx +++ b/src/pages/controls/business-services/components/business-service-form/business-service-form.tsx @@ -19,16 +19,22 @@ import { SingleSelectFetchFormikField, OptionWithValue, } from "shared/components"; -import { useFetchStakeholders } from "shared/hooks"; +import { useFetch } from "shared/hooks"; import { DEFAULT_SELECT_MAX_HEIGHT } from "Constants"; import { createBusinessService, updateBusinessService } from "api/rest"; -import { BusinessService, Stakeholder } from "api/models"; +import { + BusinessService, + PageRepresentation, + Stakeholder, + StakeholderPage, +} from "api/models"; import { getAxiosErrorMessage, getValidatedFromError, getValidatedFromErrorTouched, } from "utils/utils"; +import { getAllStakeholders, stakeholderPageMapper } from "api/apiUtils"; const stakeholderToOption = ( value: Stakeholder @@ -59,11 +65,15 @@ export const BusinessServiceForm: React.FC = ({ const [error, setError] = useState(); const { - stakeholders, + data: stakeholders, isFetching: isFetchingStakeholders, fetchError: fetchErrorStakeholders, - fetchAllStakeholders, - } = useFetchStakeholders(); + requestFetch: fetchAllStakeholders, + } = useFetch>({ + defaultIsFetching: true, + onFetch: getAllStakeholders, + mapper: stakeholderPageMapper, + }); useEffect(() => { fetchAllStakeholders(); diff --git a/src/pages/controls/stakeholder-groups/components/stakeholder-group-form/stakeholder-group-form.tsx b/src/pages/controls/stakeholder-groups/components/stakeholder-group-form/stakeholder-group-form.tsx index b828f0bf..cc0f3de6 100644 --- a/src/pages/controls/stakeholder-groups/components/stakeholder-group-form/stakeholder-group-form.tsx +++ b/src/pages/controls/stakeholder-groups/components/stakeholder-group-form/stakeholder-group-form.tsx @@ -19,16 +19,22 @@ import { OptionWithValue, MultiSelectFetchFormikField, } from "shared/components"; -import { useFetchStakeholders } from "shared/hooks"; +import { useFetch } from "shared/hooks"; import { DEFAULT_SELECT_MAX_HEIGHT } from "Constants"; import { createStakeholderGroup, updateStakeholderGroup } from "api/rest"; -import { Stakeholder, StakeholderGroup } from "api/models"; +import { + PageRepresentation, + Stakeholder, + StakeholderGroup, + StakeholderPage, +} from "api/models"; import { getAxiosErrorMessage, getValidatedFromError, getValidatedFromErrorTouched, } from "utils/utils"; +import { getAllStakeholders, stakeholderPageMapper } from "api/apiUtils"; const stakeholderToOption = ( value: Stakeholder @@ -59,11 +65,15 @@ export const StakeholderGroupForm: React.FC = ({ const [error, setError] = useState(); const { - stakeholders, + data: stakeholders, isFetching: isFetchingStakeholders, fetchError: fetchErrorStakeholders, - fetchAllStakeholders, - } = useFetchStakeholders(); + requestFetch: fetchAllStakeholders, + } = useFetch>({ + defaultIsFetching: true, + onFetch: getAllStakeholders, + mapper: stakeholderPageMapper, + }); useEffect(() => { fetchAllStakeholders(); diff --git a/src/pages/controls/stakeholder-groups/stakeholder-groups.tsx b/src/pages/controls/stakeholder-groups/stakeholder-groups.tsx index 7e142901..c14352d0 100644 --- a/src/pages/controls/stakeholder-groups/stakeholder-groups.tsx +++ b/src/pages/controls/stakeholder-groups/stakeholder-groups.tsx @@ -38,13 +38,24 @@ import { } from "shared/components"; import { useTableControls, - useFetchStakeholderGroups, + useFetch, useDeleteStakeholderGroup, } from "shared/hooks"; import { getAxiosErrorMessage } from "utils/utils"; -import { StakeholderGroupSortBy, StakeholderGroupSortByQuery } from "api/rest"; -import { StakeholderGroup, SortByQuery } from "api/models"; + +import { + getStakeholderGroups, + StakeholderGroupSortBy, + StakeholderGroupSortByQuery, +} from "api/rest"; +import { + StakeholderGroup, + SortByQuery, + StakeholderGroupPage, + PageRepresentation, +} from "api/models"; +import { stakeholderGroupPageMapper } from "api/apiUtils"; import { NewStakeholderGroupModal } from "./components/new-stakeholder-group-modal"; import { UpdateStakeholderGroupModal } from "./components/update-stakeholder-group-modal"; @@ -113,13 +124,6 @@ export const StakeholderGroups: React.FC = () => { const { deleteStakeholderGroup } = useDeleteStakeholderGroup(); - const { - stakeholderGroups, - isFetching, - fetchError, - fetchStakeholderGroups, - } = useFetchStakeholderGroups(true); - const { paginationQuery, sortByQuery, @@ -129,16 +133,8 @@ export const StakeholderGroups: React.FC = () => { sortByQuery: { direction: "asc", index: 1 }, }); - const { - isItemSelected: isItemExpanded, - toggleItemSelected: toggleItemExpanded, - } = useSelectionState({ - items: stakeholderGroups?.data || [], - isEqual: (a, b) => a.id === b.id, - }); - - const refreshTable = useCallback(() => { - fetchStakeholderGroups( + const fetchStakeholderGroups = useCallback(() => { + return getStakeholderGroups( { name: filtersValue.get(FilterKey.NAME), description: filtersValue.get(FilterKey.DESCRIPTION), @@ -147,19 +143,30 @@ export const StakeholderGroups: React.FC = () => { paginationQuery, toSortByQuery(sortByQuery) ); - }, [filtersValue, paginationQuery, sortByQuery, fetchStakeholderGroups]); + }, [filtersValue, paginationQuery, sortByQuery]); + + const { + data: stakeholderGroups, + isFetching, + fetchError, + requestFetch: refreshTable, + } = useFetch>({ + defaultIsFetching: true, + onFetch: fetchStakeholderGroups, + mapper: stakeholderGroupPageMapper, + }); useEffect(() => { - fetchStakeholderGroups( - { - name: filtersValue.get(FilterKey.NAME), - description: filtersValue.get(FilterKey.DESCRIPTION), - stakeholder: filtersValue.get(FilterKey.STAKEHOLDER), - }, - paginationQuery, - toSortByQuery(sortByQuery) - ); - }, [filtersValue, paginationQuery, sortByQuery, fetchStakeholderGroups]); + refreshTable(); + }, [filtersValue, paginationQuery, sortByQuery, refreshTable]); + + const { + isItemSelected: isItemExpanded, + toggleItemSelected: toggleItemExpanded, + } = useSelectionState({ + items: stakeholderGroups?.data || [], + isEqual: (a, b) => a.id === b.id, + }); const columns: ICell[] = [ { diff --git a/src/pages/controls/stakeholders/components/stakeholder-form/stakeholder-form.tsx b/src/pages/controls/stakeholders/components/stakeholder-form/stakeholder-form.tsx index 1641be54..58487bf7 100644 --- a/src/pages/controls/stakeholders/components/stakeholder-form/stakeholder-form.tsx +++ b/src/pages/controls/stakeholders/components/stakeholder-form/stakeholder-form.tsx @@ -19,16 +19,29 @@ import { OptionWithValue, MultiSelectFetchFormikField, } from "shared/components"; -import { useFetchStakeholderGroups, useFetchJobFunctions } from "shared/hooks"; +import { useFetch } from "shared/hooks"; import { DEFAULT_SELECT_MAX_HEIGHT } from "Constants"; import { createStakeholder, updateStakeholder } from "api/rest"; -import { JobFunction, Stakeholder, StakeholderGroup } from "api/models"; +import { + JobFunction, + JobFunctionPage, + PageRepresentation, + Stakeholder, + StakeholderGroup, + StakeholderGroupPage, +} from "api/models"; import { getAxiosErrorMessage, getValidatedFromError, getValidatedFromErrorTouched, } from "utils/utils"; +import { + getAllJobFunctions, + getAllStakeholderGroups, + jobFunctionPageMapper, + stakeholderGroupPageMapper, +} from "api/apiUtils"; const jobFunctionToOption = ( value: JobFunction @@ -67,22 +80,30 @@ export const StakeholderForm: React.FC = ({ const [error, setError] = useState(); const { - jobFunctions, + data: jobFunctions, isFetching: isFetchingJobFunctions, fetchError: fetchErrorJobFunctions, - fetchAllJobFunctions, - } = useFetchJobFunctions(); + requestFetch: fetchAllJobFunctions, + } = useFetch>({ + defaultIsFetching: true, + onFetch: getAllJobFunctions, + mapper: jobFunctionPageMapper, + }); useEffect(() => { fetchAllJobFunctions(); }, [fetchAllJobFunctions]); const { - stakeholderGroups, + data: stakeholderGroups, isFetching: isFetchingGroups, fetchError: fetchErrorGroups, - fetchAllStakeholderGroups, - } = useFetchStakeholderGroups(); + requestFetch: fetchAllStakeholderGroups, + } = useFetch>({ + defaultIsFetching: true, + onFetch: getAllStakeholderGroups, + mapper: stakeholderGroupPageMapper, + }); useEffect(() => { fetchAllStakeholderGroups(); diff --git a/src/pages/controls/stakeholders/stakeholders.tsx b/src/pages/controls/stakeholders/stakeholders.tsx index cc1f2dfe..aa99fd2b 100644 --- a/src/pages/controls/stakeholders/stakeholders.tsx +++ b/src/pages/controls/stakeholders/stakeholders.tsx @@ -36,15 +36,21 @@ import { AppTableToolbarToggleGroup, NoDataEmptyState, } from "shared/components"; -import { - useTableControls, - useFetchStakeholders, - useDeleteStakeholder, -} from "shared/hooks"; +import { useTableControls, useDeleteStakeholder, useFetch } from "shared/hooks"; import { getAxiosErrorMessage } from "utils/utils"; -import { StakeholderSortBy, StakeholderSortByQuery } from "api/rest"; -import { Stakeholder, SortByQuery } from "api/models"; +import { + getStakeholders, + StakeholderSortBy, + StakeholderSortByQuery, +} from "api/rest"; +import { + Stakeholder, + SortByQuery, + StakeholderPage, + PageRepresentation, +} from "api/models"; +import { stakeholderPageMapper } from "api/apiUtils"; import { NewStakeholderModal } from "./components/new-stakeholder-modal"; import { UpdateStakeholderModal } from "./components/update-stakeholder-modal"; @@ -124,13 +130,6 @@ export const Stakeholders: React.FC = () => { const { deleteStakeholder } = useDeleteStakeholder(); - const { - stakeholders, - isFetching, - fetchError, - fetchStakeholders, - } = useFetchStakeholders(true); - const { paginationQuery, sortByQuery, @@ -140,16 +139,8 @@ export const Stakeholders: React.FC = () => { sortByQuery: { direction: "asc", index: 1 }, }); - const { - isItemSelected: isItemExpanded, - toggleItemSelected: toggleItemExpanded, - } = useSelectionState({ - items: stakeholders?.data || [], - isEqual: (a, b) => a.id === b.id, - }); - - const refreshTable = useCallback(() => { - fetchStakeholders( + const fetchStakeholders = useCallback(() => { + return getStakeholders( { email: filtersValue.get(FilterKey.EMAIL), displayName: filtersValue.get(FilterKey.DISPLAY_NAME), @@ -159,20 +150,30 @@ export const Stakeholders: React.FC = () => { paginationQuery, toSortByQuery(sortByQuery) ); - }, [filtersValue, paginationQuery, sortByQuery, fetchStakeholders]); + }, [filtersValue, paginationQuery, sortByQuery]); + + const { + data: stakeholders, + isFetching, + fetchError, + requestFetch: refreshTable, + } = useFetch>({ + defaultIsFetching: true, + onFetch: fetchStakeholders, + mapper: stakeholderPageMapper, + }); useEffect(() => { - fetchStakeholders( - { - email: filtersValue.get(FilterKey.EMAIL), - displayName: filtersValue.get(FilterKey.DISPLAY_NAME), - jobFunction: filtersValue.get(FilterKey.JOB_FUNCTION), - stakeholderGroup: filtersValue.get(FilterKey.STAKEHOLDER_GROUP), - }, - paginationQuery, - toSortByQuery(sortByQuery) - ); - }, [filtersValue, paginationQuery, sortByQuery, fetchStakeholders]); + refreshTable(); + }, [filtersValue, paginationQuery, sortByQuery, refreshTable]); + + const { + isItemSelected: isItemExpanded, + toggleItemSelected: toggleItemExpanded, + } = useSelectionState({ + items: stakeholders?.data || [], + isEqual: (a, b) => a.id === b.id, + }); const columns: ICell[] = [ { diff --git a/src/shared/hooks/index.ts b/src/shared/hooks/index.ts index 269e4fbb..5e1c5b4c 100644 --- a/src/shared/hooks/index.ts +++ b/src/shared/hooks/index.ts @@ -3,9 +3,4 @@ export { useDeleteBusinessService } from "./useDeleteBusinessService"; export { useDeleteStakeholder } from "./useDeleteStakeholder"; export { useDeleteStakeholderGroup } from "./useDeleteStakeholderGroup"; export { useFetch } from "./useFetch"; -export { useFetchApplications } from "./useFetchApplications"; -export { useFetchBusinessServices } from "./useFetchBusinessServices"; -export { useFetchJobFunctions } from "./useFetchJobFunctions"; -export { useFetchStakeholderGroups } from "./useFetchStakeholderGroups"; -export { useFetchStakeholders } from "./useFetchStakeholders"; export { useTableControls } from "./useTableControls"; diff --git a/src/shared/hooks/useFetch/useFetch.test.tsx b/src/shared/hooks/useFetch/useFetch.test.tsx index 9c9a3c01..11bc7c6e 100644 --- a/src/shared/hooks/useFetch/useFetch.test.tsx +++ b/src/shared/hooks/useFetch/useFetch.test.tsx @@ -5,13 +5,33 @@ import { renderHook, act } from "@testing-library/react-hooks"; import { useFetch } from "./useFetch"; describe("useFetch", () => { + it("Initial value", async () => { + // Use hook + const { result, waitForNextUpdate } = renderHook(() => + useFetch({ + defaultIsFetching: true, + onFetch: () => axios.get("/myendpoint"), + mapper: (t: number) => t.toString(), + }) + ); + + const { data, isFetching, fetchError, requestFetch } = result.current; + + expect(isFetching).toBe(true); + expect(data).toBeUndefined(); + expect(fetchError).toBeUndefined(); + }); + it("Fetch error due to no REST API found", async () => { // Mock REST API new MockAdapter(axios).onGet("/myendpoint").networkError(); // Use hook const { result, waitForNextUpdate } = renderHook(() => - useFetch({ onFetch: () => axios.get("/myendpoint") }) + useFetch({ + onFetch: () => axios.get("/myendpoint"), + mapper: (t: number) => t.toString(), + }) ); const { data, isFetching, fetchError, requestFetch } = result.current; @@ -33,16 +53,17 @@ describe("useFetch", () => { it("Fetch success", async () => { // Mock REST API - const responseData = "hello world!"; + const responseData = 123; new MockAdapter(axios).onGet("/myendpoint").reply(200, responseData); // Use hook const { result, waitForNextUpdate } = renderHook(() => - useFetch({ + useFetch({ onFetch: () => { return axios.get("/myendpoint"); }, + mapper: (t: number) => "Hello world " + t, }) ); @@ -59,7 +80,7 @@ describe("useFetch", () => { // Fetch finished await waitForNextUpdate(); expect(result.current.isFetching).toBe(false); - expect(result.current.data).toBe(responseData); + expect(result.current.data).toBe("Hello world 123"); expect(result.current.fetchError).toBeUndefined(); }); }); diff --git a/src/shared/hooks/useFetch/useFetch.ts b/src/shared/hooks/useFetch/useFetch.ts index 29a00d2d..566bcf3a 100644 --- a/src/shared/hooks/useFetch/useFetch.ts +++ b/src/shared/hooks/useFetch/useFetch.ts @@ -1,4 +1,4 @@ -import { useReducer } from "react"; +import { useCallback, useReducer } from "react"; import { AxiosError, AxiosPromise } from "axios"; import { ActionType, createAsyncAction, getType } from "typesafe-actions"; @@ -64,36 +64,38 @@ const reducer = (state: State, action: Action): State => { } }; -export interface IArgs { +export interface IArgs { defaultIsFetching?: boolean; onFetch: () => AxiosPromise; + mapper: (t: T) => R; } -export interface IState { - data?: T; +export interface IState { + data?: R; isFetching: boolean; fetchError?: AxiosError; fetchCount: number; requestFetch: () => void; } -export const useFetch = ({ +export const useFetch = ({ defaultIsFetching = false, onFetch, -}: IArgs): IState => { + mapper, +}: IArgs): IState => { const [state, dispatch] = useReducer(reducer, defaultIsFetching, initReducer); - const fetchHandler = () => { + const fetchHandler = useCallback(() => { dispatch(fetchRequest()); onFetch() .then(({ data }) => { - dispatch(fetchSuccess(data)); + dispatch(fetchSuccess(mapper(data))); }) .catch((error: AxiosError) => { dispatch(fetchFailure(error)); }); - }; + }, [onFetch, mapper]); return { data: state.data, diff --git a/src/shared/hooks/useFetchApplications/index.ts b/src/shared/hooks/useFetchApplications/index.ts deleted file mode 100644 index 8ecdca30..00000000 --- a/src/shared/hooks/useFetchApplications/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { useFetchApplications } from "./useFetchApplications"; diff --git a/src/shared/hooks/useFetchApplications/useFetchApplications.test.tsx b/src/shared/hooks/useFetchApplications/useFetchApplications.test.tsx deleted file mode 100644 index 55090da5..00000000 --- a/src/shared/hooks/useFetchApplications/useFetchApplications.test.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import axios from "axios"; -import MockAdapter from "axios-mock-adapter"; -import { renderHook, act } from "@testing-library/react-hooks"; -import { useFetchApplications } from "./useFetchApplications"; -import { ApplicationPage } from "api/models"; -import { APPLICATIONS } from "api/rest"; - -describe("useFetchApplications", () => { - it("Fetch error due to no REST API found", async () => { - // Mock REST API - new MockAdapter(axios).onGet(APPLICATIONS).networkError(); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => - useFetchApplications() - ); - - const { - applications: items, - isFetching, - fetchError, - fetchApplications: fetchPage, - } = result.current; - - expect(isFetching).toBe(false); - expect(items).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => fetchPage({}, { page: 2, perPage: 50 })); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.applications).toBeUndefined(); - expect(result.current.fetchError).not.toBeUndefined(); - }); - - it("Fetch success", async () => { - // Mock REST API - const data: ApplicationPage = { - _embedded: { - application: [], - }, - total_count: 0, - }; - - new MockAdapter(axios) - .onGet(`${APPLICATIONS}?page=0&size=10`) - .reply(200, data); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => - useFetchApplications() - ); - - const { - applications: items, - isFetching, - fetchError, - fetchApplications: fetchPage, - } = result.current; - - expect(isFetching).toBe(false); - expect(items).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => fetchPage({}, { page: 1, perPage: 10 })); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.applications).toMatchObject({ - data: [], - meta: { count: 0 }, - }); - expect(result.current.fetchError).toBeUndefined(); - }); -}); diff --git a/src/shared/hooks/useFetchApplications/useFetchApplications.ts b/src/shared/hooks/useFetchApplications/useFetchApplications.ts deleted file mode 100644 index 735e52cd..00000000 --- a/src/shared/hooks/useFetchApplications/useFetchApplications.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { useCallback, useReducer } from "react"; -import { AxiosError } from "axios"; -import { ActionType, createAsyncAction, getType } from "typesafe-actions"; - -import { getApplications, ApplicationSortByQuery } from "api/rest"; -import { PageRepresentation, Application, PageQuery } from "api/models"; - -export const { - request: fetchRequest, - success: fetchSuccess, - failure: fetchFailure, -} = createAsyncAction( - "useFetchApplications/fetch/request", - "useFetchApplications/fetch/success", - "useFetchApplications/fetch/failure" -), AxiosError>(); - -type State = Readonly<{ - isFetching: boolean; - applications?: PageRepresentation; - fetchError?: AxiosError; - fetchCount: number; -}>; - -const defaultState: State = { - isFetching: false, - applications: undefined, - fetchError: undefined, - fetchCount: 0, -}; - -type Action = ActionType< - typeof fetchRequest | typeof fetchSuccess | typeof fetchFailure ->; - -const initReducer = (isFetching: boolean): State => { - return { - ...defaultState, - isFetching, - }; -}; - -const reducer = (state: State, action: Action): State => { - switch (action.type) { - case getType(fetchRequest): - return { - ...state, - isFetching: true, - }; - case getType(fetchSuccess): - return { - ...state, - isFetching: false, - fetchError: undefined, - applications: action.payload, - fetchCount: state.fetchCount + 1, - }; - case getType(fetchFailure): - return { - ...state, - isFetching: false, - fetchError: action.payload, - fetchCount: state.fetchCount + 1, - }; - default: - return state; - } -}; - -export interface IState { - applications?: PageRepresentation; - isFetching: boolean; - fetchError?: AxiosError; - fetchCount: number; - fetchApplications: ( - filters: { - name?: string[]; - description?: string[]; - businessService?: string[]; - }, - page: PageQuery, - sortBy?: ApplicationSortByQuery - ) => void; -} - -export const useFetchApplications = ( - defaultIsFetching: boolean = false -): IState => { - const [state, dispatch] = useReducer(reducer, defaultIsFetching, initReducer); - - const fetchApplications = useCallback( - ( - filters: { - name?: string[]; - description?: string[]; - businessService?: string[]; - }, - page: PageQuery, - sortBy?: ApplicationSortByQuery - ) => { - dispatch(fetchRequest()); - - getApplications(filters, page, sortBy) - .then(({ data }) => { - const list = data._embedded.application; - const total = data.total_count; - - dispatch( - fetchSuccess({ - data: list, - meta: { - count: total, - }, - }) - ); - }) - .catch((error: AxiosError) => { - dispatch(fetchFailure(error)); - }); - }, - [] - ); - - return { - applications: state.applications, - isFetching: state.isFetching, - fetchError: state.fetchError, - fetchCount: state.fetchCount, - fetchApplications, - }; -}; - -export default useFetchApplications; diff --git a/src/shared/hooks/useFetchBusinessServices/index.ts b/src/shared/hooks/useFetchBusinessServices/index.ts deleted file mode 100644 index e066df53..00000000 --- a/src/shared/hooks/useFetchBusinessServices/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { useFetchBusinessServices } from "./useFetchBusinessServices"; diff --git a/src/shared/hooks/useFetchBusinessServices/useFetchBusinessServices.test.tsx b/src/shared/hooks/useFetchBusinessServices/useFetchBusinessServices.test.tsx deleted file mode 100644 index d6d6265d..00000000 --- a/src/shared/hooks/useFetchBusinessServices/useFetchBusinessServices.test.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import axios from "axios"; -import MockAdapter from "axios-mock-adapter"; -import { renderHook, act } from "@testing-library/react-hooks"; -import { useFetchBusinessServices } from "./useFetchBusinessServices"; -import { BusinessServicePage } from "api/models"; -import { BUSINESS_SERVICES } from "api/rest"; - -describe("useFetchBusinessServices", () => { - it("Fetch error due to no REST API found", async () => { - // Mock REST API - new MockAdapter(axios).onGet(BUSINESS_SERVICES).networkError(); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => - useFetchBusinessServices() - ); - - const { - businessServices, - isFetching, - fetchError, - fetchBusinessServices, - } = result.current; - - expect(isFetching).toBe(false); - expect(businessServices).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => fetchBusinessServices({}, { page: 2, perPage: 50 })); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.businessServices).toBeUndefined(); - expect(result.current.fetchError).not.toBeUndefined(); - }); - - it("Fetch success", async () => { - // Mock REST API - const data: BusinessServicePage = { - _embedded: { - "business-service": [], - }, - total_count: 0, - }; - - new MockAdapter(axios) - .onGet(`${BUSINESS_SERVICES}?page=0&size=10&name=something`) - .reply(200, data); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => - useFetchBusinessServices() - ); - - const { - businessServices, - isFetching, - fetchError, - fetchBusinessServices, - } = result.current; - - expect(isFetching).toBe(false); - expect(businessServices).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => - fetchBusinessServices({ name: ["something"] }, { page: 1, perPage: 10 }) - ); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.businessServices).toMatchObject({ - data: [], - meta: { count: 0 }, - }); - expect(result.current.fetchError).toBeUndefined(); - }); - - it("Fetch all", async () => { - // Mock REST API - const data: BusinessServicePage = { - _embedded: { - "business-service": [], - }, - total_count: 0, - }; - - new MockAdapter(axios) - .onGet(`${BUSINESS_SERVICES}?page=0&size=1000&sort=name`) - .reply(200, data); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => - useFetchBusinessServices() - ); - - const { - businessServices: items, - isFetching, - fetchError, - fetchAllBusinessServices: fetchAll, - } = result.current; - - expect(isFetching).toBe(false); - expect(items).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => fetchAll()); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.businessServices).toMatchObject({ - data: [], - meta: { count: 0 }, - }); - expect(result.current.fetchError).toBeUndefined(); - }); -}); diff --git a/src/shared/hooks/useFetchBusinessServices/useFetchBusinessServices.ts b/src/shared/hooks/useFetchBusinessServices/useFetchBusinessServices.ts deleted file mode 100644 index 1c401f4b..00000000 --- a/src/shared/hooks/useFetchBusinessServices/useFetchBusinessServices.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { useCallback, useReducer } from "react"; -import { AxiosError } from "axios"; -import { ActionType, createAsyncAction, getType } from "typesafe-actions"; - -import { - getBusinessServices, - BusinessServiceSortByQuery, - BusinessServiceSortBy, -} from "api/rest"; -import { PageRepresentation, BusinessService, PageQuery } from "api/models"; - -export const { - request: fetchRequest, - success: fetchSuccess, - failure: fetchFailure, -} = createAsyncAction( - "useFetchBusinessServices/fetch/request", - "useFetchBusinessServices/fetch/success", - "useFetchBusinessServices/fetch/failure" -), AxiosError>(); - -type State = Readonly<{ - isFetching: boolean; - businessServices?: PageRepresentation; - fetchError?: AxiosError; - fetchCount: number; -}>; - -const defaultState: State = { - isFetching: false, - businessServices: undefined, - fetchError: undefined, - fetchCount: 0, -}; - -type Action = ActionType< - typeof fetchRequest | typeof fetchSuccess | typeof fetchFailure ->; - -const initReducer = (isFetching: boolean): State => { - return { - ...defaultState, - isFetching, - }; -}; - -const reducer = (state: State, action: Action): State => { - switch (action.type) { - case getType(fetchRequest): - return { - ...state, - isFetching: true, - }; - case getType(fetchSuccess): - return { - ...state, - isFetching: false, - fetchError: undefined, - businessServices: action.payload, - fetchCount: state.fetchCount + 1, - }; - case getType(fetchFailure): - return { - ...state, - isFetching: false, - fetchError: action.payload, - fetchCount: state.fetchCount + 1, - }; - default: - return state; - } -}; - -export interface IState { - businessServices?: PageRepresentation; - isFetching: boolean; - fetchError?: AxiosError; - fetchCount: number; - fetchBusinessServices: ( - filters: { - name?: string[]; - description?: string[]; - owner?: string[]; - }, - page: PageQuery, - sortBy?: BusinessServiceSortByQuery - ) => void; - fetchAllBusinessServices: () => void; -} - -export const useFetchBusinessServices = ( - defaultIsFetching: boolean = false -): IState => { - const [state, dispatch] = useReducer(reducer, defaultIsFetching, initReducer); - - const fetchBusinessServices = useCallback( - ( - filters: { name?: string[]; description?: string[]; owner?: string[] }, - page: PageQuery, - sortBy?: BusinessServiceSortByQuery - ) => { - dispatch(fetchRequest()); - - getBusinessServices(filters, page, sortBy) - .then(({ data }) => { - const list = data._embedded["business-service"]; - const total = data.total_count; - - dispatch( - fetchSuccess({ - data: list, - meta: { - count: total, - }, - }) - ); - }) - .catch((error: AxiosError) => { - dispatch(fetchFailure(error)); - }); - }, - [] - ); - - const fetchAllBusinessServices = useCallback(() => { - dispatch(fetchRequest()); - - getBusinessServices( - {}, - { page: 1, perPage: 1000 }, - { field: BusinessServiceSortBy.NAME } - ) - .then(({ data }) => { - const list = data._embedded["business-service"]; - const total = data.total_count; - - dispatch( - fetchSuccess({ - data: list, - meta: { - count: total, - }, - }) - ); - }) - .catch((error: AxiosError) => { - dispatch(fetchFailure(error)); - }); - }, []); - - return { - businessServices: state.businessServices, - isFetching: state.isFetching, - fetchError: state.fetchError, - fetchCount: state.fetchCount, - fetchBusinessServices, - fetchAllBusinessServices, - }; -}; - -export default useFetchBusinessServices; diff --git a/src/shared/hooks/useFetchJobFunctions/index.ts b/src/shared/hooks/useFetchJobFunctions/index.ts deleted file mode 100644 index afb12de4..00000000 --- a/src/shared/hooks/useFetchJobFunctions/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { useFetchJobFunctions } from "./useFetchJobFunctions"; diff --git a/src/shared/hooks/useFetchJobFunctions/useFetchJobFunctions.test.tsx b/src/shared/hooks/useFetchJobFunctions/useFetchJobFunctions.test.tsx deleted file mode 100644 index 1ae2ab80..00000000 --- a/src/shared/hooks/useFetchJobFunctions/useFetchJobFunctions.test.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import axios from "axios"; -import MockAdapter from "axios-mock-adapter"; -import { renderHook, act } from "@testing-library/react-hooks"; -import { useFetchJobFunctions } from "./useFetchJobFunctions"; -import { JobFunctionPage } from "api/models"; -import { JOB_FUNCTIONS } from "api/rest"; - -describe("useFetchJobFunctions", () => { - it("Fetch all", async () => { - // Mock REST API - const data: JobFunctionPage = { - _embedded: { - "job-function": [], - }, - total_count: 0, - }; - - new MockAdapter(axios) - .onGet(`${JOB_FUNCTIONS}?page=0&size=1000&sort=role`) - .reply(200, data); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => - useFetchJobFunctions() - ); - - const { - jobFunctions: items, - isFetching, - fetchError, - fetchAllJobFunctions: fetchAll, - } = result.current; - - expect(isFetching).toBe(false); - expect(items).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => fetchAll()); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.jobFunctions).toMatchObject({ - data: [], - meta: { count: 0 }, - }); - expect(result.current.fetchError).toBeUndefined(); - }); -}); diff --git a/src/shared/hooks/useFetchJobFunctions/useFetchJobFunctions.ts b/src/shared/hooks/useFetchJobFunctions/useFetchJobFunctions.ts deleted file mode 100644 index 118c1650..00000000 --- a/src/shared/hooks/useFetchJobFunctions/useFetchJobFunctions.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { useCallback, useReducer } from "react"; -import { AxiosError } from "axios"; -import { ActionType, createAsyncAction, getType } from "typesafe-actions"; - -import { getJobFunctions, JobFunctionSortBy } from "api/rest"; -import { PageRepresentation, JobFunction } from "api/models"; - -export const { - request: fetchRequest, - success: fetchSuccess, - failure: fetchFailure, -} = createAsyncAction( - "useFetchJobFunctions/fetch/request", - "useFetchJobFunctions/fetch/success", - "useFetchJobFunctions/fetch/failure" -), AxiosError>(); - -type State = Readonly<{ - isFetching: boolean; - jobFunctions?: PageRepresentation; - fetchError?: AxiosError; - fetchCount: number; -}>; - -const defaultState: State = { - isFetching: false, - jobFunctions: undefined, - fetchError: undefined, - fetchCount: 0, -}; - -type Action = ActionType< - typeof fetchRequest | typeof fetchSuccess | typeof fetchFailure ->; - -const initReducer = (isFetching: boolean): State => { - return { - ...defaultState, - isFetching, - }; -}; - -const reducer = (state: State, action: Action): State => { - switch (action.type) { - case getType(fetchRequest): - return { - ...state, - isFetching: true, - }; - case getType(fetchSuccess): - return { - ...state, - isFetching: false, - fetchError: undefined, - jobFunctions: action.payload, - fetchCount: state.fetchCount + 1, - }; - case getType(fetchFailure): - return { - ...state, - isFetching: false, - fetchError: action.payload, - fetchCount: state.fetchCount + 1, - }; - default: - return state; - } -}; - -export interface IState { - jobFunctions?: PageRepresentation; - isFetching: boolean; - fetchError?: AxiosError; - fetchCount: number; - fetchAllJobFunctions: () => void; -} - -export const useFetchJobFunctions = ( - defaultIsFetching: boolean = false -): IState => { - const [state, dispatch] = useReducer(reducer, defaultIsFetching, initReducer); - - const fetchAllJobFunctions = useCallback(() => { - dispatch(fetchRequest()); - - getJobFunctions( - {}, - { page: 1, perPage: 1000 }, - { field: JobFunctionSortBy.ROLE } - ) - .then(({ data }) => { - const list = data._embedded["job-function"]; - const total = data.total_count; - - dispatch( - fetchSuccess({ - data: list, - meta: { - count: total, - }, - }) - ); - }) - .catch((error: AxiosError) => { - dispatch(fetchFailure(error)); - }); - }, []); - - return { - jobFunctions: state.jobFunctions, - isFetching: state.isFetching, - fetchError: state.fetchError, - fetchCount: state.fetchCount, - fetchAllJobFunctions, - }; -}; - -export default useFetchJobFunctions; diff --git a/src/shared/hooks/useFetchStakeholderGroups/index.ts b/src/shared/hooks/useFetchStakeholderGroups/index.ts deleted file mode 100644 index 91061bf6..00000000 --- a/src/shared/hooks/useFetchStakeholderGroups/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { useFetchStakeholderGroups } from "./useFetchStakeholderGroups"; diff --git a/src/shared/hooks/useFetchStakeholderGroups/useFetchStakeholderGroups.test.tsx b/src/shared/hooks/useFetchStakeholderGroups/useFetchStakeholderGroups.test.tsx deleted file mode 100644 index ab3a4c1d..00000000 --- a/src/shared/hooks/useFetchStakeholderGroups/useFetchStakeholderGroups.test.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import axios from "axios"; -import MockAdapter from "axios-mock-adapter"; -import { renderHook, act } from "@testing-library/react-hooks"; -import { useFetchStakeholderGroups } from "./useFetchStakeholderGroups"; -import { StakeholderGroupPage } from "api/models"; -import { STAKEHOLDER_GROUPS } from "api/rest"; - -describe("useFetchStakeholderGroups", () => { - it("Fetch error due to no REST API found", async () => { - // Mock REST API - new MockAdapter(axios).onGet(STAKEHOLDER_GROUPS).networkError(); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => - useFetchStakeholderGroups() - ); - - const { - stakeholderGroups: stakeholders, - isFetching, - fetchError, - fetchStakeholderGroups, - } = result.current; - - expect(isFetching).toBe(false); - expect(stakeholders).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => fetchStakeholderGroups({}, { page: 2, perPage: 50 })); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.stakeholderGroups).toBeUndefined(); - expect(result.current.fetchError).not.toBeUndefined(); - }); - - it("Fetch success", async () => { - // Mock REST API - const data: StakeholderGroupPage = { - _embedded: { - "stakeholder-group": [], - }, - total_count: 0, - }; - - new MockAdapter(axios) - .onGet(`${STAKEHOLDER_GROUPS}?page=0&size=10`) - .reply(200, data); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => - useFetchStakeholderGroups() - ); - - const { - stakeholderGroups: stakeholders, - isFetching, - fetchError, - fetchStakeholderGroups: fetchStakeholders, - } = result.current; - - expect(isFetching).toBe(false); - expect(stakeholders).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => fetchStakeholders({}, { page: 1, perPage: 10 })); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.stakeholderGroups).toMatchObject({ - data: [], - meta: { count: 0 }, - }); - expect(result.current.fetchError).toBeUndefined(); - }); - - it("Fetch all", async () => { - // Mock REST API - const data: StakeholderGroupPage = { - _embedded: { - "stakeholder-group": [], - }, - total_count: 0, - }; - - new MockAdapter(axios) - .onGet(`${STAKEHOLDER_GROUPS}?page=0&size=1000&sort=name`) - .reply(200, data); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => - useFetchStakeholderGroups() - ); - - const { - stakeholderGroups, - isFetching, - fetchError, - fetchAllStakeholderGroups, - } = result.current; - - expect(isFetching).toBe(false); - expect(stakeholderGroups).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => fetchAllStakeholderGroups()); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.stakeholderGroups).toMatchObject({ - data: [], - meta: { count: 0 }, - }); - expect(result.current.fetchError).toBeUndefined(); - }); -}); diff --git a/src/shared/hooks/useFetchStakeholderGroups/useFetchStakeholderGroups.ts b/src/shared/hooks/useFetchStakeholderGroups/useFetchStakeholderGroups.ts deleted file mode 100644 index 312c4925..00000000 --- a/src/shared/hooks/useFetchStakeholderGroups/useFetchStakeholderGroups.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { useCallback, useReducer } from "react"; -import { AxiosError } from "axios"; -import { ActionType, createAsyncAction, getType } from "typesafe-actions"; - -import { - getStakeholderGroups, - StakeholderGroupSortBy, - StakeholderGroupSortByQuery, -} from "api/rest"; -import { PageRepresentation, StakeholderGroup, PageQuery } from "api/models"; - -export const { - request: fetchRequest, - success: fetchSuccess, - failure: fetchFailure, -} = createAsyncAction( - "useFetchStakeholderGroups/fetch/request", - "useFetchStakeholderGroups/fetch/success", - "useFetchStakeholderGroups/fetch/failure" -), AxiosError>(); - -type State = Readonly<{ - isFetching: boolean; - stakeholderGroups?: PageRepresentation; - fetchError?: AxiosError; - fetchCount: number; -}>; - -const defaultState: State = { - isFetching: false, - stakeholderGroups: undefined, - fetchError: undefined, - fetchCount: 0, -}; - -type Action = ActionType< - typeof fetchRequest | typeof fetchSuccess | typeof fetchFailure ->; - -const initReducer = (isFetching: boolean): State => { - return { - ...defaultState, - isFetching, - }; -}; - -const reducer = (state: State, action: Action): State => { - switch (action.type) { - case getType(fetchRequest): - return { - ...state, - isFetching: true, - }; - case getType(fetchSuccess): - return { - ...state, - isFetching: false, - fetchError: undefined, - stakeholderGroups: action.payload, - fetchCount: state.fetchCount + 1, - }; - case getType(fetchFailure): - return { - ...state, - isFetching: false, - fetchError: action.payload, - fetchCount: state.fetchCount + 1, - }; - default: - return state; - } -}; - -export interface IState { - stakeholderGroups?: PageRepresentation; - isFetching: boolean; - fetchError?: AxiosError; - fetchCount: number; - fetchStakeholderGroups: ( - filters: { - name?: string[]; - description?: string[]; - stakeholder?: string[]; - }, - page: PageQuery, - sortBy?: StakeholderGroupSortByQuery - ) => void; - fetchAllStakeholderGroups: () => void; -} - -export const useFetchStakeholderGroups = ( - defaultIsFetching: boolean = false -): IState => { - const [state, dispatch] = useReducer(reducer, defaultIsFetching, initReducer); - - const fetchStakeholderGroups = useCallback( - ( - filters: { - name?: string[]; - description?: string[]; - stakeholder?: string[]; - }, - page: PageQuery, - sortBy?: StakeholderGroupSortByQuery - ) => { - dispatch(fetchRequest()); - - getStakeholderGroups(filters, page, sortBy) - .then(({ data }) => { - const list = data._embedded["stakeholder-group"]; - const total = data.total_count; - - dispatch( - fetchSuccess({ - data: list, - meta: { - count: total, - }, - }) - ); - }) - .catch((error: AxiosError) => { - dispatch(fetchFailure(error)); - }); - }, - [] - ); - - const fetchAllStakeholderGroups = useCallback(() => { - dispatch(fetchRequest()); - - getStakeholderGroups( - {}, - { page: 1, perPage: 1000 }, - { field: StakeholderGroupSortBy.NAME } - ) - .then(({ data }) => { - const list = data._embedded["stakeholder-group"]; - const total = data.total_count; - - dispatch( - fetchSuccess({ - data: list, - meta: { - count: total, - }, - }) - ); - }) - .catch((error: AxiosError) => { - dispatch(fetchFailure(error)); - }); - }, []); - - return { - stakeholderGroups: state.stakeholderGroups, - isFetching: state.isFetching, - fetchError: state.fetchError, - fetchCount: state.fetchCount, - fetchStakeholderGroups, - fetchAllStakeholderGroups, - }; -}; - -export default useFetchStakeholderGroups; diff --git a/src/shared/hooks/useFetchStakeholders/index.ts b/src/shared/hooks/useFetchStakeholders/index.ts deleted file mode 100644 index 6908b875..00000000 --- a/src/shared/hooks/useFetchStakeholders/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { useFetchStakeholders } from "./useFetchStakeholders"; diff --git a/src/shared/hooks/useFetchStakeholders/useFetchStakeholders.test.tsx b/src/shared/hooks/useFetchStakeholders/useFetchStakeholders.test.tsx deleted file mode 100644 index bac3bb88..00000000 --- a/src/shared/hooks/useFetchStakeholders/useFetchStakeholders.test.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import axios from "axios"; -import MockAdapter from "axios-mock-adapter"; -import { renderHook, act } from "@testing-library/react-hooks"; -import { useFetchStakeholders } from "./useFetchStakeholders"; -import { StakeholderPage } from "api/models"; -import { STAKEHOLDERS } from "api/rest"; - -describe("useFetchStakeholders", () => { - it("Fetch error due to no REST API found", async () => { - // Mock REST API - new MockAdapter(axios).onGet(STAKEHOLDERS).networkError(); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => - useFetchStakeholders() - ); - - const { - stakeholders, - isFetching, - fetchError, - fetchStakeholders, - } = result.current; - - expect(isFetching).toBe(false); - expect(stakeholders).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => fetchStakeholders({}, { page: 2, perPage: 50 })); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.stakeholders).toBeUndefined(); - expect(result.current.fetchError).not.toBeUndefined(); - }); - - it("Fetch success", async () => { - // Mock REST API - const data: StakeholderPage = { - _embedded: { - stakeholder: [], - }, - total_count: 0, - }; - - new MockAdapter(axios) - .onGet(`${STAKEHOLDERS}?page=0&size=10`) - .reply(200, data); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => - useFetchStakeholders() - ); - - const { - stakeholders, - isFetching, - fetchError, - fetchStakeholders, - } = result.current; - - expect(isFetching).toBe(false); - expect(stakeholders).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => fetchStakeholders({}, { page: 1, perPage: 10 })); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.stakeholders).toMatchObject({ - data: [], - meta: { count: 0 }, - }); - expect(result.current.fetchError).toBeUndefined(); - }); - - it("Fetch all", async () => { - // Mock REST API - const data: StakeholderPage = { - _embedded: { - stakeholder: [], - }, - total_count: 0, - }; - - new MockAdapter(axios) - .onGet(`${STAKEHOLDERS}?page=0&size=1000&sort=displayName`) - .reply(200, data); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => - useFetchStakeholders() - ); - - const { - stakeholders, - isFetching, - fetchError, - fetchAllStakeholders, - } = result.current; - - expect(isFetching).toBe(false); - expect(stakeholders).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => fetchAllStakeholders()); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.stakeholders).toMatchObject({ - data: [], - meta: { count: 0 }, - }); - expect(result.current.fetchError).toBeUndefined(); - }); -}); diff --git a/src/shared/hooks/useFetchStakeholders/useFetchStakeholders.ts b/src/shared/hooks/useFetchStakeholders/useFetchStakeholders.ts deleted file mode 100644 index d4057a75..00000000 --- a/src/shared/hooks/useFetchStakeholders/useFetchStakeholders.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { useCallback, useReducer } from "react"; -import { AxiosError } from "axios"; -import { ActionType, createAsyncAction, getType } from "typesafe-actions"; - -import { - getStakeholders, - StakeholderSortBy, - StakeholderSortByQuery, -} from "api/rest"; -import { PageRepresentation, Stakeholder, PageQuery } from "api/models"; - -export const { - request: fetchRequest, - success: fetchSuccess, - failure: fetchFailure, -} = createAsyncAction( - "useFetchStakeholders/fetch/request", - "useFetchStakeholders/fetch/success", - "useFetchStakeholders/fetch/failure" -), AxiosError>(); - -type State = Readonly<{ - isFetching: boolean; - stakeholders?: PageRepresentation; - fetchError?: AxiosError; - fetchCount: number; -}>; - -const defaultState: State = { - isFetching: false, - stakeholders: undefined, - fetchError: undefined, - fetchCount: 0, -}; - -type Action = ActionType< - typeof fetchRequest | typeof fetchSuccess | typeof fetchFailure ->; - -const initReducer = (isFetching: boolean): State => { - return { - ...defaultState, - isFetching, - }; -}; - -const reducer = (state: State, action: Action): State => { - switch (action.type) { - case getType(fetchRequest): - return { - ...state, - isFetching: true, - }; - case getType(fetchSuccess): - return { - ...state, - isFetching: false, - fetchError: undefined, - stakeholders: action.payload, - fetchCount: state.fetchCount + 1, - }; - case getType(fetchFailure): - return { - ...state, - isFetching: false, - fetchError: action.payload, - fetchCount: state.fetchCount + 1, - }; - default: - return state; - } -}; - -export interface IState { - stakeholders?: PageRepresentation; - isFetching: boolean; - fetchError?: AxiosError; - fetchCount: number; - fetchStakeholders: ( - filters: { - email?: string[]; - displayName?: string[]; - jobFunction?: string[]; - stakeholderGroup?: string[]; - }, - page: PageQuery, - sortBy?: StakeholderSortByQuery - ) => void; - fetchAllStakeholders: () => void; -} - -export const useFetchStakeholders = ( - defaultIsFetching: boolean = false -): IState => { - const [state, dispatch] = useReducer(reducer, defaultIsFetching, initReducer); - - const fetchStakeholders = useCallback( - ( - filters: { - email?: string[]; - displayName?: string[]; - jobFunction?: string[]; - stakeholderGroups?: string[]; - }, - page: PageQuery, - sortBy?: StakeholderSortByQuery - ) => { - dispatch(fetchRequest()); - - getStakeholders(filters, page, sortBy) - .then(({ data }) => { - const list = data._embedded.stakeholder; - const total = data.total_count; - - dispatch( - fetchSuccess({ - data: list, - meta: { - count: total, - }, - }) - ); - }) - .catch((error: AxiosError) => { - dispatch(fetchFailure(error)); - }); - }, - [] - ); - - const fetchAllStakeholders = useCallback(() => { - dispatch(fetchRequest()); - - getStakeholders( - {}, - { page: 1, perPage: 1000 }, - { field: StakeholderSortBy.DISPLAY_NAME } - ) - .then(({ data }) => { - const list = data._embedded.stakeholder; - const total = data.total_count; - - dispatch( - fetchSuccess({ - data: list, - meta: { - count: total, - }, - }) - ); - }) - .catch((error: AxiosError) => { - dispatch(fetchFailure(error)); - }); - }, []); - - return { - stakeholders: state.stakeholders, - isFetching: state.isFetching, - fetchError: state.fetchError, - fetchCount: state.fetchCount, - fetchStakeholders, - fetchAllStakeholders, - }; -}; - -export default useFetchStakeholders; From 84b04852ee852be72bbf1a26b540008082f0da45 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Feria Vila <2582866+carlosthe19916@users.noreply.github.com> Date: Fri, 23 Apr 2021 10:35:19 +0200 Subject: [PATCH 04/10] change useFetchJobFunction by useFetch --- .../controls/job-functions/job-functions.tsx | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/pages/controls/job-functions/job-functions.tsx b/src/pages/controls/job-functions/job-functions.tsx index 134fdfad..e6638f22 100644 --- a/src/pages/controls/job-functions/job-functions.tsx +++ b/src/pages/controls/job-functions/job-functions.tsx @@ -24,15 +24,21 @@ import { NoDataEmptyState, SearchFilter, } from "shared/components"; -import { - useTableControls, - useDeleteJobFunction, - useFetchJobFunctions, -} from "shared/hooks"; +import { useTableControls, useDeleteJobFunction, useFetch } from "shared/hooks"; import { getAxiosErrorMessage } from "utils/utils"; -import { JobFunctionSortBy, JobFunctionSortByQuery } from "api/rest"; -import { SortByQuery, JobFunction } from "api/models"; +import { + getJobFunctions, + JobFunctionSortBy, + JobFunctionSortByQuery, +} from "api/rest"; +import { + SortByQuery, + JobFunction, + JobFunctionPage, + PageRepresentation, +} from "api/models"; +import { jobFunctionPageMapper } from "api/apiUtils"; import { NewJobFunctionModal } from "./components/new-job-function-modal"; import { UpdateJobFunctionModal } from "./components/update-job-function-modal"; @@ -84,13 +90,6 @@ export const JobFunctions: React.FC = () => { const { deleteJobFunction } = useDeleteJobFunction(); - const { - jobFunctions, - isFetching, - fetchError, - fetchJobFunctions, - } = useFetchJobFunctions(true); - const { paginationQuery, sortByQuery, @@ -100,25 +99,30 @@ export const JobFunctions: React.FC = () => { sortByQuery: { direction: "asc", index: 0 }, }); - const refreshTable = useCallback(() => { - fetchJobFunctions( + const fetchJobFunctions = useCallback(() => { + return getJobFunctions( { role: filtersValue.get(FilterKey.NAME), }, paginationQuery, toSortByQuery(sortByQuery) ); - }, [filtersValue, paginationQuery, sortByQuery, fetchJobFunctions]); + }, [filtersValue, paginationQuery, sortByQuery]); + + const { + data: jobFunctions, + isFetching, + fetchError, + requestFetch: refreshTable, + } = useFetch>({ + defaultIsFetching: true, + onFetch: fetchJobFunctions, + mapper: jobFunctionPageMapper, + }); useEffect(() => { - fetchJobFunctions( - { - role: filtersValue.get(FilterKey.NAME), - }, - paginationQuery, - toSortByQuery(sortByQuery) - ); - }, [filtersValue, paginationQuery, sortByQuery, fetchJobFunctions]); + refreshTable(); + }, [filtersValue, paginationQuery, sortByQuery, refreshTable]); // From 01fb1d68b04b435971ffd1022c17e9ff5c34dd73 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Feria Vila <2582866+carlosthe19916@users.noreply.github.com> Date: Mon, 26 Apr 2021 09:44:23 +0200 Subject: [PATCH 05/10] Merge main --- .../application-list/application-list.tsx | 60 ++++++++++--------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/src/pages/application-inventory/application-list/application-list.tsx b/src/pages/application-inventory/application-list/application-list.tsx index 61b8eea4..4b2abf72 100644 --- a/src/pages/application-inventory/application-list/application-list.tsx +++ b/src/pages/application-inventory/application-list/application-list.tsx @@ -43,15 +43,23 @@ import { ConditionalRender, NoDataEmptyState, } from "shared/components"; -import { - useDeleteApplication, - useTableControls, - useFetchApplications, -} from "shared/hooks"; + +import { useDeleteApplication, useTableControls, useFetch } from "shared/hooks"; import { formatPath, Paths } from "Paths"; -import { Application, Assessment, SortByQuery } from "api/models"; -import { ApplicationSortBy, ApplicationSortByQuery } from "api/rest"; +import { + Application, + ApplicationPage, + Assessment, + PageRepresentation, + SortByQuery, +} from "api/models"; +import { + ApplicationSortBy, + ApplicationSortByQuery, + getApplications, +} from "api/rest"; +import { applicationPageMapper } from "api/apiUtils"; import { getAxiosErrorMessage } from "utils/utils"; import { NewApplicationModal } from "./components/new-application-modal"; @@ -130,13 +138,6 @@ export const ApplicationList: React.FC = () => { inProgress: isApplicationAssessInProgress, } = useAssessApplication(); - const { - applications, - isFetching, - fetchError, - fetchApplications, - } = useFetchApplications(true); - const { paginationQuery, sortByQuery, @@ -146,8 +147,8 @@ export const ApplicationList: React.FC = () => { sortByQuery: { direction: "asc", index: 2 }, }); - const refreshTable = useCallback(() => { - fetchApplications( + const fetchApplications = useCallback(() => { + return getApplications( { name: filtersValue.get(FilterKey.NAME)?.map((f) => f.key), description: filtersValue.get(FilterKey.DESCRIPTION)?.map((f) => f.key), @@ -158,21 +159,22 @@ export const ApplicationList: React.FC = () => { paginationQuery, toSortByQuery(sortByQuery) ); - }, [filtersValue, paginationQuery, sortByQuery, fetchApplications]); + }, [filtersValue, paginationQuery, sortByQuery]); + + const { + data: applications, + isFetching, + fetchError, + requestFetch: refreshTable, + } = useFetch>({ + defaultIsFetching: true, + onFetch: fetchApplications, + mapper: applicationPageMapper, + }); useEffect(() => { - fetchApplications( - { - name: filtersValue.get(FilterKey.NAME)?.map((f) => f.key), - description: filtersValue.get(FilterKey.DESCRIPTION)?.map((f) => f.key), - businessService: filtersValue - .get(FilterKey.BUSINESS_SERVICE) - ?.map((f) => f.key), - }, - paginationQuery, - toSortByQuery(sortByQuery) - ); - }, [filtersValue, paginationQuery, sortByQuery, fetchApplications]); + refreshTable(); + }, [filtersValue, paginationQuery, sortByQuery, refreshTable]); // Expansion and selection of rows From 50fde2c9fa4b2bb7b865895a53b61a87bd2be7f4 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Feria Vila <2582866+carlosthe19916@users.noreply.github.com> Date: Mon, 26 Apr 2021 10:09:18 +0200 Subject: [PATCH 06/10] useFetch on assessment --- .../application-assessment.tsx | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/pages/application-inventory/application-assessment/application-assessment.tsx b/src/pages/application-inventory/application-assessment/application-assessment.tsx index c85e7e0d..d190d59c 100644 --- a/src/pages/application-inventory/application-assessment/application-assessment.tsx +++ b/src/pages/application-inventory/application-assessment/application-assessment.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useState } from "react"; import { useHistory, useParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { FormikHelpers, FormikProvider, useFormik } from "formik"; +import { AxiosError } from "axios"; import { Bullseye, @@ -20,20 +21,29 @@ import { PageHeader, AppPlaceholder, } from "shared/components"; -import { useFetchStakeholderGroups, useFetchStakeholders } from "shared/hooks"; +import { useFetch } from "shared/hooks"; import { AssessmentRoute, Paths } from "Paths"; import { Application, Assessment, + PageRepresentation, Stakeholder, StakeholderGroup, + StakeholderGroupPage, + StakeholderPage, } from "api/models"; import { getApplicationById, getAssessmentById } from "api/rest"; import { CustomWizardFooter } from "./components/custom-wizard-footer"; import { StakeholdersForm } from "./components/stakeholders-form"; -import { AxiosError } from "axios"; + +import { + getAllStakeholderGroups, + getAllStakeholders, + stakeholderGroupPageMapper, + stakeholderPageMapper, +} from "api/apiUtils"; enum StepId { Stakeholders = 1, @@ -110,11 +120,15 @@ export const ApplicationAssessment: React.FC = () => { // Fetch stakeholders const { - stakeholders, + data: stakeholders, isFetching: isFetchingStakeholders, fetchError: fetchErrorStakeholders, - fetchAllStakeholders, - } = useFetchStakeholders(); + requestFetch: fetchAllStakeholders, + } = useFetch>({ + defaultIsFetching: true, + onFetch: getAllStakeholders, + mapper: stakeholderPageMapper, + }); useEffect(() => { fetchAllStakeholders(); @@ -123,11 +137,15 @@ export const ApplicationAssessment: React.FC = () => { // Fetch stakeholder groups const { - stakeholderGroups, + data: stakeholderGroups, isFetching: isFetchingStakeholderGroups, fetchError: fetchErrorStakeholderGroups, - fetchAllStakeholderGroups, - } = useFetchStakeholderGroups(); + requestFetch: fetchAllStakeholderGroups, + } = useFetch>({ + defaultIsFetching: true, + onFetch: getAllStakeholderGroups, + mapper: stakeholderGroupPageMapper, + }); useEffect(() => { fetchAllStakeholderGroups(); From f6bcf7333efd4ca7d4317b69c888340201a10401 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Feria Vila <2582866+carlosthe19916@users.noreply.github.com> Date: Tue, 4 May 2021 12:02:47 +0200 Subject: [PATCH 07/10] Add useFetch on tag/tagTypes --- src/api/apiUtils.ts | 45 ++++++++++ .../application-dependencies-form.tsx | 82 +++++++++++++------ .../application-form/application-form.tsx | 23 ++++-- .../select-tag-filter/select-tag-filter.tsx | 21 +++-- .../tags/components/tag-form/tag-form.tsx | 15 ++-- src/pages/controls/tags/tags.tsx | 65 ++++++++------- src/shared/hooks/index.ts | 7 -- 7 files changed, 182 insertions(+), 76 deletions(-) diff --git a/src/api/apiUtils.ts b/src/api/apiUtils.ts index f0e12e37..ad587f70 100644 --- a/src/api/apiUtils.ts +++ b/src/api/apiUtils.ts @@ -1,5 +1,7 @@ import { Application, + ApplicationDependency, + ApplicationDependencyPage, ApplicationPage, BusinessService, BusinessServicePage, @@ -10,16 +12,24 @@ import { StakeholderGroup, StakeholderGroupPage, StakeholderPage, + TagType, + TagTypePage, } from "./models"; import { + ApplicationSortBy, BusinessServiceSortBy, + getApplicationDependencies, + getApplications, getBusinessServices, getJobFunctions, getStakeholderGroups, getStakeholders, + getTagTypes, JobFunctionSortBy, StakeholderGroupSortBy, StakeholderSortBy, + TagTypeSortBy, + TagTypeSortByQuery, } from "./rest"; export const getAllBusinessServices = () => { @@ -54,6 +64,27 @@ export const getAllJobFunctions = () => { ); }; +export const getAllTagTypes = ( + sortBy: TagTypeSortByQuery = { field: TagTypeSortBy.NAME } +) => { + return getTagTypes({}, { page: 1, perPage: 1000 }, sortBy); +}; + +export const getAllApplications = () => { + return getApplications( + {}, + { page: 1, perPage: 1000 }, + { field: ApplicationSortBy.NAME } + ); +}; + +export const getAllApplicationDependencies = (filters: { + from?: string[]; + to?: string[]; +}) => { + return getApplicationDependencies(filters, { page: 1, perPage: 1000 }); +}; + // export const stakeholderPageMapper = ( @@ -84,6 +115,13 @@ export const jobFunctionPageMapper = ( data: page._embedded["job-function"], }); +export const tagTypePageMapper = ( + page: TagTypePage +): PageRepresentation => ({ + meta: { count: page.total_count }, + data: page._embedded["tag-type"], +}); + // export const applicationPageMapper = ( @@ -92,3 +130,10 @@ export const applicationPageMapper = ( meta: { count: page.total_count }, data: page._embedded.application, }); + +export const applicationDependencyPageMapper = ( + page: ApplicationDependencyPage +): PageRepresentation => ({ + meta: { count: page.total_count }, + data: page._embedded["applications-dependency"], +}); diff --git a/src/pages/application-inventory/application-list/components/application-dependencies-form/application-dependencies-form.tsx b/src/pages/application-inventory/application-list/components/application-dependencies-form/application-dependencies-form.tsx index 1161fd15..6febe98b 100644 --- a/src/pages/application-inventory/application-list/components/application-dependencies-form/application-dependencies-form.tsx +++ b/src/pages/application-inventory/application-list/components/application-dependencies-form/application-dependencies-form.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useState } from "react"; +import React, { useCallback, useContext, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { @@ -15,16 +15,26 @@ import { } from "@patternfly/react-core"; import { OptionWithValue } from "shared/components"; +import { useFetch } from "shared/hooks"; + import { - useFetchApplicationDependencies, - useFetchApplications, -} from "shared/hooks"; + Application, + ApplicationDependency, + ApplicationDependencyPage, + ApplicationPage, + PageRepresentation, +} from "api/models"; -import { Application, ApplicationDependency } from "api/models"; +import { + applicationDependencyPageMapper, + applicationPageMapper, + getAllApplicationDependencies, + getAllApplications, +} from "api/apiUtils"; +import { getAxiosErrorMessage } from "utils/utils"; import { FormContext } from "./form-context"; import { SelectDependency } from "./select-dependency"; -import { getAxiosErrorMessage } from "utils/utils"; const northToStringFn = (value: ApplicationDependency) => value.from.name; const southToStringFn = (value: ApplicationDependency) => value.to.name; @@ -68,40 +78,66 @@ export const ApplicationDependenciesForm: React.FC { + return getAllApplicationDependencies({ + to: [`${application.id}`], + }); + }, [application]); + + const getAllSouthApplicationDependencies = useCallback(() => { + return getAllApplicationDependencies({ + from: [`${application.id}`], + }); + }, [application]); + const { - applicationDependencies: northDependencies, + data: northDependencies, isFetching: isFetchingNorthDependencies, fetchError: fetchErrorNorthDependencies, - fetchAllApplicationDependencies: fetchAllNorthDependencies, - } = useFetchApplicationDependencies(); + requestFetch: fetchAllNorthDependencies, + } = useFetch< + ApplicationDependencyPage, + PageRepresentation + >({ + defaultIsFetching: true, + onFetch: getAllNorthApplicationDependencies, + mapper: applicationDependencyPageMapper, + }); const { - applicationDependencies: southDependencies, + data: southDependencies, isFetching: isFetchingSouthDependencies, fetchError: fetchErrorSouthDependencies, - fetchAllApplicationDependencies: fetchAllSouthDependencies, - } = useFetchApplicationDependencies(); + requestFetch: fetchAllSouthDependencies, + } = useFetch< + ApplicationDependencyPage, + PageRepresentation + >({ + defaultIsFetching: true, + onFetch: getAllSouthApplicationDependencies, + mapper: applicationDependencyPageMapper, + }); useEffect(() => { - fetchAllNorthDependencies({ - to: [`${application.id}`], - }); - }, [application, fetchAllNorthDependencies]); + fetchAllNorthDependencies(); + }, [fetchAllNorthDependencies]); useEffect(() => { - fetchAllSouthDependencies({ - from: [`${application.id}`], - }); - }, [application, fetchAllSouthDependencies]); + fetchAllSouthDependencies(); + }, [fetchAllSouthDependencies]); // Applications const { - applications, + data: applications, isFetching: isFetchingApplications, fetchError: fetchErrorApplications, - fetchAllApplications, - } = useFetchApplications(); + requestFetch: fetchAllApplications, + } = useFetch>({ + defaultIsFetching: true, + onFetch: getAllApplications, + mapper: applicationPageMapper, + }); useEffect(() => { fetchAllApplications(); diff --git a/src/pages/application-inventory/application-list/components/application-form/application-form.tsx b/src/pages/application-inventory/application-list/components/application-form/application-form.tsx index d7b9c5e6..5b15b5ba 100644 --- a/src/pages/application-inventory/application-list/components/application-form/application-form.tsx +++ b/src/pages/application-inventory/application-list/components/application-form/application-form.tsx @@ -24,13 +24,14 @@ import { useFetch } from "shared/hooks"; import { DEFAULT_SELECT_MAX_HEIGHT } from "Constants"; import { createApplication, TagTypeSortBy, updateApplication } from "api/rest"; -import { Application, BusinessService, Tag } from "api/models"; -import { createApplication, updateApplication } from "api/rest"; import { Application, BusinessService, BusinessServicePage, PageRepresentation, + Tag, + TagType, + TagTypePage, } from "api/models"; import { getAxiosErrorMessage, @@ -40,8 +41,14 @@ import { import { bussinessServicePageMapper, getAllBusinessServices, + getAllTagTypes, + tagTypePageMapper, } from "api/apiUtils"; +export const getAllTagTypesSortedByRank = () => { + return getAllTagTypes({ field: TagTypeSortBy.RANK }); +}; + const businesServiceToOption = ( value: BusinessService ): OptionWithValue => ({ @@ -100,14 +107,18 @@ export const ApplicationForm: React.FC = ({ // TagTypes const { - tagTypes, + data: tagTypes, isFetching: isFetchingTagTypes, fetchError: fetchErrorTagTypes, - fetchAllTagTypes, - } = useFetchTagTypes(); + requestFetch: fetchAllTagTypes, + } = useFetch>({ + defaultIsFetching: true, + onFetch: getAllTagTypesSortedByRank, + mapper: tagTypePageMapper, + }); useEffect(() => { - fetchAllTagTypes({ field: TagTypeSortBy.RANK }); + fetchAllTagTypes(); }, [fetchAllTagTypes]); // Tags diff --git a/src/pages/application-inventory/application-list/components/toolbar-search-filter/select-tag-filter/select-tag-filter.tsx b/src/pages/application-inventory/application-list/components/toolbar-search-filter/select-tag-filter/select-tag-filter.tsx index 6a483de5..55abc818 100644 --- a/src/pages/application-inventory/application-list/components/toolbar-search-filter/select-tag-filter/select-tag-filter.tsx +++ b/src/pages/application-inventory/application-list/components/toolbar-search-filter/select-tag-filter/select-tag-filter.tsx @@ -4,12 +4,17 @@ import { useTranslation } from "react-i18next"; import { SelectVariant, ToolbarChip } from "@patternfly/react-core"; import { SimpleSelectFetch, OptionWithValue } from "shared/components"; -import { useFetchTagTypes } from "shared/hooks"; +import { useFetch } from "shared/hooks"; -import { Tag } from "api/models"; import { DEFAULT_SELECT_MAX_HEIGHT } from "Constants"; +import { PageRepresentation, Tag, TagType, TagTypePage } from "api/models"; +import { getAllTagTypes, tagTypePageMapper } from "api/apiUtils"; import { TagTypeSortBy } from "api/rest"; +export const getAllTagTypesSortedByRank = () => { + return getAllTagTypes({ field: TagTypeSortBy.RANK }); +}; + const tagToToolbarChip = (value: Tag): ToolbarChip => ({ key: `${value.id}`, node: value.name, @@ -50,14 +55,18 @@ export const SelectTagFilter: React.FC = ({ // Tag types const { - tagTypes, + data: tagTypes, isFetching: isFetchingTagTypes, fetchError: fetchErrorTagTypes, - fetchAllTagTypes, - } = useFetchTagTypes(); + requestFetch: fetchAllTagTypes, + } = useFetch>({ + defaultIsFetching: true, + onFetch: getAllTagTypesSortedByRank, + mapper: tagTypePageMapper, + }); useEffect(() => { - fetchAllTagTypes({ field: TagTypeSortBy.RANK }); + fetchAllTagTypes(); }, [fetchAllTagTypes]); // Tags diff --git a/src/pages/controls/tags/components/tag-form/tag-form.tsx b/src/pages/controls/tags/components/tag-form/tag-form.tsx index 0e823ccc..c622512c 100644 --- a/src/pages/controls/tags/components/tag-form/tag-form.tsx +++ b/src/pages/controls/tags/components/tag-form/tag-form.tsx @@ -18,16 +18,17 @@ import { SingleSelectFetchFormikField, OptionWithValue, } from "shared/components"; -import { useFetchTagTypes } from "shared/hooks"; +import { useFetch } from "shared/hooks"; import { DEFAULT_SELECT_MAX_HEIGHT } from "Constants"; import { createTag, updateTag } from "api/rest"; -import { Tag, TagType } from "api/models"; +import { PageRepresentation, Tag, TagType, TagTypePage } from "api/models"; import { getAxiosErrorMessage, getValidatedFromError, getValidatedFromErrorTouched, } from "utils/utils"; +import { getAllTagTypes, tagTypePageMapper } from "api/apiUtils"; const tagTypeToOption = (value: TagType): OptionWithValue => ({ value, @@ -50,11 +51,15 @@ export const TagForm: React.FC = ({ tag, onSaved, onCancel }) => { const [error, setError] = useState(); const { - tagTypes, + data: tagTypes, isFetching: isFetchingTagTypes, fetchError: fetchErrorTagTypes, - fetchAllTagTypes, - } = useFetchTagTypes(); + requestFetch: fetchAllTagTypes, + } = useFetch>({ + defaultIsFetching: true, + onFetch: getAllTagTypes, + mapper: tagTypePageMapper, + }); useEffect(() => { fetchAllTagTypes(); diff --git a/src/pages/controls/tags/tags.tsx b/src/pages/controls/tags/tags.tsx index e1713c34..73451ab3 100644 --- a/src/pages/controls/tags/tags.tsx +++ b/src/pages/controls/tags/tags.tsx @@ -36,13 +36,20 @@ import { import { useTableControls, useDeleteTagType, - useFetchTagTypes, + useFetch, useDeleteTag, } from "shared/hooks"; import { getAxiosErrorMessage } from "utils/utils"; -import { TagTypeSortBy, TagTypeSortByQuery } from "api/rest"; -import { SortByQuery, Tag, TagType } from "api/models"; +import { getTagTypes, TagTypeSortBy, TagTypeSortByQuery } from "api/rest"; +import { + PageRepresentation, + SortByQuery, + Tag, + TagType, + TagTypePage, +} from "api/models"; +import { tagTypePageMapper } from "api/apiUtils"; import { NewTagTypeModal } from "./components/new-tag-type-modal"; import { UpdateTagTypeModal } from "./components/update-tag-type-modal"; @@ -119,10 +126,6 @@ export const Tags: React.FC = () => { const { deleteTagType } = useDeleteTagType(); const { deleteTag } = useDeleteTag(); - const { tagTypes, isFetching, fetchError, fetchTagTypes } = useFetchTagTypes( - true - ); - const { paginationQuery, sortByQuery, @@ -132,16 +135,8 @@ export const Tags: React.FC = () => { sortByQuery: { direction: "asc", index: 1 }, }); - const { - isItemSelected: isItemExpanded, - toggleItemSelected: toggleItemExpanded, - } = useSelectionState({ - items: tagTypes?.data || [], - isEqual: (a, b) => a.id === b.id, - }); - - const refreshTable = useCallback(() => { - fetchTagTypes( + const fetchTagTypes = useCallback(() => { + return getTagTypes( { tagTypes: filtersValue.get(FilterKey.TAG_TYPE), tags: filtersValue.get(FilterKey.TAG), @@ -149,19 +144,31 @@ export const Tags: React.FC = () => { paginationQuery, toSortByQuery(sortByQuery) ); - }, [filtersValue, paginationQuery, sortByQuery, fetchTagTypes]); + }, [filtersValue, paginationQuery, sortByQuery]); + + const { + data: tagTypes, + isFetching: isFetchingTagTypes, + fetchError: fetchErrorTagTypes, + requestFetch: refreshTable, + } = useFetch>({ + defaultIsFetching: true, + onFetch: fetchTagTypes, + mapper: tagTypePageMapper, + }); useEffect(() => { - fetchTagTypes( - { - tagTypes: filtersValue.get(FilterKey.TAG_TYPE), - tags: filtersValue.get(FilterKey.TAG), - }, - paginationQuery, - toSortByQuery(sortByQuery) - ); + fetchTagTypes(); }, [filtersValue, paginationQuery, sortByQuery, fetchTagTypes]); + const { + isItemSelected: isItemExpanded, + toggleItemSelected: toggleItemExpanded, + } = useSelectionState({ + items: tagTypes?.data || [], + isEqual: (a, b) => a.id === b.id, + }); + // const deleteTagFromTable = (row: Tag) => { @@ -401,7 +408,7 @@ export const Tags: React.FC = () => { return ( <> } > { columns={columns} rows={rows} // actions={actions} - isLoading={isFetching} + isLoading={isFetchingTagTypes} loadingVariant="skeleton" - fetchError={fetchError} + fetchError={fetchErrorTagTypes} clearAllFilters={handleOnClearAllFilters} filtersApplied={ Array.from(filtersValue.values()).reduce( diff --git a/src/shared/hooks/index.ts b/src/shared/hooks/index.ts index db3081df..6f33f0aa 100644 --- a/src/shared/hooks/index.ts +++ b/src/shared/hooks/index.ts @@ -3,14 +3,7 @@ export { useDeleteBusinessService } from "./useDeleteBusinessService"; export { useDeleteJobFunction } from "./useDeleteJobFunction"; export { useDeleteStakeholder } from "./useDeleteStakeholder"; export { useDeleteStakeholderGroup } from "./useDeleteStakeholderGroup"; -export { useFetchApplicationDependencies } from "./useFetchApplicationDependencies"; export { useDeleteTag } from "./useDeleteTag"; export { useDeleteTagType } from "./useDeleteTagType"; -export { useFetchApplications } from "./useFetchApplications"; -export { useFetchBusinessServices } from "./useFetchBusinessServices"; -export { useFetchJobFunctions } from "./useFetchJobFunctions"; -export { useFetchStakeholderGroups } from "./useFetchStakeholderGroups"; -export { useFetchStakeholders } from "./useFetchStakeholders"; -export { useFetchTagTypes } from "./useFetchTagTypes"; export { useFetch } from "./useFetch"; export { useTableControls } from "./useTableControls"; From bf3bd3438ef720e9fa08a7e3114dfbe904eaba9e Mon Sep 17 00:00:00 2001 From: Carlos Esteban Feria Vila <2582866+carlosthe19916@users.noreply.github.com> Date: Mon, 10 May 2021 18:08:09 +0200 Subject: [PATCH 08/10] Fix refreshTable on tagTypes --- src/pages/controls/tags/tags.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/controls/tags/tags.tsx b/src/pages/controls/tags/tags.tsx index 73451ab3..b37a8704 100644 --- a/src/pages/controls/tags/tags.tsx +++ b/src/pages/controls/tags/tags.tsx @@ -158,8 +158,8 @@ export const Tags: React.FC = () => { }); useEffect(() => { - fetchTagTypes(); - }, [filtersValue, paginationQuery, sortByQuery, fetchTagTypes]); + refreshTable(); + }, [filtersValue, paginationQuery, sortByQuery, refreshTable]); const { isItemSelected: isItemExpanded, From 58a0cff932cc3955ae1a8f34286186cc63abb185 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Feria Vila <2582866+carlosthe19916@users.noreply.github.com> Date: Fri, 14 May 2021 10:44:55 +0200 Subject: [PATCH 09/10] Remove custom useFeth comming from master and replace them by useFetch --- .../stakeholders-form/stakeholders-form.tsx | 40 ++++- .../useFetchApplicationDependencies/index.ts | 1 - .../useFetchApplicationDependencies.test.tsx | 51 ------ .../useFetchApplicationDependencies.ts | 120 ------------- src/shared/hooks/useFetchTagTypes/index.ts | 1 - .../useFetchTagTypes.test.tsx | 119 ------------- .../useFetchTagTypes/useFetchTagTypes.ts | 159 ------------------ 7 files changed, 33 insertions(+), 458 deletions(-) delete mode 100644 src/shared/hooks/useFetchApplicationDependencies/index.ts delete mode 100644 src/shared/hooks/useFetchApplicationDependencies/useFetchApplicationDependencies.test.tsx delete mode 100644 src/shared/hooks/useFetchApplicationDependencies/useFetchApplicationDependencies.ts delete mode 100644 src/shared/hooks/useFetchTagTypes/index.ts delete mode 100644 src/shared/hooks/useFetchTagTypes/useFetchTagTypes.test.tsx delete mode 100644 src/shared/hooks/useFetchTagTypes/useFetchTagTypes.ts diff --git a/src/pages/application-inventory/application-assessment/components/stakeholders-form/stakeholders-form.tsx b/src/pages/application-inventory/application-assessment/components/stakeholders-form/stakeholders-form.tsx index f1a3bc6e..afef9cbb 100644 --- a/src/pages/application-inventory/application-assessment/components/stakeholders-form/stakeholders-form.tsx +++ b/src/pages/application-inventory/application-assessment/components/stakeholders-form/stakeholders-form.tsx @@ -11,11 +11,25 @@ import { TextContent, } from "@patternfly/react-core"; -import { useFetchStakeholderGroups, useFetchStakeholders } from "shared/hooks"; +import { useFetch } from "shared/hooks"; import { DEFAULT_SELECT_MAX_HEIGHT } from "Constants"; import { getValidatedFromError } from "utils/utils"; +import { + PageRepresentation, + Stakeholder, + StakeholderGroup, + StakeholderGroupPage, + StakeholderPage, +} from "api/models"; +import { + getAllStakeholderGroups, + getAllStakeholders, + stakeholderGroupPageMapper, + stakeholderPageMapper, +} from "api/apiUtils"; + import { IFormValues } from "../../form-utils"; import { StakeholderSelect } from "./stakeholder-select"; @@ -27,23 +41,35 @@ export const StakeholdersForm: React.FC = () => { const { t } = useTranslation(); const formik = useFormikContext(); + // Fetch stakeholders + const { - stakeholders, + data: stakeholders, isFetching: isFetchingStakeholders, fetchError: fetchErrorStakeholders, - fetchAllStakeholders, - } = useFetchStakeholders(); + requestFetch: fetchAllStakeholders, + } = useFetch>({ + defaultIsFetching: true, + onFetch: getAllStakeholders, + mapper: stakeholderPageMapper, + }); useEffect(() => { fetchAllStakeholders(); }, [fetchAllStakeholders]); + // Fetch stakeholder groups + const { - stakeholderGroups, + data: stakeholderGroups, isFetching: isFetchingStakeholderGroups, fetchError: fetchErrorStakeholderGroups, - fetchAllStakeholderGroups, - } = useFetchStakeholderGroups(); + requestFetch: fetchAllStakeholderGroups, + } = useFetch>({ + defaultIsFetching: true, + onFetch: getAllStakeholderGroups, + mapper: stakeholderGroupPageMapper, + }); useEffect(() => { fetchAllStakeholderGroups(); diff --git a/src/shared/hooks/useFetchApplicationDependencies/index.ts b/src/shared/hooks/useFetchApplicationDependencies/index.ts deleted file mode 100644 index a47b2956..00000000 --- a/src/shared/hooks/useFetchApplicationDependencies/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { useFetchApplicationDependencies } from "./useFetchApplicationDependencies"; diff --git a/src/shared/hooks/useFetchApplicationDependencies/useFetchApplicationDependencies.test.tsx b/src/shared/hooks/useFetchApplicationDependencies/useFetchApplicationDependencies.test.tsx deleted file mode 100644 index b0f81682..00000000 --- a/src/shared/hooks/useFetchApplicationDependencies/useFetchApplicationDependencies.test.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import axios from "axios"; -import MockAdapter from "axios-mock-adapter"; -import { renderHook, act } from "@testing-library/react-hooks"; -import { useFetchApplicationDependencies } from "./useFetchApplicationDependencies"; -import { ApplicationDependencyPage } from "api/models"; -import { APPLICATION_DEPENDENCY } from "api/rest"; - -describe("useFetchApplicationDependencies", () => { - it("Fetch all", async () => { - // Mock REST API - const data: ApplicationDependencyPage = { - _embedded: { - "applications-dependency": [], - }, - total_count: 0, - }; - - new MockAdapter(axios) - .onGet(`${APPLICATION_DEPENDENCY}?page=0&size=1000&from.id=1`) - .reply(200, data); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => - useFetchApplicationDependencies() - ); - - const { - applicationDependencies: items, - isFetching, - fetchError, - fetchAllApplicationDependencies: fetchAll, - } = result.current; - - expect(isFetching).toBe(false); - expect(items).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => fetchAll({ from: ["1"] })); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.applicationDependencies).toMatchObject({ - data: [], - meta: { count: 0 }, - }); - expect(result.current.fetchError).toBeUndefined(); - }); -}); diff --git a/src/shared/hooks/useFetchApplicationDependencies/useFetchApplicationDependencies.ts b/src/shared/hooks/useFetchApplicationDependencies/useFetchApplicationDependencies.ts deleted file mode 100644 index 8149d5a5..00000000 --- a/src/shared/hooks/useFetchApplicationDependencies/useFetchApplicationDependencies.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { useCallback, useReducer } from "react"; -import { AxiosError } from "axios"; -import { ActionType, createAsyncAction, getType } from "typesafe-actions"; - -import { getApplicationDependencies } from "api/rest"; -import { PageRepresentation, ApplicationDependency } from "api/models"; - -export const { - request: fetchRequest, - success: fetchSuccess, - failure: fetchFailure, -} = createAsyncAction( - "useFetchApplicationDependencies/fetch/request", - "useFetchApplicationDependencies/fetch/success", - "useFetchApplicationDependencies/fetch/failure" -), AxiosError>(); - -type State = Readonly<{ - isFetching: boolean; - applicationDependencies?: PageRepresentation; - fetchError?: AxiosError; - fetchCount: number; -}>; - -const defaultState: State = { - isFetching: false, - applicationDependencies: undefined, - fetchError: undefined, - fetchCount: 0, -}; - -type Action = ActionType< - typeof fetchRequest | typeof fetchSuccess | typeof fetchFailure ->; - -const initReducer = (isFetching: boolean): State => { - return { - ...defaultState, - isFetching, - }; -}; - -const reducer = (state: State, action: Action): State => { - switch (action.type) { - case getType(fetchRequest): - return { - ...state, - isFetching: true, - }; - case getType(fetchSuccess): - return { - ...state, - isFetching: false, - fetchError: undefined, - applicationDependencies: action.payload, - fetchCount: state.fetchCount + 1, - }; - case getType(fetchFailure): - return { - ...state, - isFetching: false, - fetchError: action.payload, - fetchCount: state.fetchCount + 1, - }; - default: - return state; - } -}; - -export interface IState { - applicationDependencies?: PageRepresentation; - isFetching: boolean; - fetchError?: AxiosError; - fetchCount: number; - fetchAllApplicationDependencies: (filters: { - from?: string[]; - to?: string[]; - }) => void; -} - -export const useFetchApplicationDependencies = ( - defaultIsFetching: boolean = false -): IState => { - const [state, dispatch] = useReducer(reducer, defaultIsFetching, initReducer); - - const fetchAllApplicationDependencies = useCallback( - (filters: { from?: string[]; to?: string[] }) => { - dispatch(fetchRequest()); - - getApplicationDependencies(filters, { page: 1, perPage: 1000 }) - .then(({ data }) => { - const list = data._embedded["applications-dependency"]; - const total = data.total_count; - - dispatch( - fetchSuccess({ - data: list, - meta: { - count: total, - }, - }) - ); - }) - .catch((error: AxiosError) => { - dispatch(fetchFailure(error)); - }); - }, - [] - ); - - return { - applicationDependencies: state.applicationDependencies, - isFetching: state.isFetching, - fetchError: state.fetchError, - fetchCount: state.fetchCount, - fetchAllApplicationDependencies, - }; -}; - -export default useFetchApplicationDependencies; diff --git a/src/shared/hooks/useFetchTagTypes/index.ts b/src/shared/hooks/useFetchTagTypes/index.ts deleted file mode 100644 index 489ee3f9..00000000 --- a/src/shared/hooks/useFetchTagTypes/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { useFetchTagTypes } from "./useFetchTagTypes"; diff --git a/src/shared/hooks/useFetchTagTypes/useFetchTagTypes.test.tsx b/src/shared/hooks/useFetchTagTypes/useFetchTagTypes.test.tsx deleted file mode 100644 index 5de10bae..00000000 --- a/src/shared/hooks/useFetchTagTypes/useFetchTagTypes.test.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import axios from "axios"; -import MockAdapter from "axios-mock-adapter"; -import { renderHook, act } from "@testing-library/react-hooks"; -import { useFetchTagTypes } from "./useFetchTagTypes"; -import { TagTypePage } from "api/models"; -import { TAG_TYPES } from "api/rest"; - -describe("useFetchTagTypes", () => { - it("Fetch error due to no REST API found", async () => { - // Mock REST API - new MockAdapter(axios).onGet(TAG_TYPES).networkError(); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => useFetchTagTypes()); - - const { - tagTypes: items, - isFetching, - fetchError, - fetchTagTypes: fetchEntities, - } = result.current; - - expect(isFetching).toBe(false); - expect(items).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => fetchEntities({}, { page: 2, perPage: 50 })); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.tagTypes).toBeUndefined(); - expect(result.current.fetchError).not.toBeUndefined(); - }); - - it("Fetch success", async () => { - // Mock REST API - const data: TagTypePage = { - _embedded: { - "tag-type": [], - }, - total_count: 0, - }; - - new MockAdapter(axios) - .onGet(`${TAG_TYPES}?page=0&size=10`) - .reply(200, data); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => useFetchTagTypes()); - - const { - tagTypes: stakeholders, - isFetching, - fetchError, - fetchTagTypes: fetchStakeholders, - } = result.current; - - expect(isFetching).toBe(false); - expect(stakeholders).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => fetchStakeholders({}, { page: 1, perPage: 10 })); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.tagTypes).toMatchObject({ - data: [], - meta: { count: 0 }, - }); - expect(result.current.fetchError).toBeUndefined(); - }); - - it("Fetch all", async () => { - // Mock REST API - const data: TagTypePage = { - _embedded: { - "tag-type": [], - }, - total_count: 0, - }; - - new MockAdapter(axios) - .onGet(`${TAG_TYPES}?page=0&size=1000&sort=name`) - .reply(200, data); - - // Use hook - const { result, waitForNextUpdate } = renderHook(() => useFetchTagTypes()); - - const { - tagTypes: items, - isFetching, - fetchError, - fetchAllTagTypes: fetchAll, - } = result.current; - - expect(isFetching).toBe(false); - expect(items).toBeUndefined(); - expect(fetchError).toBeUndefined(); - - // Init fetch - act(() => fetchAll()); - expect(result.current.isFetching).toBe(true); - - // Fetch finished - await waitForNextUpdate(); - expect(result.current.isFetching).toBe(false); - expect(result.current.tagTypes).toMatchObject({ - data: [], - meta: { count: 0 }, - }); - expect(result.current.fetchError).toBeUndefined(); - }); -}); diff --git a/src/shared/hooks/useFetchTagTypes/useFetchTagTypes.ts b/src/shared/hooks/useFetchTagTypes/useFetchTagTypes.ts deleted file mode 100644 index 714a4d4e..00000000 --- a/src/shared/hooks/useFetchTagTypes/useFetchTagTypes.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { useCallback, useReducer } from "react"; -import { AxiosError } from "axios"; -import { ActionType, createAsyncAction, getType } from "typesafe-actions"; - -import { getTagTypes, TagTypeSortBy, TagTypeSortByQuery } from "api/rest"; -import { PageRepresentation, PageQuery, TagType } from "api/models"; - -export const { - request: fetchRequest, - success: fetchSuccess, - failure: fetchFailure, -} = createAsyncAction( - "useFetchTagTypes/fetch/request", - "useFetchTagTypes/fetch/success", - "useFetchTagTypes/fetch/failure" -), AxiosError>(); - -type State = Readonly<{ - isFetching: boolean; - tagTypes?: PageRepresentation; - fetchError?: AxiosError; - fetchCount: number; -}>; - -const defaultState: State = { - isFetching: false, - tagTypes: undefined, - fetchError: undefined, - fetchCount: 0, -}; - -type Action = ActionType< - typeof fetchRequest | typeof fetchSuccess | typeof fetchFailure ->; - -const initReducer = (isFetching: boolean): State => { - return { - ...defaultState, - isFetching, - }; -}; - -const reducer = (state: State, action: Action): State => { - switch (action.type) { - case getType(fetchRequest): - return { - ...state, - isFetching: true, - }; - case getType(fetchSuccess): - return { - ...state, - isFetching: false, - fetchError: undefined, - tagTypes: action.payload, - fetchCount: state.fetchCount + 1, - }; - case getType(fetchFailure): - return { - ...state, - isFetching: false, - fetchError: action.payload, - fetchCount: state.fetchCount + 1, - }; - default: - return state; - } -}; - -export interface IState { - tagTypes?: PageRepresentation; - isFetching: boolean; - fetchError?: AxiosError; - fetchCount: number; - fetchTagTypes: ( - filters: { - tagTypes?: string[]; - tags?: string[]; - }, - page: PageQuery, - sortBy?: TagTypeSortByQuery - ) => void; - fetchAllTagTypes: (sortBy?: TagTypeSortByQuery) => void; -} - -export const useFetchTagTypes = ( - defaultIsFetching: boolean = false -): IState => { - const [state, dispatch] = useReducer(reducer, defaultIsFetching, initReducer); - - const fetchTagTypes = useCallback( - ( - filters: { - name?: string[]; - tagTypes?: string[]; - }, - page: PageQuery, - sortBy?: TagTypeSortByQuery - ) => { - dispatch(fetchRequest()); - - getTagTypes(filters, page, sortBy) - .then(({ data }) => { - const list = data._embedded["tag-type"]; - const total = data.total_count; - - dispatch( - fetchSuccess({ - data: list, - meta: { - count: total, - }, - }) - ); - }) - .catch((error: AxiosError) => { - dispatch(fetchFailure(error)); - }); - }, - [] - ); - - const fetchAllTagTypes = useCallback((sortBy?: TagTypeSortByQuery) => { - dispatch(fetchRequest()); - - getTagTypes( - {}, - { page: 1, perPage: 1000 }, - sortBy || { field: TagTypeSortBy.NAME } - ) - .then(({ data }) => { - const list = data._embedded["tag-type"]; - const total = data.total_count; - - dispatch( - fetchSuccess({ - data: list, - meta: { - count: total, - }, - }) - ); - }) - .catch((error: AxiosError) => { - dispatch(fetchFailure(error)); - }); - }, []); - - return { - tagTypes: state.tagTypes, - isFetching: state.isFetching, - fetchError: state.fetchError, - fetchCount: state.fetchCount, - fetchTagTypes, - fetchAllTagTypes, - }; -}; - -export default useFetchTagTypes; From 5f5d09a73b72c9cbec10f5605ac0637fc56a4f23 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Feria Vila <2582866+carlosthe19916@users.noreply.github.com> Date: Mon, 21 Jun 2021 15:48:19 +0200 Subject: [PATCH 10/10] merge main --- .../stakeholders-form/stakeholders-form.tsx | 32 +++++++------ .../application-dependencies-form.tsx | 48 ++++++++++++------- .../application-form/application-form.tsx | 22 +++++---- .../business-services/business-services.tsx | 20 ++++---- .../business-service-form.tsx | 18 +++---- .../controls/job-functions/job-functions.tsx | 20 ++++---- .../stakeholder-group-form.tsx | 18 +++---- .../stakeholder-groups/stakeholder-groups.tsx | 14 ++++-- .../stakeholder-form/stakeholder-form.tsx | 23 ++++++--- .../controls/stakeholders/stakeholders.tsx | 20 ++++---- .../tags/components/tag-form/tag-form.tsx | 11 +++-- src/pages/controls/tags/tags.tsx | 19 ++++---- .../select-business-service-filter.tsx | 19 ++++---- .../select-tag-filter/select-tag-filter.tsx | 13 +++-- src/shared/hooks/index.ts | 7 --- 15 files changed, 169 insertions(+), 135 deletions(-) diff --git a/src/pages/application-inventory/application-assessment/components/stakeholders-form/stakeholders-form.tsx b/src/pages/application-inventory/application-assessment/components/stakeholders-form/stakeholders-form.tsx index afef9cbb..4be75445 100644 --- a/src/pages/application-inventory/application-assessment/components/stakeholders-form/stakeholders-form.tsx +++ b/src/pages/application-inventory/application-assessment/components/stakeholders-form/stakeholders-form.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React, { useEffect, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useFormikContext } from "formik"; @@ -16,13 +16,7 @@ import { useFetch } from "shared/hooks"; import { DEFAULT_SELECT_MAX_HEIGHT } from "Constants"; import { getValidatedFromError } from "utils/utils"; -import { - PageRepresentation, - Stakeholder, - StakeholderGroup, - StakeholderGroupPage, - StakeholderPage, -} from "api/models"; +import { StakeholderGroupPage, StakeholderPage } from "api/models"; import { getAllStakeholderGroups, getAllStakeholders, @@ -44,16 +38,21 @@ export const StakeholdersForm: React.FC = () => { // Fetch stakeholders const { - data: stakeholders, + data: stakeholdersPage, isFetching: isFetchingStakeholders, fetchError: fetchErrorStakeholders, requestFetch: fetchAllStakeholders, - } = useFetch>({ + } = useFetch({ defaultIsFetching: true, onFetch: getAllStakeholders, - mapper: stakeholderPageMapper, }); + const stakeholders = useMemo(() => { + return stakeholdersPage + ? stakeholderPageMapper(stakeholdersPage) + : undefined; + }, [stakeholdersPage]); + useEffect(() => { fetchAllStakeholders(); }, [fetchAllStakeholders]); @@ -61,16 +60,21 @@ export const StakeholdersForm: React.FC = () => { // Fetch stakeholder groups const { - data: stakeholderGroups, + data: stakeholderGroupPage, isFetching: isFetchingStakeholderGroups, fetchError: fetchErrorStakeholderGroups, requestFetch: fetchAllStakeholderGroups, - } = useFetch>({ + } = useFetch({ defaultIsFetching: true, onFetch: getAllStakeholderGroups, - mapper: stakeholderGroupPageMapper, }); + const stakeholderGroups = useMemo(() => { + return stakeholderGroupPage + ? stakeholderGroupPageMapper(stakeholderGroupPage) + : undefined; + }, [stakeholderGroupPage]); + useEffect(() => { fetchAllStakeholderGroups(); }, [fetchAllStakeholderGroups]); diff --git a/src/pages/application-inventory/application-list/components/application-dependencies-form/application-dependencies-form.tsx b/src/pages/application-inventory/application-list/components/application-dependencies-form/application-dependencies-form.tsx index 42f1124d..32cb0ecd 100644 --- a/src/pages/application-inventory/application-list/components/application-dependencies-form/application-dependencies-form.tsx +++ b/src/pages/application-inventory/application-list/components/application-dependencies-form/application-dependencies-form.tsx @@ -1,4 +1,10 @@ -import React, { useCallback, useContext, useEffect, useState } from "react"; +import React, { + useCallback, + useContext, + useEffect, + useMemo, + useState, +} from "react"; import { useTranslation } from "react-i18next"; import { @@ -20,7 +26,6 @@ import { ApplicationDependency, ApplicationDependencyPage, ApplicationPage, - PageRepresentation, } from "api/models"; import { @@ -89,33 +94,37 @@ export const ApplicationDependenciesForm: React.FC - >({ + } = useFetch({ defaultIsFetching: true, onFetch: getAllNorthApplicationDependencies, - mapper: applicationDependencyPageMapper, }); + const northDependencies = useMemo(() => { + return northDependenciesPage + ? applicationDependencyPageMapper(northDependenciesPage) + : undefined; + }, [northDependenciesPage]); + const { - data: southDependencies, + data: southDependenciesPage, isFetching: isFetchingSouthDependencies, fetchError: fetchErrorSouthDependencies, requestFetch: fetchAllSouthDependencies, - } = useFetch< - ApplicationDependencyPage, - PageRepresentation - >({ + } = useFetch({ defaultIsFetching: true, onFetch: getAllSouthApplicationDependencies, - mapper: applicationDependencyPageMapper, }); + const southDependencies = useMemo(() => { + return southDependenciesPage + ? applicationDependencyPageMapper(southDependenciesPage) + : undefined; + }, [southDependenciesPage]); + useEffect(() => { fetchAllNorthDependencies(); }, [fetchAllNorthDependencies]); @@ -127,16 +136,21 @@ export const ApplicationDependenciesForm: React.FC>({ + } = useFetch({ defaultIsFetching: true, onFetch: getAllApplications, - mapper: applicationPageMapper, }); + const applications = useMemo(() => { + return applicationsPage + ? applicationPageMapper(applicationsPage) + : undefined; + }, [applicationsPage]); + useEffect(() => { fetchAllApplications(); }, [fetchAllApplications]); diff --git a/src/pages/application-inventory/application-list/components/application-form/application-form.tsx b/src/pages/application-inventory/application-list/components/application-form/application-form.tsx index d1210cb1..e37d879c 100644 --- a/src/pages/application-inventory/application-list/components/application-form/application-form.tsx +++ b/src/pages/application-inventory/application-list/components/application-form/application-form.tsx @@ -28,9 +28,7 @@ import { Application, BusinessService, BusinessServicePage, - PageRepresentation, Tag, - TagType, TagTypePage, } from "api/models"; import { @@ -90,16 +88,21 @@ export const ApplicationForm: React.FC = ({ // Business services const { - data: businessServices, + data: businessServicesPage, isFetching: isFetchingBusinessServices, fetchError: fetchErrorBusinessServices, requestFetch: fetchAllBusinessServices, - } = useFetch>({ + } = useFetch({ defaultIsFetching: true, onFetch: getAllBusinessServices, - mapper: bussinessServicePageMapper, }); + const businessServices = useMemo(() => { + return businessServicesPage + ? bussinessServicePageMapper(businessServicesPage) + : undefined; + }, [businessServicesPage]); + useEffect(() => { fetchAllBusinessServices(); }, [fetchAllBusinessServices]); @@ -107,16 +110,19 @@ export const ApplicationForm: React.FC = ({ // TagTypes const { - data: tagTypes, + data: tagTypesPage, isFetching: isFetchingTagTypes, fetchError: fetchErrorTagTypes, requestFetch: fetchAllTagTypes, - } = useFetch>({ + } = useFetch({ defaultIsFetching: true, onFetch: getAllTagTypesSortedByRank, - mapper: tagTypePageMapper, }); + const tagTypes = useMemo(() => { + return tagTypesPage ? tagTypePageMapper(tagTypesPage) : undefined; + }, [tagTypesPage]); + useEffect(() => { fetchAllTagTypes(); }, [fetchAllTagTypes]); diff --git a/src/pages/controls/business-services/business-services.tsx b/src/pages/controls/business-services/business-services.tsx index cf6bfb19..c676fca3 100644 --- a/src/pages/controls/business-services/business-services.tsx +++ b/src/pages/controls/business-services/business-services.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import { AxiosResponse } from "axios"; import { useTranslation } from "react-i18next"; @@ -36,12 +36,7 @@ import { useDeleteBusinessService, } from "shared/hooks"; -import { - BusinessService, - BusinessServicePage, - PageRepresentation, - SortByQuery, -} from "api/models"; +import { BusinessService, BusinessServicePage, SortByQuery } from "api/models"; import { BusinessServiceSortBy, BusinessServiceSortByQuery, @@ -139,16 +134,21 @@ export const BusinessServices: React.FC = () => { }, [filtersValue, paginationQuery, sortByQuery]); const { - data: businessServices, + data: businessServicesPage, isFetching, fetchError, requestFetch: refreshTable, - } = useFetch>({ + } = useFetch({ defaultIsFetching: true, onFetch: fetchBusinessServices, - mapper: bussinessServicePageMapper, }); + const businessServices = useMemo(() => { + return businessServicesPage + ? bussinessServicePageMapper(businessServicesPage) + : undefined; + }, [businessServicesPage]); + useEffect(() => { refreshTable(); }, [filtersValue, paginationQuery, sortByQuery, refreshTable]); diff --git a/src/pages/controls/business-services/components/business-service-form/business-service-form.tsx b/src/pages/controls/business-services/components/business-service-form/business-service-form.tsx index 790fffd3..f4cbce41 100644 --- a/src/pages/controls/business-services/components/business-service-form/business-service-form.tsx +++ b/src/pages/controls/business-services/components/business-service-form/business-service-form.tsx @@ -23,12 +23,7 @@ import { useFetch } from "shared/hooks"; import { DEFAULT_SELECT_MAX_HEIGHT } from "Constants"; import { createBusinessService, updateBusinessService } from "api/rest"; -import { - BusinessService, - PageRepresentation, - Stakeholder, - StakeholderPage, -} from "api/models"; +import { BusinessService, Stakeholder, StakeholderPage } from "api/models"; import { getAxiosErrorMessage, getValidatedFromError, @@ -65,16 +60,21 @@ export const BusinessServiceForm: React.FC = ({ const [error, setError] = useState(); const { - data: stakeholders, + data: stakeholdersPage, isFetching: isFetchingStakeholders, fetchError: fetchErrorStakeholders, requestFetch: fetchAllStakeholders, - } = useFetch>({ + } = useFetch({ defaultIsFetching: true, onFetch: getAllStakeholders, - mapper: stakeholderPageMapper, }); + const stakeholders = useMemo(() => { + return stakeholdersPage + ? stakeholderPageMapper(stakeholdersPage) + : undefined; + }, [stakeholdersPage]); + useEffect(() => { fetchAllStakeholders(); }, [fetchAllStakeholders]); diff --git a/src/pages/controls/job-functions/job-functions.tsx b/src/pages/controls/job-functions/job-functions.tsx index 2efe07f4..ee89f79b 100644 --- a/src/pages/controls/job-functions/job-functions.tsx +++ b/src/pages/controls/job-functions/job-functions.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import { AxiosResponse } from "axios"; import { useTranslation } from "react-i18next"; @@ -38,12 +38,7 @@ import { JobFunctionSortBy, JobFunctionSortByQuery, } from "api/rest"; -import { - SortByQuery, - JobFunction, - JobFunctionPage, - PageRepresentation, -} from "api/models"; +import { SortByQuery, JobFunction, JobFunctionPage } from "api/models"; import { jobFunctionPageMapper } from "api/apiUtils"; import { NewJobFunctionModal } from "./components/new-job-function-modal"; @@ -116,16 +111,21 @@ export const JobFunctions: React.FC = () => { }, [filtersValue, paginationQuery, sortByQuery]); const { - data: jobFunctions, + data: jobFunctionsPage, isFetching, fetchError, requestFetch: refreshTable, - } = useFetch>({ + } = useFetch({ defaultIsFetching: true, onFetch: fetchJobFunctions, - mapper: jobFunctionPageMapper, }); + const jobFunctions = useMemo(() => { + return jobFunctionsPage + ? jobFunctionPageMapper(jobFunctionsPage) + : undefined; + }, [jobFunctionsPage]); + useEffect(() => { refreshTable(); }, [filtersValue, paginationQuery, sortByQuery, refreshTable]); diff --git a/src/pages/controls/stakeholder-groups/components/stakeholder-group-form/stakeholder-group-form.tsx b/src/pages/controls/stakeholder-groups/components/stakeholder-group-form/stakeholder-group-form.tsx index f95b1e65..f9ae8c96 100644 --- a/src/pages/controls/stakeholder-groups/components/stakeholder-group-form/stakeholder-group-form.tsx +++ b/src/pages/controls/stakeholder-groups/components/stakeholder-group-form/stakeholder-group-form.tsx @@ -23,12 +23,7 @@ import { useFetch } from "shared/hooks"; import { DEFAULT_SELECT_MAX_HEIGHT } from "Constants"; import { createStakeholderGroup, updateStakeholderGroup } from "api/rest"; -import { - PageRepresentation, - Stakeholder, - StakeholderGroup, - StakeholderPage, -} from "api/models"; +import { Stakeholder, StakeholderGroup, StakeholderPage } from "api/models"; import { getAxiosErrorMessage, getValidatedFromError, @@ -65,16 +60,21 @@ export const StakeholderGroupForm: React.FC = ({ const [error, setError] = useState(); const { - data: stakeholders, + data: stakeholdersPage, isFetching: isFetchingStakeholders, fetchError: fetchErrorStakeholders, requestFetch: fetchAllStakeholders, - } = useFetch>({ + } = useFetch({ defaultIsFetching: true, onFetch: getAllStakeholders, - mapper: stakeholderPageMapper, }); + const stakeholders = useMemo(() => { + return stakeholdersPage + ? stakeholderPageMapper(stakeholdersPage) + : undefined; + }, [stakeholdersPage]); + useEffect(() => { fetchAllStakeholders(); }, [fetchAllStakeholders]); diff --git a/src/pages/controls/stakeholder-groups/stakeholder-groups.tsx b/src/pages/controls/stakeholder-groups/stakeholder-groups.tsx index d259ad5e..de751d22 100644 --- a/src/pages/controls/stakeholder-groups/stakeholder-groups.tsx +++ b/src/pages/controls/stakeholder-groups/stakeholder-groups.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import { AxiosResponse } from "axios"; import { useTranslation } from "react-i18next"; import { useSelectionState } from "@konveyor/lib-ui"; @@ -55,7 +55,6 @@ import { StakeholderGroup, SortByQuery, StakeholderGroupPage, - PageRepresentation, } from "api/models"; import { stakeholderGroupPageMapper } from "api/apiUtils"; @@ -148,16 +147,21 @@ export const StakeholderGroups: React.FC = () => { }, [filtersValue, paginationQuery, sortByQuery]); const { - data: stakeholderGroups, + data: stakeholderGroupsPage, isFetching, fetchError, requestFetch: refreshTable, - } = useFetch>({ + } = useFetch({ defaultIsFetching: true, onFetch: fetchStakeholderGroups, - mapper: stakeholderGroupPageMapper, }); + const stakeholderGroups = useMemo(() => { + return stakeholderGroupsPage + ? stakeholderGroupPageMapper(stakeholderGroupsPage) + : undefined; + }, [stakeholderGroupsPage]); + useEffect(() => { refreshTable(); }, [filtersValue, paginationQuery, sortByQuery, refreshTable]); diff --git a/src/pages/controls/stakeholders/components/stakeholder-form/stakeholder-form.tsx b/src/pages/controls/stakeholders/components/stakeholder-form/stakeholder-form.tsx index d450d5ad..523cac3f 100644 --- a/src/pages/controls/stakeholders/components/stakeholder-form/stakeholder-form.tsx +++ b/src/pages/controls/stakeholders/components/stakeholder-form/stakeholder-form.tsx @@ -26,7 +26,6 @@ import { createStakeholder, updateStakeholder } from "api/rest"; import { JobFunction, JobFunctionPage, - PageRepresentation, Stakeholder, StakeholderGroup, StakeholderGroupPage, @@ -80,31 +79,41 @@ export const StakeholderForm: React.FC = ({ const [error, setError] = useState(); const { - data: jobFunctions, + data: jobFunctionsPage, isFetching: isFetchingJobFunctions, fetchError: fetchErrorJobFunctions, requestFetch: fetchAllJobFunctions, - } = useFetch>({ + } = useFetch({ defaultIsFetching: true, onFetch: getAllJobFunctions, - mapper: jobFunctionPageMapper, }); + const jobFunctions = useMemo(() => { + return jobFunctionsPage + ? jobFunctionPageMapper(jobFunctionsPage) + : undefined; + }, [jobFunctionsPage]); + useEffect(() => { fetchAllJobFunctions(); }, [fetchAllJobFunctions]); const { - data: stakeholderGroups, + data: stakeholderGroupsPage, isFetching: isFetchingGroups, fetchError: fetchErrorGroups, requestFetch: fetchAllStakeholderGroups, - } = useFetch>({ + } = useFetch({ defaultIsFetching: true, onFetch: getAllStakeholderGroups, - mapper: stakeholderGroupPageMapper, }); + const stakeholderGroups = useMemo(() => { + return stakeholderGroupsPage + ? stakeholderGroupPageMapper(stakeholderGroupsPage) + : undefined; + }, [stakeholderGroupsPage]); + useEffect(() => { fetchAllStakeholderGroups(); }, [fetchAllStakeholderGroups]); diff --git a/src/pages/controls/stakeholders/stakeholders.tsx b/src/pages/controls/stakeholders/stakeholders.tsx index e9331dc4..1ea3c5b1 100644 --- a/src/pages/controls/stakeholders/stakeholders.tsx +++ b/src/pages/controls/stakeholders/stakeholders.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import { AxiosResponse } from "axios"; import { useTranslation } from "react-i18next"; import { useSelectionState } from "@konveyor/lib-ui"; @@ -46,12 +46,7 @@ import { StakeholderSortBy, StakeholderSortByQuery, } from "api/rest"; -import { - Stakeholder, - SortByQuery, - StakeholderPage, - PageRepresentation, -} from "api/models"; +import { Stakeholder, SortByQuery, StakeholderPage } from "api/models"; import { stakeholderPageMapper } from "api/apiUtils"; import { NewStakeholderModal } from "./components/new-stakeholder-modal"; @@ -155,16 +150,21 @@ export const Stakeholders: React.FC = () => { }, [filtersValue, paginationQuery, sortByQuery]); const { - data: stakeholders, + data: stakeholdersPage, isFetching, fetchError, requestFetch: refreshTable, - } = useFetch>({ + } = useFetch({ defaultIsFetching: true, onFetch: fetchStakeholders, - mapper: stakeholderPageMapper, }); + const stakeholders = useMemo(() => { + return stakeholdersPage + ? stakeholderPageMapper(stakeholdersPage) + : undefined; + }, [stakeholdersPage]); + useEffect(() => { refreshTable(); }, [filtersValue, paginationQuery, sortByQuery, refreshTable]); diff --git a/src/pages/controls/tags/components/tag-form/tag-form.tsx b/src/pages/controls/tags/components/tag-form/tag-form.tsx index e633afc5..12b52998 100644 --- a/src/pages/controls/tags/components/tag-form/tag-form.tsx +++ b/src/pages/controls/tags/components/tag-form/tag-form.tsx @@ -22,7 +22,7 @@ import { useFetch } from "shared/hooks"; import { DEFAULT_SELECT_MAX_HEIGHT } from "Constants"; import { createTag, updateTag } from "api/rest"; -import { PageRepresentation, Tag, TagType, TagTypePage } from "api/models"; +import { Tag, TagType, TagTypePage } from "api/models"; import { getAxiosErrorMessage, getValidatedFromError, @@ -51,16 +51,19 @@ export const TagForm: React.FC = ({ tag, onSaved, onCancel }) => { const [error, setError] = useState(); const { - data: tagTypes, + data: tagTypesPage, isFetching: isFetchingTagTypes, fetchError: fetchErrorTagTypes, requestFetch: fetchAllTagTypes, - } = useFetch>({ + } = useFetch({ defaultIsFetching: true, onFetch: getAllTagTypes, - mapper: tagTypePageMapper, }); + const tagTypes = useMemo(() => { + return tagTypesPage ? tagTypePageMapper(tagTypesPage) : undefined; + }, [tagTypesPage]); + useEffect(() => { fetchAllTagTypes(); }, [fetchAllTagTypes]); diff --git a/src/pages/controls/tags/tags.tsx b/src/pages/controls/tags/tags.tsx index e65ee0f6..3e2f2d80 100644 --- a/src/pages/controls/tags/tags.tsx +++ b/src/pages/controls/tags/tags.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import { AxiosResponse } from "axios"; import { useTranslation } from "react-i18next"; import { useSelectionState } from "@konveyor/lib-ui"; @@ -42,13 +42,7 @@ import { import { getAxiosErrorMessage } from "utils/utils"; import { getTagTypes, TagTypeSortBy, TagTypeSortByQuery } from "api/rest"; -import { - PageRepresentation, - SortByQuery, - Tag, - TagType, - TagTypePage, -} from "api/models"; +import { SortByQuery, Tag, TagType, TagTypePage } from "api/models"; import { tagTypePageMapper } from "api/apiUtils"; import { NewTagTypeModal } from "./components/new-tag-type-modal"; @@ -147,16 +141,19 @@ export const Tags: React.FC = () => { }, [filtersValue, paginationQuery, sortByQuery]); const { - data: tagTypes, + data: tagTypesPage, isFetching: isFetchingTagTypes, fetchError: fetchErrorTagTypes, requestFetch: refreshTable, - } = useFetch>({ + } = useFetch({ defaultIsFetching: true, onFetch: fetchTagTypes, - mapper: tagTypePageMapper, }); + const tagTypes = useMemo(() => { + return tagTypesPage ? tagTypePageMapper(tagTypesPage) : undefined; + }, [tagTypesPage]); + useEffect(() => { refreshTable(); }, [filtersValue, paginationQuery, sortByQuery, refreshTable]); diff --git a/src/shared/containers/select-business-service-filter/select-business-service-filter.tsx b/src/shared/containers/select-business-service-filter/select-business-service-filter.tsx index 3fb8f7de..949cbf4c 100644 --- a/src/shared/containers/select-business-service-filter/select-business-service-filter.tsx +++ b/src/shared/containers/select-business-service-filter/select-business-service-filter.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React, { useEffect, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { SelectVariant, ToolbarChip } from "@patternfly/react-core"; @@ -6,11 +6,7 @@ import { SelectVariant, ToolbarChip } from "@patternfly/react-core"; import { SimpleSelectFetch, OptionWithValue } from "shared/components"; import { useFetch } from "shared/hooks"; -import { - BusinessService, - BusinessServicePage, - PageRepresentation, -} from "api/models"; +import { BusinessService, BusinessServicePage } from "api/models"; import { bussinessServicePageMapper, getAllBusinessServices, @@ -54,16 +50,21 @@ export const SelectBusinessServiceFilter: React.FC = const { t } = useTranslation(); const { - data: businessServices, + data: businessServicesPage, isFetching: isFetchingBusinessServices, fetchError: fetchErrorBusinessServices, requestFetch: fetchAllBusinessServices, - } = useFetch>({ + } = useFetch({ defaultIsFetching: true, onFetch: getAllBusinessServices, - mapper: bussinessServicePageMapper, }); + const businessServices = useMemo(() => { + return businessServicesPage + ? bussinessServicePageMapper(businessServicesPage) + : undefined; + }, [businessServicesPage]); + useEffect(() => { fetchAllBusinessServices(); }, [fetchAllBusinessServices]); diff --git a/src/shared/containers/select-tag-filter/select-tag-filter.tsx b/src/shared/containers/select-tag-filter/select-tag-filter.tsx index 55abc818..fa129bb4 100644 --- a/src/shared/containers/select-tag-filter/select-tag-filter.tsx +++ b/src/shared/containers/select-tag-filter/select-tag-filter.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { SelectVariant, ToolbarChip } from "@patternfly/react-core"; @@ -7,7 +7,7 @@ import { SimpleSelectFetch, OptionWithValue } from "shared/components"; import { useFetch } from "shared/hooks"; import { DEFAULT_SELECT_MAX_HEIGHT } from "Constants"; -import { PageRepresentation, Tag, TagType, TagTypePage } from "api/models"; +import { Tag, TagTypePage } from "api/models"; import { getAllTagTypes, tagTypePageMapper } from "api/apiUtils"; import { TagTypeSortBy } from "api/rest"; @@ -55,16 +55,19 @@ export const SelectTagFilter: React.FC = ({ // Tag types const { - data: tagTypes, + data: tagTypesPage, isFetching: isFetchingTagTypes, fetchError: fetchErrorTagTypes, requestFetch: fetchAllTagTypes, - } = useFetch>({ + } = useFetch({ defaultIsFetching: true, onFetch: getAllTagTypesSortedByRank, - mapper: tagTypePageMapper, }); + const tagTypes = useMemo(() => { + return tagTypesPage ? tagTypePageMapper(tagTypesPage) : undefined; + }, [tagTypesPage]); + useEffect(() => { fetchAllTagTypes(); }, [fetchAllTagTypes]); diff --git a/src/shared/hooks/index.ts b/src/shared/hooks/index.ts index 0b8f16ab..00aea39c 100644 --- a/src/shared/hooks/index.ts +++ b/src/shared/hooks/index.ts @@ -6,17 +6,10 @@ export { useDeleteBusinessService } from "./useDeleteBusinessService"; export { useDeleteJobFunction } from "./useDeleteJobFunction"; export { useDeleteStakeholder } from "./useDeleteStakeholder"; export { useDeleteStakeholderGroup } from "./useDeleteStakeholderGroup"; -export { useFetchApplicationDependencies } from "./useFetchApplicationDependencies"; export { useDeleteTag } from "./useDeleteTag"; export { useDeleteTagType } from "./useDeleteTagType"; export { useEntityModal } from "./useEntityModal"; export { useFetch } from "./useFetch"; -export { useFetchApplications } from "./useFetchApplications"; -export { useFetchBusinessServices } from "./useFetchBusinessServices"; -export { useFetchJobFunctions } from "./useFetchJobFunctions"; -export { useFetchStakeholderGroups } from "./useFetchStakeholderGroups"; -export { useFetchStakeholders } from "./useFetchStakeholders"; -export { useFetchTagTypes } from "./useFetchTagTypes"; export { useMultipleFetch } from "./useMultipleFetch"; export { useQueryString } from "./useRouteAsState"; export { useToolbarFilter } from "./useToolbarFilter";