From 23f7f420544fcceedcd563fe8cf037857d7914bf Mon Sep 17 00:00:00 2001 From: Diego Cohen Date: Fri, 22 Nov 2024 14:22:13 -0500 Subject: [PATCH 1/6] Add eligibility status type --- pages/hold/confirmation/[id].tsx | 12 ++++++ pages/hold/request/[id]/index.tsx | 8 +++- src/server/api/hold.ts | 66 ++++++++++++++++++++++++++++--- src/types/holdPageTypes.ts | 16 ++++++++ 4 files changed, 95 insertions(+), 7 deletions(-) diff --git a/pages/hold/confirmation/[id].tsx b/pages/hold/confirmation/[id].tsx index 92c5df89f..443303ead 100644 --- a/pages/hold/confirmation/[id].tsx +++ b/pages/hold/confirmation/[id].tsx @@ -11,6 +11,8 @@ import { import HoldConfirmationFAQ from "../../../src/components/HoldPages/HoldConfirmationFAQ" // import HoldItemDetails from "../../../src/components/HoldPages/HoldItemDetails" +import { fetchHoldRequestDetails } from "../../../src/server/api/hold" + interface HoldConfirmationPageProps { isEDD?: boolean } @@ -56,3 +58,13 @@ export default function HoldConfirmationPage({ ) } + +export async function getServerSideProps({ query }) { + const { pickupLocation, requestId } = query + fetchHoldRequestDetails({ requestId, patronId: "" }) + return { + props: { + isEDD: pickupLocation === "edd", + }, + } +} diff --git a/pages/hold/request/[id]/index.tsx b/pages/hold/request/[id]/index.tsx index 315889447..f2fb573db 100644 --- a/pages/hold/request/[id]/index.tsx +++ b/pages/hold/request/[id]/index.tsx @@ -17,7 +17,10 @@ import { SITE_NAME, BASE_URL, PATHS } from "../../../../src/config/constants" import useLoading from "../../../../src/hooks/useLoading" import { fetchBib } from "../../../../src/server/api/bib" -import { fetchDeliveryLocations } from "../../../../src/server/api/hold" +import { + fetchDeliveryLocations, + fetchHoldRequestEligibility, +} from "../../../../src/server/api/hold" import initializePatronTokenAuth, { doRedirectBasedOnNyplAccountRedirects, @@ -210,6 +213,9 @@ export async function getServerSideProps({ params, req, res }) { try { const patronId = patronTokenResponse?.decodedPatron?.sub + const holdRequestElligibility = await fetchHoldRequestEligibility(patronId) + console.log("holdRequestElligibility", holdRequestElligibility) + // fetch bib and item const [bibId, itemId] = id.split("-") diff --git a/src/server/api/hold.ts b/src/server/api/hold.ts index ace140715..6e110eac2 100644 --- a/src/server/api/hold.ts +++ b/src/server/api/hold.ts @@ -1,6 +1,9 @@ import nyplApiClient from "../nyplApiClient" -import type { HoldPostResult } from "../../types/holdPageTypes" +import type { + HoldPostResult, + HoldRequestDetailsParams, +} from "../../types/holdPageTypes" import type { DeliveryLocation, DeliveryLocationsResult, @@ -9,6 +12,7 @@ import type { import type { DiscoveryHoldPostParams, HoldRequestParams, + PatronEligibilityStatus, } from "../../types/holdPageTypes" import { @@ -92,15 +96,12 @@ export async function postHoldRequest( try { const client = await nyplApiClient() const holdPostResult = await client.post("/hold-requests", holdPostParams) - const { id: requestId } = holdPostResult.data + const requestId = holdPostResult?.data?.id if (!requestId) { - console.error( - "postHoldRequest failed, no id returned from Discovery API", - holdPostResult - ) return { status: 400, + errorMessage: holdPostResult?.data?.message, } } @@ -110,6 +111,7 @@ export async function postHoldRequest( requestId, } } catch (error) { + console.log("error", error) console.error( `Error posting hold request in postHoldRequest server function, itemId: ${itemId}`, error.message @@ -120,3 +122,55 @@ export async function postHoldRequest( } } } + +/** + * Getter function for hold request details. + */ +export async function fetchHoldRequestDetails( + holdRequestDetailsParams: HoldRequestDetailsParams +): Promise { + const { requestId, patronId } = holdRequestDetailsParams + + try { + const client = await nyplApiClient() + const holdDetailsResult = await client.get(`/hold-requests/${requestId}`) + if (patronId !== holdDetailsResult.data.patron) { + console.log("TODO: Redirect to 404 page") + } + + console.log("holdDetailsResult", holdDetailsResult) + return + } catch (error) { + console.error( + `Error fetching hold request details in fetchHoldRequestDetails server function, requestId: ${requestId}`, + error.message + ) + + return + } +} + +/** + * Getter function for hold request eligibility for patrons. + */ +export async function fetchHoldRequestEligibility( + patronId: string +): Promise { + const eligibilityEndpoint = `/patrons/${patronId}/hold-request-eligibility` + + try { + const client = await nyplApiClient() + const eligibilityResult = await client.get(eligibilityEndpoint, { + cache: false, + }) + + return eligibilityResult as PatronEligibilityStatus + } catch (error) { + console.error( + `Error fetching hold request elligibility in fetchHoldRequestEligibility server function, patronId: ${patronId}`, + error.message + ) + + return { eligibility: false } + } +} diff --git a/src/types/holdPageTypes.ts b/src/types/holdPageTypes.ts index 458952ecb..ede826764 100644 --- a/src/types/holdPageTypes.ts +++ b/src/types/holdPageTypes.ts @@ -21,10 +21,26 @@ export interface EDDRequestParams extends HoldRequestParams { requestNotes?: string } +export interface HoldRequestDetailsParams { + requestId: string + patronId: string +} + +export interface PatronEligibilityStatus { + eligibility: boolean + expired?: boolean + blocked?: boolean + moneyOwed?: boolean + ptypeDisallowsHolds?: boolean + reachedHoldLimit?: boolean + hasIssues?: boolean +} + export interface HoldPostResult { status: HTTPStatusCode pickupLocation?: NYPLocationKey | "edd" requestId?: string + errorMessage?: string } export interface DiscoveryHoldPostParams { From 5d2a0e85fdf342adfb0fb1011a8b5a3a2ee3d303 Mon Sep 17 00:00:00 2001 From: Diego Cohen Date: Fri, 22 Nov 2024 17:12:31 -0500 Subject: [PATCH 2/6] Add bad patron redirect --- pages/hold/confirmation/[id].tsx | 76 +++++++++++++++++++++++++++++--- src/server/api/hold.ts | 25 ++++++----- src/types/holdPageTypes.ts | 25 ++++++----- 3 files changed, 96 insertions(+), 30 deletions(-) diff --git a/pages/hold/confirmation/[id].tsx b/pages/hold/confirmation/[id].tsx index 443303ead..334e7dbea 100644 --- a/pages/hold/confirmation/[id].tsx +++ b/pages/hold/confirmation/[id].tsx @@ -6,12 +6,28 @@ import { SITE_NAME, HOLD_PAGE_HEADING, EDD_PAGE_HEADING, + BASE_URL, + PATHS, } from "../../../src/config/constants" import HoldConfirmationFAQ from "../../../src/components/HoldPages/HoldConfirmationFAQ" // import HoldItemDetails from "../../../src/components/HoldPages/HoldItemDetails" -import { fetchHoldRequestDetails } from "../../../src/server/api/hold" +import { fetchHoldDetails } from "../../../src/server/api/hold" + +import initializePatronTokenAuth, { + doRedirectBasedOnNyplAccountRedirects, + getLoginRedirect, +} from "../../../src/server/auth" + +import Bib from "../../../src/models/Bib" +import Item from "../../../src/models/Item" + +import type { DiscoveryBibResult } from "../../../src/types/bibTypes" +import type { DiscoveryItemResult } from "../../../src/types/itemTypes" + +import { fetchBib } from "../../../src/server/api/bib" +import { fetchDeliveryLocations } from "../../../src/server/api/hold" interface HoldConfirmationPageProps { isEDD?: boolean @@ -59,12 +75,58 @@ export default function HoldConfirmationPage({ ) } -export async function getServerSideProps({ query }) { +export async function getServerSideProps({ req, res, query }) { + // authentication redirect + const patronTokenResponse = await initializePatronTokenAuth(req.cookies) + const isAuthenticated = patronTokenResponse.isTokenValid + const redirectTrackerCookie = req.cookies["nyplAccountRedirects"] + const redirectCount = parseInt(redirectTrackerCookie, 10) || 0 + const redirectBasedOnNyplAccountRedirects = + doRedirectBasedOnNyplAccountRedirects(redirectCount) + + // If we end up not authenticated 3 times after redirecting to the login url, don't redirect. + if (redirectBasedOnNyplAccountRedirects && !isAuthenticated) { + res.setHeader( + "Set-Cookie", + `nyplAccountRedirects=${ + redirectCount + 1 + }; Max-Age=10; path=/; domain=.nypl.org;` + ) + const redirect = getLoginRedirect(req, `${PATHS.HOLD_REQUEST}/${id}/edd`) + + return { + redirect: { + destination: redirect, + permanent: false, + }, + } + } + + const patronId = patronTokenResponse?.decodedPatron?.sub const { pickupLocation, requestId } = query - fetchHoldRequestDetails({ requestId, patronId: "" }) - return { - props: { - isEDD: pickupLocation === "edd", - }, + + try { + const { patronId: patronIdFromResponse } = await fetchHoldDetails(requestId) + + if (patronId !== patronIdFromResponse) { + throw new Error( + "Error in HoldConfirmationPage getServerSideProps: Logged in patron Id doesn't match the patron Id in the hold request." + ) + } + + return { + props: { + isEDD: pickupLocation === "edd", + }, + } + } catch (error) { + console.log(error) + + return { + redirect: { + destination: PATHS["404"], + permanent: false, + }, + } } } diff --git a/src/server/api/hold.ts b/src/server/api/hold.ts index 6e110eac2..381fefea1 100644 --- a/src/server/api/hold.ts +++ b/src/server/api/hold.ts @@ -2,7 +2,7 @@ import nyplApiClient from "../nyplApiClient" import type { HoldPostResult, - HoldRequestDetailsParams, + HoldDetailsResult, } from "../../types/holdPageTypes" import type { DeliveryLocation, @@ -126,27 +126,28 @@ export async function postHoldRequest( /** * Getter function for hold request details. */ -export async function fetchHoldRequestDetails( - holdRequestDetailsParams: HoldRequestDetailsParams -): Promise { - const { requestId, patronId } = holdRequestDetailsParams - +// TODO: Add return type +export async function fetchHoldDetails( + requestId: string +): Promise { try { const client = await nyplApiClient() const holdDetailsResult = await client.get(`/hold-requests/${requestId}`) - if (patronId !== holdDetailsResult.data.patron) { - console.log("TODO: Redirect to 404 page") - } + const { id, pickupLocation, patron } = holdDetailsResult.data - console.log("holdDetailsResult", holdDetailsResult) - return + return { + requestId: id, + patronId: patron, + pickupLocation, + status: 200, + } } catch (error) { console.error( `Error fetching hold request details in fetchHoldRequestDetails server function, requestId: ${requestId}`, error.message ) - return + return { status: 400 } } } diff --git a/src/types/holdPageTypes.ts b/src/types/holdPageTypes.ts index ede826764..ab691fb44 100644 --- a/src/types/holdPageTypes.ts +++ b/src/types/holdPageTypes.ts @@ -21,9 +21,19 @@ export interface EDDRequestParams extends HoldRequestParams { requestNotes?: string } -export interface HoldRequestDetailsParams { - requestId: string - patronId: string +export interface HoldPostResult { + status: HTTPStatusCode + pickupLocation?: NYPLocationKey | "edd" + formInvalid?: boolean + requestId?: string +} + +export interface HoldDetailsResult { + status: HTTPStatusCode + pickupLocation?: NYPLocationKey | "edd" + patronId?: string + requestId?: string + errorMessage?: string } export interface PatronEligibilityStatus { @@ -36,13 +46,6 @@ export interface PatronEligibilityStatus { hasIssues?: boolean } -export interface HoldPostResult { - status: HTTPStatusCode - pickupLocation?: NYPLocationKey | "edd" - requestId?: string - errorMessage?: string -} - export interface DiscoveryHoldPostParams { patron: string record: string @@ -52,7 +55,7 @@ export interface DiscoveryHoldPostParams { pickupLocation?: NYPLocationKey | "edd" numberOfCopies?: number // TODO: make this EDD form content object - docDeliveryData?: string + docDeliveryData?: EDDRequestParams } export type EDDPageStatus = null | "failed" | "unavailable" | "invalid" From 1393182ec477c952b41b739a4e29b4004743a023 Mon Sep 17 00:00:00 2001 From: Diego Cohen Date: Fri, 22 Nov 2024 17:13:05 -0500 Subject: [PATCH 3/6] Remove unused imports --- pages/hold/confirmation/[id].tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pages/hold/confirmation/[id].tsx b/pages/hold/confirmation/[id].tsx index 334e7dbea..e813ccc52 100644 --- a/pages/hold/confirmation/[id].tsx +++ b/pages/hold/confirmation/[id].tsx @@ -6,7 +6,6 @@ import { SITE_NAME, HOLD_PAGE_HEADING, EDD_PAGE_HEADING, - BASE_URL, PATHS, } from "../../../src/config/constants" @@ -20,15 +19,6 @@ import initializePatronTokenAuth, { getLoginRedirect, } from "../../../src/server/auth" -import Bib from "../../../src/models/Bib" -import Item from "../../../src/models/Item" - -import type { DiscoveryBibResult } from "../../../src/types/bibTypes" -import type { DiscoveryItemResult } from "../../../src/types/itemTypes" - -import { fetchBib } from "../../../src/server/api/bib" -import { fetchDeliveryLocations } from "../../../src/server/api/hold" - interface HoldConfirmationPageProps { isEDD?: boolean } From 1c81830c95f0d6be1e40455915525845f5b5185b Mon Sep 17 00:00:00 2001 From: Diego Cohen Date: Fri, 22 Nov 2024 17:16:24 -0500 Subject: [PATCH 4/6] Fix errors --- pages/hold/confirmation/[id].tsx | 7 +++++-- src/types/holdPageTypes.ts | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pages/hold/confirmation/[id].tsx b/pages/hold/confirmation/[id].tsx index e813ccc52..5304df2cc 100644 --- a/pages/hold/confirmation/[id].tsx +++ b/pages/hold/confirmation/[id].tsx @@ -66,6 +66,7 @@ export default function HoldConfirmationPage({ } export async function getServerSideProps({ req, res, query }) { + const { pickupLocation, requestId } = query // authentication redirect const patronTokenResponse = await initializePatronTokenAuth(req.cookies) const isAuthenticated = patronTokenResponse.isTokenValid @@ -82,7 +83,10 @@ export async function getServerSideProps({ req, res, query }) { redirectCount + 1 }; Max-Age=10; path=/; domain=.nypl.org;` ) - const redirect = getLoginRedirect(req, `${PATHS.HOLD_REQUEST}/${id}/edd`) + const redirect = getLoginRedirect( + req, + `${PATHS.HOLD_CONFIRMATION}?pickupLocation=${pickupLocation}&requestId=${requestId}` + ) return { redirect: { @@ -93,7 +97,6 @@ export async function getServerSideProps({ req, res, query }) { } const patronId = patronTokenResponse?.decodedPatron?.sub - const { pickupLocation, requestId } = query try { const { patronId: patronIdFromResponse } = await fetchHoldDetails(requestId) diff --git a/src/types/holdPageTypes.ts b/src/types/holdPageTypes.ts index ab691fb44..60e9fa2d0 100644 --- a/src/types/holdPageTypes.ts +++ b/src/types/holdPageTypes.ts @@ -26,6 +26,7 @@ export interface HoldPostResult { pickupLocation?: NYPLocationKey | "edd" formInvalid?: boolean requestId?: string + errorMessage?: string } export interface HoldDetailsResult { From d72f36c5e80c1e537b44cadc1ae236f5bb576ce3 Mon Sep 17 00:00:00 2001 From: Edwin Guzman Date: Tue, 26 Nov 2024 09:30:22 -0500 Subject: [PATCH 5/6] Deploy to train --- .github/workflows/deploy_train.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy_train.yml b/.github/workflows/deploy_train.yml index 4af976703..f91154046 100644 --- a/.github/workflows/deploy_train.yml +++ b/.github/workflows/deploy_train.yml @@ -7,6 +7,7 @@ on: - train - set-up-train-env - hold-pages + - SCC-3762/hold-confirmation-server-functions permissions: id-token: write From 4a1929c36d36274e94d264c48923697b216bb2bb Mon Sep 17 00:00:00 2001 From: Edwin Guzman Date: Tue, 26 Nov 2024 09:33:05 -0500 Subject: [PATCH 6/6] remove gha train test --- .github/workflows/deploy_train.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/deploy_train.yml b/.github/workflows/deploy_train.yml index f91154046..4af976703 100644 --- a/.github/workflows/deploy_train.yml +++ b/.github/workflows/deploy_train.yml @@ -7,7 +7,6 @@ on: - train - set-up-train-env - hold-pages - - SCC-3762/hold-confirmation-server-functions permissions: id-token: write