diff --git a/actions/editProfile.ts b/actions/editProfile.ts index 010772c4a..8f0791c48 100644 --- a/actions/editProfile.ts +++ b/actions/editProfile.ts @@ -4,7 +4,7 @@ import { z } from "zod" import { ApiLang } from "@/constants/languages" import * as api from "@/lib/api" -import { serverClient } from "@/lib/trpc/server" +import { getProfile } from "@/lib/trpc/memoizedRequests" import { protectedServerActionProcedure } from "@/server/trpc" import { editProfileSchema } from "@/components/Forms/Edit/Profile/schema" @@ -68,7 +68,7 @@ export const editProfile = protectedServerActionProcedure } } - const profile = await serverClient().user.get() + const profile = await getProfile() if (!profile || "error" in profile) { console.error( "editProfile profile fetch error", diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/@membershipCards/page.tsx b/app/[lang]/(live)/(protected)/my-pages/profile/@membershipCards/page.tsx index 226a33860..693bdc171 100644 --- a/app/[lang]/(live)/(protected)/my-pages/profile/@membershipCards/page.tsx +++ b/app/[lang]/(live)/(protected)/my-pages/profile/@membershipCards/page.tsx @@ -1,4 +1,4 @@ -import { serverClient } from "@/lib/trpc/server" +import { getMembershipCards } from "@/lib/trpc/memoizedRequests" import { PlusCircleIcon } from "@/components/Icons" import Link from "@/components/TempDesignSystem/Link" @@ -16,7 +16,7 @@ export default async function MembershipCardSlot({ }: PageArgs) { setLang(params.lang) const { formatMessage } = await getIntl() - const membershipCards = await serverClient().user.membershipCards() + const membershipCards = await getMembershipCards() return (
diff --git a/components/Blocks/DynamicContent/OverviewTable/index.tsx b/components/Blocks/DynamicContent/OverviewTable/index.tsx index e5c23b370..f39fb9a25 100644 --- a/components/Blocks/DynamicContent/OverviewTable/index.tsx +++ b/components/Blocks/DynamicContent/OverviewTable/index.tsx @@ -1,3 +1,4 @@ +import { getMembershipLevelSafely } from "@/lib/trpc/memoizedRequests" import { serverClient } from "@/lib/trpc/server" import SectionWrapper from "../SectionWrapper" @@ -11,7 +12,7 @@ export default async function OverviewTable({ }: OverviewTableProps) { const [levels, membershipLevel] = await Promise.all([ serverClient().contentstack.rewards.all(), - serverClient().user.safeMembershipLevel(), + getMembershipLevelSafely(), ]) return ( diff --git a/components/Blocks/DynamicContent/Points/ExpiringPoints/index.tsx b/components/Blocks/DynamicContent/Points/ExpiringPoints/index.tsx index ebfa71d28..8b0206351 100644 --- a/components/Blocks/DynamicContent/Points/ExpiringPoints/index.tsx +++ b/components/Blocks/DynamicContent/Points/ExpiringPoints/index.tsx @@ -1,4 +1,4 @@ -import { serverClient } from "@/lib/trpc/server" +import { getMembershipLevel } from "@/lib/trpc/memoizedRequests" import SectionContainer from "@/components/Section/Container" import SectionHeader from "@/components/Section/Header" @@ -12,7 +12,7 @@ export default async function ExpiringPoints({ subtitle, title, }: AccountPageComponentProps) { - const membershipLevel = await serverClient().user.membershipLevel() + const membershipLevel = await getMembershipLevel() if (!membershipLevel?.pointsToExpire || !membershipLevel?.pointsExpiryDate) { return null diff --git a/components/Blocks/DynamicContent/Rewards/NextLevel/index.tsx b/components/Blocks/DynamicContent/Rewards/NextLevel/index.tsx index cf86c6aa1..7517ed955 100644 --- a/components/Blocks/DynamicContent/Rewards/NextLevel/index.tsx +++ b/components/Blocks/DynamicContent/Rewards/NextLevel/index.tsx @@ -1,6 +1,7 @@ import { Lock } from "react-feather" import { MembershipLevelEnum } from "@/constants/membershipLevels" +import { getMembershipLevel } from "@/lib/trpc/memoizedRequests" import { serverClient } from "@/lib/trpc/server" import SectionContainer from "@/components/Section/Container" @@ -22,7 +23,7 @@ export default async function NextLevelRewardsBlock({ link, }: AccountPageComponentProps) { const intl = await getIntl() - const membershipLevel = await serverClient().user.membershipLevel() + const membershipLevel = await getMembershipLevel() if (!membershipLevel || !membershipLevel?.nextLevel) { return null diff --git a/components/Current/Header/TopMenu/index.tsx b/components/Current/Header/TopMenu/index.tsx index dcce0b333..1472461d8 100644 --- a/components/Current/Header/TopMenu/index.tsx +++ b/components/Current/Header/TopMenu/index.tsx @@ -1,6 +1,6 @@ import { logout } from "@/constants/routes/handleAuth" import { overview } from "@/constants/routes/myPages" -import { serverClient } from "@/lib/trpc/server" +import { getName } from "@/lib/trpc/memoizedRequests" import Link from "@/components/TempDesignSystem/Link" import { getIntl } from "@/i18n" @@ -23,7 +23,7 @@ export default async function TopMenu({ languageSwitcher, }: TopMenuProps) { const { formatMessage } = await getIntl() - const user = await serverClient().user.name() + const user = await getName() return (
diff --git a/components/Current/Header/index.tsx b/components/Current/Header/index.tsx index 2b376c778..59b5e479b 100644 --- a/components/Current/Header/index.tsx +++ b/components/Current/Header/index.tsx @@ -1,6 +1,6 @@ import { homeHrefs } from "@/constants/homeHrefs" import { env } from "@/env/server" -import { getLanguageSwitcher } from "@/lib/trpc/memoizedRequests" +import { getLanguageSwitcher, getName } from "@/lib/trpc/memoizedRequests" import { serverClient } from "@/lib/trpc/server" import { getLang } from "@/i18n/serverContext" @@ -18,7 +18,7 @@ export default async function Header() { serverClient().contentstack.base.currentHeader({ lang: getLang(), }), - serverClient().user.name(), + getName(), getLanguageSwitcher(), serverClient().contentstack.myPages.navigation.get(), ]) diff --git a/components/Header/MainMenu/MyPagesMenuWrapper/index.tsx b/components/Header/MainMenu/MyPagesMenuWrapper/index.tsx index eacb037c6..0f5a8ef0e 100644 --- a/components/Header/MainMenu/MyPagesMenuWrapper/index.tsx +++ b/components/Header/MainMenu/MyPagesMenuWrapper/index.tsx @@ -1,5 +1,6 @@ import { MembershipLevelEnum } from "@/constants/membershipLevels" import { myPages } from "@/constants/routes/myPages" +import { getMembershipLevelSafely, getName } from "@/lib/trpc/memoizedRequests" import { serverClient } from "@/lib/trpc/server" import Link from "@/components/TempDesignSystem/Link" @@ -17,8 +18,8 @@ export default async function MyPagesMenuWrapper() { const [intl, myPagesNavigation, user, membership] = await Promise.all([ getIntl(), serverClient().contentstack.myPages.navigation.get(), - serverClient().user.name(), - serverClient().user.safeMembershipLevel(), + getName(), + getMembershipLevelSafely(), ]) const membershipLevel = membership?.membershipLevel diff --git a/components/Sidebar/JoinLoyalty/index.tsx b/components/Sidebar/JoinLoyalty/index.tsx index ae5f582d5..31465ae78 100644 --- a/components/Sidebar/JoinLoyalty/index.tsx +++ b/components/Sidebar/JoinLoyalty/index.tsx @@ -1,4 +1,4 @@ -import { serverClient } from "@/lib/trpc/server" +import { getName } from "@/lib/trpc/memoizedRequests" import LoginButton from "@/components/Current/Header/LoginButton" import ArrowRight from "@/components/Icons/ArrowRight" @@ -19,10 +19,10 @@ export default async function JoinLoyaltyContact({ block, }: JoinLoyaltyContactProps) { const intl = await getIntl() - const user = await serverClient().user.name() + const username = await getName() // Check if we have user, that means we are logged in. - if (user) { + if (username) { return null } return ( diff --git a/components/Sidebar/MyPagesNavigation.tsx b/components/Sidebar/MyPagesNavigation.tsx index 23f60e144..62d62cb82 100644 --- a/components/Sidebar/MyPagesNavigation.tsx +++ b/components/Sidebar/MyPagesNavigation.tsx @@ -1,12 +1,12 @@ -import { serverClient } from "@/lib/trpc/server" +import { getName } from "@/lib/trpc/memoizedRequests" import MyPagesSidebar from "@/components/MyPages/Sidebar" export default async function MyPagesNavigation() { - const user = await serverClient().user.name() + const username = await getName() // Check if we have user, that means we are logged in andt the My Pages menu can show. - if (!user) { + if (!username) { return null } return diff --git a/components/TrackingSDK/index.tsx b/components/TrackingSDK/index.tsx index d267341b6..05fd93d9c 100644 --- a/components/TrackingSDK/index.tsx +++ b/components/TrackingSDK/index.tsx @@ -1,3 +1,4 @@ +import { getUserTracking } from "@/lib/trpc/memoizedRequests" import { serverClient } from "@/lib/trpc/server" import RouterTransition from "@/components/TrackingSDK/RouterTransition" @@ -7,7 +8,7 @@ import TrackingSDKClient from "./Client" import { TrackingSDKPageData } from "@/types/components/tracking" export const preloadUserTracking = () => { - void serverClient().user.tracking() + void getUserTracking() } export default async function TrackingSDK({ @@ -15,7 +16,7 @@ export default async function TrackingSDK({ }: { pageData: TrackingSDKPageData }) { - const userTrackingData = await serverClient().user.tracking() + const userTrackingData = await getUserTracking() return ( <> diff --git a/lib/trpc/memoizedRequests/index.ts b/lib/trpc/memoizedRequests/index.ts index 471d20def..2bf70a9e9 100644 --- a/lib/trpc/memoizedRequests/index.ts +++ b/lib/trpc/memoizedRequests/index.ts @@ -22,6 +22,32 @@ export const getCreditCardsSafely = cache( } ) +export const getMembershipLevel = cache( + async function getMemoizedMembershipLevel() { + return serverClient().user.membershipLevel() + } +) + +export const getMembershipLevelSafely = cache( + async function getMemoizedMembershipLevelSafely() { + return serverClient().user.safeMembershipLevel() + } +) + +export const getMembershipCards = cache( + async function getMemoizedMembershipCards() { + return serverClient().user.membershipCards() + } +) + +export const getName = cache(async function getMemoizedName() { + return serverClient().user.name() +}) + +export const getUserTracking = cache(async function getMemoizedUserTracking() { + return serverClient().user.tracking() +}) + export const getHotelData = cache(async function getMemoizedHotelData( hotelId: string, language: string diff --git a/server/routers/user/query.ts b/server/routers/user/query.ts index 5b823c68d..224f03198 100644 --- a/server/routers/user/query.ts +++ b/server/routers/user/query.ts @@ -1,4 +1,5 @@ import { metrics } from "@opentelemetry/api" +import { cache } from "react" import * as api from "@/lib/api" import { @@ -80,86 +81,87 @@ const getCreditCardsFailCounter = meter.createCounter( "trpc.user.creditCards-fail" ) -export async function getVerifiedUser({ session }: { session: Session }) { - const now = Date.now() - - if (session.token.expires_at && session.token.expires_at < now) { - return { error: true, cause: "token_expired" } as const - } - getVerifiedUserCounter.add(1) - console.info("api.user.profile getVerifiedUser start", JSON.stringify({})) - const apiResponse = await api.get(api.endpoints.v1.profile, { - headers: { - Authorization: `Bearer ${session.token.access_token}`, - }, - }) - - if (!apiResponse.ok) { - const text = await apiResponse.text() - getVerifiedUserFailCounter.add(1, { - error_type: "http_error", - error: JSON.stringify({ - status: apiResponse.status, - statusText: apiResponse.statusText, - text, - }), +export const getVerifiedUser = cache( + async ({ session }: { session: Session }) => { + const now = Date.now() + if (session.token.expires_at && session.token.expires_at < now) { + return { error: true, cause: "token_expired" } as const + } + getVerifiedUserCounter.add(1) + console.info("api.user.profile getVerifiedUser start", JSON.stringify({})) + const apiResponse = await api.get(api.endpoints.v1.profile, { + headers: { + Authorization: `Bearer ${session.token.access_token}`, + }, }) - console.error( - "api.user.profile getVerifiedUser error", - JSON.stringify({ - error: { + + if (!apiResponse.ok) { + const text = await apiResponse.text() + getVerifiedUserFailCounter.add(1, { + error_type: "http_error", + error: JSON.stringify({ status: apiResponse.status, statusText: apiResponse.statusText, text, - }, + }), }) - ) - if (apiResponse.status === 401) { - return { error: true, cause: "unauthorized" } as const - } else if (apiResponse.status === 403) { - return { error: true, cause: "forbidden" } as const - } else if (apiResponse.status === 404) { - return { error: true, cause: "notfound" } as const + console.error( + "api.user.profile getVerifiedUser error", + JSON.stringify({ + error: { + status: apiResponse.status, + statusText: apiResponse.statusText, + text, + }, + }) + ) + if (apiResponse.status === 401) { + return { error: true, cause: "unauthorized" } as const + } else if (apiResponse.status === 403) { + return { error: true, cause: "forbidden" } as const + } else if (apiResponse.status === 404) { + return { error: true, cause: "notfound" } as const + } + return { + error: true, + cause: "unknown", + status: apiResponse.status, + } as const } - return { - error: true, - cause: "unknown", - status: apiResponse.status, - } as const - } - const apiJson = await apiResponse.json() - if (!apiJson.data?.attributes) { - getVerifiedUserFailCounter.add(1, { - error_type: "data_error", - }) - console.error( - "api.user.profile getVerifiedUser data error", - JSON.stringify({ - apiResponse: apiJson, + const apiJson = await apiResponse.json() + if (!apiJson.data?.attributes) { + getVerifiedUserFailCounter.add(1, { + error_type: "data_error", }) - ) - return null - } + console.error( + "api.user.profile getVerifiedUser data error", + JSON.stringify({ + apiResponse: apiJson, + }) + ) + return null + } - const verifiedData = getUserSchema.safeParse(apiJson) - if (!verifiedData.success) { - getVerifiedUserFailCounter.add(1, { - error_type: "validation_error", - error: JSON.stringify(verifiedData.error), - }) - console.error( - "api.user.profile validation error", - JSON.stringify({ - errors: verifiedData.error, + const verifiedData = getUserSchema.safeParse(apiJson) + if (!verifiedData.success) { + getVerifiedUserFailCounter.add(1, { + error_type: "validation_error", + error: JSON.stringify(verifiedData.error), }) - ) - return null + console.error( + "api.user.profile validation error", + JSON.stringify({ + errors: verifiedData.error, + }) + ) + return null + } + getVerifiedUserSuccessCounter.add(1) + console.info("api.user.profile getVerifiedUser success", JSON.stringify({})) + return verifiedData } - getVerifiedUserSuccessCounter.add(1) - console.info("api.user.profile getVerifiedUser success", JSON.stringify({})) - return verifiedData -} +) function parsedUser(data: User, isMFA: boolean) { const country = countries.find((c) => c.code === data.address.countryCode)