From ad617c898f3311a46866deac82b1f5c147bc521b Mon Sep 17 00:00:00 2001 From: Kasper Birch Date: Fri, 13 Dec 2024 16:00:06 +0100 Subject: [PATCH] Ensure `v1/products/` is never called without an identifier We have identified a recurring issue in the code where attempts are made to access the first element of an empty array (`array[0]`), which results in `undefined`. This issue occurs across all articles where the identifier is an empty array. Additionally, our TypeScript implementation requires the `useGetV1ProductsIdentifier` hook to receive a string. To satisfy TypeScript, we currently use `|| ""`, even though the hook is only activated when a value is present. However, this approach has inadvertently allowed errors to occur, and we have been uncertain about how to address this properly. This change ensures that the `|| ""` fallback in `useGetV1ProductsIdentifier` is never activated, effectively preventing the error from occurring. --- .../materials/utils/digital-material-fetch-hoc.tsx | 8 +++++++- .../availability-label/availability-label.tsx | 3 ++- .../availability-label/useOnlineAvailabilityData.ts | 4 +++- .../online/MaterialAvailabilityTextOnline.tsx | 12 +++++++++++- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/apps/loan-list/materials/utils/digital-material-fetch-hoc.tsx b/src/apps/loan-list/materials/utils/digital-material-fetch-hoc.tsx index 0dd13b41f5..0d854c404a 100644 --- a/src/apps/loan-list/materials/utils/digital-material-fetch-hoc.tsx +++ b/src/apps/loan-list/materials/utils/digital-material-fetch-hoc.tsx @@ -35,7 +35,13 @@ const fetchDigitalMaterial = data: productsData, isSuccess: isSuccessDigital, isLoading - } = useGetV1ProductsIdentifier(item.identifier); + } = useGetV1ProductsIdentifier(item.identifier, { + query: { + // We never want to pass an empty string to the API + // So we only enable the query if we have an isbn + enabled: !!item.identifier + } + }); useEffect(() => { if (productsData && isSuccessDigital && productsData.product) { diff --git a/src/components/availability-label/availability-label.tsx b/src/components/availability-label/availability-label.tsx index e915c8ccb6..4ef1631a02 100644 --- a/src/components/availability-label/availability-label.tsx +++ b/src/components/availability-label/availability-label.tsx @@ -1,4 +1,5 @@ import React, { memo } from "react"; +import { first } from "lodash"; import { useDeepCompareEffect } from "react-use"; import { useText } from "../../core/utils/text"; import LinkNoStyle from "../atoms/links/LinkNoStyle"; @@ -45,7 +46,7 @@ export const AvailabilityLabel: React.FC = ({ accessTypes, access, faustIds, - isbn: isbns ? isbns[0] : null, + isbn: first(isbns) ?? null, manifestText }); diff --git a/src/components/availability-label/useOnlineAvailabilityData.ts b/src/components/availability-label/useOnlineAvailabilityData.ts index 855cc44a33..de5d4decce 100644 --- a/src/components/availability-label/useOnlineAvailabilityData.ts +++ b/src/components/availability-label/useOnlineAvailabilityData.ts @@ -22,11 +22,13 @@ const useOnlineAvailabilityData = ({ // Find out if the material is cost free. const { isLoading: isLoadingIdentifier, data: dataIdentifier } = + // We never want to pass an empty string to the API + // So we only enable the query if we have an isbn useGetV1ProductsIdentifier(isbn ?? "", { query: { // Publizon / useGetV1ProductsIdentifier is responsible for online // materials. It requires an ISBN to do lookups. - enabled: enabled && isAvailable === null && isbn !== null + enabled: enabled && isAvailable === null && !!isbn } }); diff --git a/src/components/material/MaterialAvailabilityText/online/MaterialAvailabilityTextOnline.tsx b/src/components/material/MaterialAvailabilityText/online/MaterialAvailabilityTextOnline.tsx index 4626d0d30c..356e01772c 100644 --- a/src/components/material/MaterialAvailabilityText/online/MaterialAvailabilityTextOnline.tsx +++ b/src/components/material/MaterialAvailabilityText/online/MaterialAvailabilityTextOnline.tsx @@ -1,4 +1,5 @@ import * as React from "react"; +import { first } from "lodash"; import { useGetV1LibraryProfile, useGetV1ProductsIdentifier, @@ -18,7 +19,16 @@ const MaterialAvailabilityTextOnline: React.FC< MaterialAvailabilityTextOnlineProps > = ({ isbns, materialType }) => { const t = useText(); - const { data: productsData } = useGetV1ProductsIdentifier(isbns[0]); + const { data: productsData } = useGetV1ProductsIdentifier( + first(isbns) || "", + { + query: { + // We never want to pass an empty string to the API + // So we only enable the query if we have an isbn + enabled: !!first(isbns) + } + } + ); const { data: libraryProfileData } = useGetV1LibraryProfile(); const { data: loansData } = useGetV1UserLoans();