From 80bcf37f0da329792a5f89b4f046c3d8b3144179 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Mon, 3 Feb 2025 16:07:01 +0100 Subject: [PATCH] Migrate last vessels apis to rtk --- .../api/bff/VesselController.kt | 2 +- frontend/src/api/BackendApi.types.ts | 1 + frontend/src/api/vessel.ts | 85 ------------------ .../domain/use_cases/vessel/searchVessels.ts | 24 ++++-- .../src/domain/use_cases/vessel/showVessel.ts | 11 ++- .../use_cases/vessel/showVesselTrack.ts | 10 ++- .../updateSelectedVesselTrackRequest.ts | 10 ++- frontend/src/features/Vessel/vesselApi.ts | 86 ++++++++++++++++++- 8 files changed, 130 insertions(+), 99 deletions(-) delete mode 100644 frontend/src/api/vessel.ts diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/VesselController.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/VesselController.kt index 3f4a7a62d0..445f44c605 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/VesselController.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/VesselController.kt @@ -54,7 +54,7 @@ class VesselController( @GetMapping("/find") @Operation(summary = "Get vessel information and positions") - fun getVessel( + fun getVesselAndPositions( @Parameter(description = "Vessel internal id") @RequestParam(name = "vesselId") vesselId: Int?, diff --git a/frontend/src/api/BackendApi.types.ts b/frontend/src/api/BackendApi.types.ts index e3110c139e..1ed8853eb3 100644 --- a/frontend/src/api/BackendApi.types.ts +++ b/frontend/src/api/BackendApi.types.ts @@ -77,5 +77,6 @@ export namespace BackendApi { export interface Meta { response?: { headers: Headers + status: number } } diff --git a/frontend/src/api/vessel.ts b/frontend/src/api/vessel.ts deleted file mode 100644 index 73ece38c06..0000000000 --- a/frontend/src/api/vessel.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { - getVesselIdentityFromLegacyVesselIdentity, - getVesselIdentityPropsAsEmptyStringsWhenUndefined -} from '@features/Vessel/utils' -import { Vessel } from '@features/Vessel/Vessel.types' -import { FrontendApiError } from '@libs/FrontendApiError' - -import { monitorfishApiKy } from './api' -import { HttpStatusCode } from './constants' - -import type { TrackRequest } from '../domain/entities/vessel/types' - -const VESSEL_POSITIONS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les informations du navire" -const VESSEL_SEARCH_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les navires dans notre base" - -/** - * Get vessel information and positions - * - * @throws {@link FrontendApiError} - */ -async function getVesselFromAPI(vesselIdentity: Vessel.VesselIdentity, trackRequest: TrackRequest) { - const { externalReferenceNumber, internalReferenceNumber, ircs, vesselId, vesselIdentifier } = - getVesselIdentityPropsAsEmptyStringsWhenUndefined(getVesselIdentityFromLegacyVesselIdentity(vesselIdentity)) - const trackDepth = trackRequest.trackDepth ?? '' - const afterDateTime = trackRequest.afterDateTime?.toISOString() ?? '' - const beforeDateTime = trackRequest.beforeDateTime?.toISOString() ?? '' - - try { - return await monitorfishApiKy - .get( - `/bff/v1/vessels/find?vesselId=${vesselId}&internalReferenceNumber=${internalReferenceNumber}&externalReferenceNumber=${externalReferenceNumber}&IRCS=${ircs}&vesselIdentifier=${vesselIdentifier}&trackDepth=${trackDepth}&afterDateTime=${afterDateTime}&beforeDateTime=${beforeDateTime}` - ) - .then(response => - response.json().then(vesselAndPositions => ({ - isTrackDepthModified: response.status === HttpStatusCode.ACCEPTED, - vesselAndPositions - })) - ) - } catch (err) { - throw new FrontendApiError(VESSEL_POSITIONS_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -/** - * Get vessel positions - * - * @throws {@link FrontendApiError} - */ -async function getVesselPositionsFromAPI(identity: Vessel.VesselIdentity, trackRequest: TrackRequest) { - const { externalReferenceNumber, internalReferenceNumber, ircs, vesselIdentifier } = - getVesselIdentityPropsAsEmptyStringsWhenUndefined(getVesselIdentityFromLegacyVesselIdentity(identity)) - const trackDepth = trackRequest.trackDepth ?? '' - const afterDateTime = trackRequest.afterDateTime?.toISOString() ?? '' - const beforeDateTime = trackRequest.beforeDateTime?.toISOString() ?? '' - - try { - return await monitorfishApiKy - .get( - `/bff/v1/vessels/positions?internalReferenceNumber=${internalReferenceNumber}&externalReferenceNumber=${externalReferenceNumber}&IRCS=${ircs}&vesselIdentifier=${vesselIdentifier}&trackDepth=${trackDepth}&afterDateTime=${afterDateTime}&beforeDateTime=${beforeDateTime}` - ) - .then(response => - response.json().then(positions => ({ - isTrackDepthModified: response.status === HttpStatusCode.ACCEPTED, - positions - })) - ) - } catch (err) { - throw new FrontendApiError(VESSEL_POSITIONS_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -/** @deprecated Use Redux RTK `searchVessels()` query. */ -async function searchVesselsFromAPI(searched: string) { - const encodedSearched = encodeURI(searched) || '' - - try { - return await monitorfishApiKy - .get(`/bff/v1/vessels/search?searched=${encodedSearched}`) - .json() - } catch (err) { - throw new FrontendApiError(VESSEL_SEARCH_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -export { searchVesselsFromAPI, getVesselPositionsFromAPI, getVesselFromAPI } diff --git a/frontend/src/domain/use_cases/vessel/searchVessels.ts b/frontend/src/domain/use_cases/vessel/searchVessels.ts index ddf433b2e7..de8a1f5c13 100644 --- a/frontend/src/domain/use_cases/vessel/searchVessels.ts +++ b/frontend/src/domain/use_cases/vessel/searchVessels.ts @@ -1,8 +1,22 @@ -import { searchVesselsFromAPI } from '../../../api/vessel' +import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' +import { Vessel } from '@features/Vessel/Vessel.types' +import { vesselApi } from '@features/Vessel/vesselApi' + import { setError } from '../../shared_slices/Global' +import type { MainAppThunk } from '@store' + /** @deprecated Use Redux RTK `searchVessels()` query. */ -export const searchVessels = searched => dispatch => - searchVesselsFromAPI(searched).catch(error => { - dispatch(setError(error)) - }) +export const searchVessels = + (searched: string): MainAppThunk> => + async dispatch => { + try { + return await dispatch( + vesselApi.endpoints.searchVessels.initiate({ searched }, RTK_FORCE_REFETCH_QUERY_OPTIONS) + ).unwrap() + } catch (error) { + dispatch(setError(error)) + + return undefined + } + } diff --git a/frontend/src/domain/use_cases/vessel/showVessel.ts b/frontend/src/domain/use_cases/vessel/showVessel.ts index 15d4afe6c6..d9f08f903f 100644 --- a/frontend/src/domain/use_cases/vessel/showVessel.ts +++ b/frontend/src/domain/use_cases/vessel/showVessel.ts @@ -1,7 +1,8 @@ -import { getVesselFromAPI } from '@api/vessel' +import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' import { logbookActions } from '@features/Logbook/slice' import { doNotAnimate } from '@features/Map/slice' import { loadingVessel, resetLoadingVessel, setSelectedVessel, vesselSelectors } from '@features/Vessel/slice' +import { vesselApi } from '@features/Vessel/vesselApi' import { DisplayedErrorKey } from '@libs/DisplayedError/constants' import { captureMessage } from '@sentry/react' import { omit } from 'lodash' @@ -59,7 +60,13 @@ export const showVessel = dispatch(addSearchedVessel(vesselIdentity)) } - const { isTrackDepthModified, vesselAndPositions } = await getVesselFromAPI(vesselIdentity, nextTrackRequest) + const { isTrackDepthModified, vesselAndPositions } = await dispatch( + vesselApi.endpoints.getVesselAndPositions.initiate( + { trackRequest: nextTrackRequest, vesselIdentity }, + RTK_FORCE_REFETCH_QUERY_OPTIONS + ) + ).unwrap() + try { throwCustomErrorFromAPIFeedback(vesselAndPositions.positions, isTrackDepthModified, isFromUserAction) } catch (error) { diff --git a/frontend/src/domain/use_cases/vessel/showVesselTrack.ts b/frontend/src/domain/use_cases/vessel/showVesselTrack.ts index d420eb394a..387902601b 100644 --- a/frontend/src/domain/use_cases/vessel/showVesselTrack.ts +++ b/frontend/src/domain/use_cases/vessel/showVesselTrack.ts @@ -1,10 +1,11 @@ +import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '@features/Map/constants' import { doNotAnimate } from '@features/Map/slice' import { addVesselTrackShowed, resetLoadingVessel } from '@features/Vessel/slice' import { getVesselCompositeIdentifier } from '@features/Vessel/utils' +import { vesselApi } from '@features/Vessel/vesselApi' import { transform } from 'ol/proj' -import { getVesselPositionsFromAPI } from '../../../api/vessel' import { getCustomOrDefaultTrackRequest, throwCustomErrorFromAPIFeedback } from '../../entities/vesselTrackDepth' import { removeError, setError } from '../../shared_slices/Global' @@ -30,7 +31,12 @@ export const showVesselTrack = dispatch(doNotAnimate(!isFromUserAction)) dispatch(removeError()) - const { isTrackDepthModified, positions } = await getVesselPositionsFromAPI(vesselIdentity, nextTrackRequest) + const { isTrackDepthModified, positions } = await dispatch( + vesselApi.endpoints.getVesselPositions.initiate( + { trackRequest: nextTrackRequest, vesselIdentity }, + RTK_FORCE_REFETCH_QUERY_OPTIONS + ) + ).unwrap() try { throwCustomErrorFromAPIFeedback(positions, isTrackDepthModified, isFromUserAction) dispatch(removeError()) diff --git a/frontend/src/domain/use_cases/vessel/updateSelectedVesselTrackRequest.ts b/frontend/src/domain/use_cases/vessel/updateSelectedVesselTrackRequest.ts index 1de1e43d44..a31d687277 100644 --- a/frontend/src/domain/use_cases/vessel/updateSelectedVesselTrackRequest.ts +++ b/frontend/src/domain/use_cases/vessel/updateSelectedVesselTrackRequest.ts @@ -1,3 +1,4 @@ +import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' import { animateToExtent, doNotAnimate } from '@features/Map/slice' import { resetLoadingVessel, @@ -5,8 +6,8 @@ import { updateSelectedVesselPositions, updatingVesselTrackDepth } from '@features/Vessel/slice' +import { vesselApi } from '@features/Vessel/vesselApi' -import { getVesselPositionsFromAPI } from '../../../api/vessel' import { logbookActions } from '../../../features/Logbook/slice' import { throwCustomErrorFromAPIFeedback } from '../../entities/vesselTrackDepth' import { removeError, setError } from '../../shared_slices/Global' @@ -30,7 +31,12 @@ export const updateSelectedVesselTrackRequest = dispatchUpdatingVessel(dispatch, true) - const { isTrackDepthModified, positions } = await getVesselPositionsFromAPI(vesselIdentity, trackRequest) + const { isTrackDepthModified, positions } = await dispatch( + vesselApi.endpoints.getVesselPositions.initiate( + { trackRequest, vesselIdentity }, + RTK_FORCE_REFETCH_QUERY_OPTIONS + ) + ).unwrap() throwCustomErrorFromAPIFeedback(positions, isTrackDepthModified, false) dispatch(removeError()) diff --git a/frontend/src/features/Vessel/vesselApi.ts b/frontend/src/features/Vessel/vesselApi.ts index 7daac6c1fc..b87c8d6773 100644 --- a/frontend/src/features/Vessel/vesselApi.ts +++ b/frontend/src/features/Vessel/vesselApi.ts @@ -1,19 +1,22 @@ import { monitorfishApi } from '@api/api' -import { RtkCacheTagType } from '@api/constants' +import { HttpStatusCode, RtkCacheTagType } from '@api/constants' import { DisplayedErrorKey } from '@libs/DisplayedError/constants' import { FrontendApiError } from '@libs/FrontendApiError' import { getUrlOrPathWithQueryParams } from '@utils/getUrlOrPathWithQueryParams' import { displayedErrorActions } from 'domain/shared_slices/DisplayedError' import { displayOrLogError } from 'domain/use_cases/error/displayOrLogError' -import { getVesselIdentityPropsAsEmptyStringsWhenUndefined } from './utils' +import { getVesselIdentityFromLegacyVesselIdentity, getVesselIdentityPropsAsEmptyStringsWhenUndefined } from './utils' import { Vessel } from './Vessel.types' +import type { TrackRequest } from '../../domain/entities/vessel/types' +import type { Meta } from '@api/BackendApi.types' import type { VesselReportings } from '@features/Reporting/types' const GET_VESSEL_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les informations de ce navire." const GET_VESSEL_REPORTINGS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les signalements de ce navire." const SEARCH_VESSELS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les navires correspondants à cette recherche." +const VESSEL_POSITIONS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les informations du navire" export const vesselApi = monitorfishApi.injectEndpoints({ endpoints: builder => ({ @@ -23,6 +26,85 @@ export const vesselApi = monitorfishApi.injectEndpoints({ transformErrorResponse: response => new FrontendApiError(GET_VESSEL_ERROR_MESSAGE, response) }), + /** + * Get vessel information and positions. + * + * Transforms the response by reading the JSON body and setting + * `isTrackDepthModified` based on the response status. + */ + getVesselAndPositions: builder.query< + { isTrackDepthModified: boolean; vesselAndPositions: Vessel.VesselAndPositions }, + { trackRequest: TrackRequest; vesselIdentity: Vessel.VesselIdentity } + >({ + query: ({ trackRequest, vesselIdentity }) => { + const { externalReferenceNumber, internalReferenceNumber, ircs, vesselId, vesselIdentifier } = + getVesselIdentityPropsAsEmptyStringsWhenUndefined(getVesselIdentityFromLegacyVesselIdentity(vesselIdentity)) + const trackDepth = trackRequest.trackDepth ?? '' + const afterDateTime = trackRequest.afterDateTime?.toISOString() ?? '' + const beforeDateTime = trackRequest.beforeDateTime?.toISOString() ?? '' + + return { + method: 'GET', + params: { + afterDateTime, + beforeDateTime, + externalReferenceNumber, + internalReferenceNumber, + IRCS: ircs, + trackDepth, + vesselId, + vesselIdentifier + }, + url: `/bff/v1/vessels/find` + } + }, + transformErrorResponse: response => new FrontendApiError(VESSEL_POSITIONS_ERROR_MESSAGE, response), + transformResponse: async (baseQueryReturnValue: Vessel.VesselAndPositions, meta: Meta) => ({ + isTrackDepthModified: meta?.response?.status === HttpStatusCode.ACCEPTED, + vesselAndPositions: baseQueryReturnValue + }) + }), + + /** + * Get vessel positions. + * + * Transforms the response by reading the JSON body and setting + * `isTrackDepthModified` based on the response status. + */ + getVesselPositions: builder.query< + { isTrackDepthModified: boolean; positions: Vessel.VesselPosition[] }, + { trackRequest: TrackRequest; vesselIdentity: Vessel.VesselIdentity } + >({ + query: ({ trackRequest, vesselIdentity }) => { + const { externalReferenceNumber, internalReferenceNumber, ircs, vesselIdentifier } = + getVesselIdentityPropsAsEmptyStringsWhenUndefined(getVesselIdentityFromLegacyVesselIdentity(vesselIdentity)) + const trackDepth = trackRequest.trackDepth ?? '' + const afterDateTime = trackRequest.afterDateTime?.toISOString() ?? '' + const beforeDateTime = trackRequest.beforeDateTime?.toISOString() ?? '' + + return { + method: 'GET', + // Pass query parameters + params: { + afterDateTime, + beforeDateTime, + externalReferenceNumber, + internalReferenceNumber, + IRCS: ircs, + trackDepth, + vesselIdentifier + }, + + url: `/bff/v1/vessels/positions` + } + }, + transformErrorResponse: response => new FrontendApiError(VESSEL_POSITIONS_ERROR_MESSAGE, response), + transformResponse: async (baseQueryReturnValue: Vessel.VesselPosition[], meta: Meta) => ({ + isTrackDepthModified: meta?.response?.status === HttpStatusCode.ACCEPTED, + positions: baseQueryReturnValue + }) + }), + getVesselReportingsByVesselIdentity: builder.query< VesselReportings, { fromDate: string; vesselIdentity: Vessel.VesselIdentity }