From bc50dcf286cc35f409fd158231466dc2e09e955c Mon Sep 17 00:00:00 2001 From: Erik Tiekstra Date: Tue, 25 Feb 2025 14:40:31 +0000 Subject: [PATCH] Merged in fix/SW-1754-overview-page-rate-limit (pull request #1412) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix(SW-1754): Fix rate limit issue on Destination Overview Page * fix(SW-1754): Fix rate limit issue on Destination Overview Page Approved-by: Matilda Landström --- .../DestinationsList/Destination/index.tsx | 19 +++-- .../hotelListingItem.module.css | 16 ++++ .../HotelListing/HotelListingItem/index.tsx | 24 ++++-- .../DestinationCityListData.graphql | 6 +- .../DestinationCityPageUrl.graphql | 20 +++++ .../DestinationCountryPageUrl.graphql | 15 ++++ .../DestinationOverviewPage.graphql | 14 ---- .../Query/HotelPage/HotelPageUrl.graphql | 5 +- .../destinationCityPage/output.ts | 50 ++++++++++++ .../destinationCityPage/telemetry.ts | 10 +++ .../contentstack/destinationCityPage/utils.ts | 74 +++++++++++++++++- .../destinationCountryPage/output.ts | 27 +++++++ .../destinationCountryPage/telemetry.ts | 12 +++ .../destinationCountryPage/utils.ts | 73 +++++++++++++++++- .../destinationOverviewPage/output.ts | 23 ------ .../destinationOverviewPage/query.ts | 26 +++---- .../destinationOverviewPage/telemetry.ts | 12 --- .../destinationOverviewPage/utils.ts | 76 ------------------- .../routers/contentstack/hotelPage/output.ts | 28 ++++--- .../contentstack/hotelPage/telemetry.ts | 12 +-- .../routers/contentstack/hotelPage/utils.ts | 68 ++++++++--------- server/routers/hotels/query.ts | 20 ++--- server/routers/hotels/utils.ts | 18 ++--- .../contentstack/destinationCityPage.ts | 5 ++ .../contentstack/destinationCountryPage.ts | 4 + .../contentstack/destinationOverviewPage.ts | 4 - types/trpc/routers/contentstack/hotelPage.ts | 9 +-- 27 files changed, 426 insertions(+), 244 deletions(-) create mode 100644 lib/graphql/Query/DestinationCityPage/DestinationCityPageUrl.graphql create mode 100644 lib/graphql/Query/DestinationCountryPage/DestinationCountryPageUrl.graphql delete mode 100644 server/routers/contentstack/destinationOverviewPage/utils.ts diff --git a/components/ContentType/DestinationPage/DestinationOverviewPage/HotelsSection/DestinationsList/Destination/index.tsx b/components/ContentType/DestinationPage/DestinationOverviewPage/HotelsSection/DestinationsList/Destination/index.tsx index 0ac1ada0b..6df3c2ef5 100644 --- a/components/ContentType/DestinationPage/DestinationOverviewPage/HotelsSection/DestinationsList/Destination/index.tsx +++ b/components/ContentType/DestinationPage/DestinationOverviewPage/HotelsSection/DestinationsList/Destination/index.tsx @@ -1,6 +1,7 @@ import { ArrowRightIcon } from "@/components/Icons" import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem" import Link from "@/components/TempDesignSystem/Link" +import Body from "@/components/TempDesignSystem/Text/Body" import { getIntl } from "@/i18n" import styles from "./destination.module.css" @@ -27,13 +28,17 @@ export default async function Destination({ diff --git a/components/ContentType/DestinationPage/HotelListing/HotelListingItem/hotelListingItem.module.css b/components/ContentType/DestinationPage/HotelListing/HotelListingItem/hotelListingItem.module.css index f2e381031..91e5e4898 100644 --- a/components/ContentType/DestinationPage/HotelListing/HotelListingItem/hotelListingItem.module.css +++ b/components/ContentType/DestinationPage/HotelListing/HotelListingItem/hotelListingItem.module.css @@ -13,6 +13,22 @@ justify-items: start; } +.imageWrapper { + position: relative; +} + +.tripAdvisor { + position: absolute; + top: var(--Spacing-x2); + left: var(--Spacing-x2); + display: flex; + align-items: center; + gap: var(--Spacing-x-half); + background-color: var(--Base-Surface-Primary-light-Normal); + padding: var(--Spacing-x-quarter) var(--Spacing-x1); + border-radius: var(--Corner-radius-Small); +} + .intro { display: grid; gap: var(--Spacing-x-half); diff --git a/components/ContentType/DestinationPage/HotelListing/HotelListingItem/index.tsx b/components/ContentType/DestinationPage/HotelListing/HotelListingItem/index.tsx index cb3a22173..3846ff2eb 100644 --- a/components/ContentType/DestinationPage/HotelListing/HotelListingItem/index.tsx +++ b/components/ContentType/DestinationPage/HotelListing/HotelListingItem/index.tsx @@ -4,7 +4,7 @@ import Link from "next/link" import { useIntl } from "react-intl" import { mapFacilityToIcon } from "@/components/ContentType/HotelPage/data" -import { ChevronRightSmallIcon } from "@/components/Icons" +import { ChevronRightSmallIcon, TripAdvisorIcon } from "@/components/Icons" import HotelLogo from "@/components/Icons/Logos" import ImageGallery from "@/components/ImageGallery" import Button from "@/components/TempDesignSystem/Button" @@ -34,13 +34,23 @@ export default function HotelListingItem({ return (
- + + {hotel.ratings?.tripAdvisor.rating && ( +
+ + + {hotel.ratings.tripAdvisor.rating} + +
)} - /> +
diff --git a/lib/graphql/Query/DestinationCityPage/DestinationCityListData.graphql b/lib/graphql/Query/DestinationCityPage/DestinationCityListData.graphql index 93170d6a3..c8aa0688e 100644 --- a/lib/graphql/Query/DestinationCityPage/DestinationCityListData.graphql +++ b/lib/graphql/Query/DestinationCityPage/DestinationCityListData.graphql @@ -4,8 +4,12 @@ query GetDestinationCityListData($locale: String!, $cityIdentifier: String!) { all_destination_city_page( where: { OR: [ - { destination_settings: { city_sweden: $cityIdentifier } } { destination_settings: { city_denmark: $cityIdentifier } } + { destination_settings: { city_finland: $cityIdentifier } } + { destination_settings: { city_germany: $cityIdentifier } } + { destination_settings: { city_norway: $cityIdentifier } } + { destination_settings: { city_poland: $cityIdentifier } } + { destination_settings: { city_sweden: $cityIdentifier } } ] } locale: $locale diff --git a/lib/graphql/Query/DestinationCityPage/DestinationCityPageUrl.graphql b/lib/graphql/Query/DestinationCityPage/DestinationCityPageUrl.graphql new file mode 100644 index 000000000..6bded7626 --- /dev/null +++ b/lib/graphql/Query/DestinationCityPage/DestinationCityPageUrl.graphql @@ -0,0 +1,20 @@ +#import "../../Fragments/System.graphql" + +query GetCityPageUrls($locale: String!) { + all_destination_city_page(locale: $locale) { + items { + url + destination_settings { + city_denmark + city_finland + city_germany + city_norway + city_poland + city_sweden + } + system { + ...System + } + } + } +} diff --git a/lib/graphql/Query/DestinationCountryPage/DestinationCountryPageUrl.graphql b/lib/graphql/Query/DestinationCountryPage/DestinationCountryPageUrl.graphql new file mode 100644 index 000000000..4c21f5bdc --- /dev/null +++ b/lib/graphql/Query/DestinationCountryPage/DestinationCountryPageUrl.graphql @@ -0,0 +1,15 @@ +#import "../../Fragments/System.graphql" + +query GetCountryPageUrls($locale: String!) { + all_destination_country_page(locale: $locale) { + items { + url + destination_settings { + country + } + system { + ...System + } + } + } +} diff --git a/lib/graphql/Query/DestinationOverviewPage/DestinationOverviewPage.graphql b/lib/graphql/Query/DestinationOverviewPage/DestinationOverviewPage.graphql index 8d55e2ebb..d9cbf41f3 100644 --- a/lib/graphql/Query/DestinationOverviewPage/DestinationOverviewPage.graphql +++ b/lib/graphql/Query/DestinationOverviewPage/DestinationOverviewPage.graphql @@ -33,20 +33,6 @@ query GetDestinationOverviewPageRefs($locale: String!, $uid: String!) { } } -query GetCountryPageUrl($locale: String!, $country: String!) { - all_destination_country_page( - where: { destination_settings: { country: $country } } - locale: $locale - ) { - items { - url - system { - ...System - } - } - } -} - query GetDaDeEnUrlsDestinationOverviewPage($uid: String!) { de: destination_overview_page(locale: "de", uid: $uid) { url diff --git a/lib/graphql/Query/HotelPage/HotelPageUrl.graphql b/lib/graphql/Query/HotelPage/HotelPageUrl.graphql index fdb103679..26c28cfd9 100644 --- a/lib/graphql/Query/HotelPage/HotelPageUrl.graphql +++ b/lib/graphql/Query/HotelPage/HotelPageUrl.graphql @@ -1,9 +1,10 @@ #import "../../Fragments/System.graphql" -query GetHotelPageUrl($locale: String!, $hotelId: String!) { - all_hotel_page(locale: $locale, where: { hotel_page_id: $hotelId }) { +query GetHotelPageUrls($locale: String!) { + all_hotel_page(locale: $locale) { items { url + hotel_page_id system { ...System } diff --git a/server/routers/contentstack/destinationCityPage/output.ts b/server/routers/contentstack/destinationCityPage/output.ts index 873160b53..aeee05c91 100644 --- a/server/routers/contentstack/destinationCityPage/output.ts +++ b/server/routers/contentstack/destinationCityPage/output.ts @@ -185,6 +185,56 @@ export const destinationCityPageSchema = z } }) +export const cityPageUrlsSchema = z + .object({ + all_destination_city_page: z.object({ + items: z.array( + z + .object({ + url: z.string(), + destination_settings: z + .object({ + city_denmark: z.string().optional().nullable(), + city_finland: z.string().optional().nullable(), + city_germany: z.string().optional().nullable(), + city_poland: z.string().optional().nullable(), + city_norway: z.string().optional().nullable(), + city_sweden: z.string().optional().nullable(), + }) + .transform( + ({ + city_denmark, + city_finland, + city_germany, + city_norway, + city_poland, + city_sweden, + }) => { + const cities = [ + city_denmark, + city_finland, + city_germany, + city_poland, + city_norway, + city_sweden, + ].filter((city): city is string => Boolean(city)) + + return { city: cities[0] } + } + ), + system: systemSchema, + }) + .transform((data) => { + return { + city: data.destination_settings.city, + url: removeMultipleSlashes(`/${data.system.locale}/${data.url}`), + } + }) + ), + }), + }) + .transform(({ all_destination_city_page }) => all_destination_city_page.items) + /** REFS */ const destinationCityPageContentRefs = z .object({ diff --git a/server/routers/contentstack/destinationCityPage/telemetry.ts b/server/routers/contentstack/destinationCityPage/telemetry.ts index 92440d1ce..24a844e20 100644 --- a/server/routers/contentstack/destinationCityPage/telemetry.ts +++ b/server/routers/contentstack/destinationCityPage/telemetry.ts @@ -21,3 +21,13 @@ export const getDestinationCityPageSuccessCounter = meter.createCounter( export const getDestinationCityPageFailCounter = meter.createCounter( "trpc.contentstack.destinationCityPage.get-fail" ) + +export const getCityPageUrlsCounter = meter.createCounter( + "trpc.contentstack.cityPageUrls.get" +) +export const getCityPageUrlsSuccessCounter = meter.createCounter( + "trpc.contentstack.cityPageUrls.get-success" +) +export const getCityPageUrlsFailCounter = meter.createCounter( + "trpc.contentstack.cityPageUrls.get-fail" +) diff --git a/server/routers/contentstack/destinationCityPage/utils.ts b/server/routers/contentstack/destinationCityPage/utils.ts index b1c69f28f..ccc952d88 100644 --- a/server/routers/contentstack/destinationCityPage/utils.ts +++ b/server/routers/contentstack/destinationCityPage/utils.ts @@ -1,8 +1,21 @@ +import { GetCityPageUrls } from "@/lib/graphql/Query/DestinationCityPage/DestinationCityPageUrl.graphql" +import { request } from "@/lib/graphql/request" + import { generateTag, generateTagsFromSystem } from "@/utils/generateTag" +import { cityPageUrlsSchema } from "./output" +import { + getCityPageUrlsCounter, + getCityPageUrlsFailCounter, + getCityPageUrlsSuccessCounter, +} from "./telemetry" + import { DestinationCityPageEnum } from "@/types/enums/destinationCityPage" import type { System } from "@/types/requests/system" -import type { DestinationCityPageRefs } from "@/types/trpc/routers/contentstack/destinationCityPage" +import type { + DestinationCityPageRefs, + GetCityPageUrlsData, +} from "@/types/trpc/routers/contentstack/destinationCityPage" import type { Lang } from "@/constants/languages" export function generatePageTags( @@ -51,3 +64,62 @@ export function getConnections({ return connections } + +export async function getCityPageUrls(lang: Lang) { + getCityPageUrlsCounter.add(1, { lang }) + console.info( + "contentstack.cityPageUrls start", + JSON.stringify({ query: { lang } }) + ) + const tag = `${lang}:city_page_urls` + const response = await request( + GetCityPageUrls, + { + locale: lang, + }, + { + cache: "force-cache", + next: { + tags: [tag], + }, + } + ) + + if (!response.data) { + getCityPageUrlsFailCounter.add(1, { + lang, + error_type: "not_found", + error: `Destination city pages not found for lang: ${lang}`, + }) + console.error( + "contentstack.cityPageUrls not found error", + JSON.stringify({ query: { lang } }) + ) + return [] + } + + const validatedResponse = cityPageUrlsSchema.safeParse(response.data) + + if (!validatedResponse.success) { + getCityPageUrlsFailCounter.add(1, { + lang, + error_type: "validation_error", + error: JSON.stringify(validatedResponse.error), + }) + console.error( + "contentstack.cityPageUrls validation error", + JSON.stringify({ + query: { lang }, + error: validatedResponse.error, + }) + ) + return [] + } + getCityPageUrlsSuccessCounter.add(1, { lang }) + console.info( + "contentstack.cityPageUrls success", + JSON.stringify({ query: { lang } }) + ) + + return validatedResponse.data +} diff --git a/server/routers/contentstack/destinationCountryPage/output.ts b/server/routers/contentstack/destinationCountryPage/output.ts index 94fab6d47..a77aeb627 100644 --- a/server/routers/contentstack/destinationCountryPage/output.ts +++ b/server/routers/contentstack/destinationCountryPage/output.ts @@ -2,6 +2,8 @@ import { z } from "zod" import { discriminatedUnionArray } from "@/lib/discriminatedUnion" +import { removeMultipleSlashes } from "@/utils/url" + import { accordionRefsSchema, accordionSchema, @@ -120,6 +122,31 @@ export const destinationCountryPageSchema = z } }) +export const countryPageUrlsSchema = z + .object({ + all_destination_country_page: z.object({ + items: z.array( + z + .object({ + url: z.string(), + destination_settings: z.object({ + country: z.string(), + }), + system: systemSchema, + }) + .transform((data) => { + return { + country: data.destination_settings.country, + url: removeMultipleSlashes(`/${data.system.locale}/${data.url}`), + } + }) + ), + }), + }) + .transform( + ({ all_destination_country_page }) => all_destination_country_page.items + ) + /** REFS */ const destinationCountryPageContentRefs = z .object({ diff --git a/server/routers/contentstack/destinationCountryPage/telemetry.ts b/server/routers/contentstack/destinationCountryPage/telemetry.ts index 9206fd74b..4030043fc 100644 --- a/server/routers/contentstack/destinationCountryPage/telemetry.ts +++ b/server/routers/contentstack/destinationCountryPage/telemetry.ts @@ -31,3 +31,15 @@ export const getCityListDataSuccessCounter = meter.createCounter( export const getCityListDataFailCounter = meter.createCounter( "trpc.contentstack.cityListData.get-fail" ) + +export const getCountryPageUrlsCounter = meter.createCounter( + "trpc.contentstack.getCountryPageUrls" +) + +export const getCountryPageUrlsSuccessCounter = meter.createCounter( + "trpc.contentstack.getCountryPageUrls-success" +) + +export const getCountryPageUrlsFailCounter = meter.createCounter( + "trpc.contentstack.getCountryPageUrls-fail" +) diff --git a/server/routers/contentstack/destinationCountryPage/utils.ts b/server/routers/contentstack/destinationCountryPage/utils.ts index 882dcf3b4..81b2d1176 100644 --- a/server/routers/contentstack/destinationCountryPage/utils.ts +++ b/server/routers/contentstack/destinationCountryPage/utils.ts @@ -1,5 +1,6 @@ import { env } from "@/env/server" import { GetDestinationCityListData } from "@/lib/graphql/Query/DestinationCityPage/DestinationCityListData.graphql" +import { GetCountryPageUrls } from "@/lib/graphql/Query/DestinationCountryPage/DestinationCountryPageUrl.graphql" import { request } from "@/lib/graphql/request" import { toApiLang } from "@/server/utils" @@ -7,10 +8,14 @@ import { generateTag, generateTagsFromSystem } from "@/utils/generateTag" import { getCitiesByCountry } from "../../hotels/utils" import { destinationCityListDataSchema } from "../destinationCityPage/output" +import { countryPageUrlsSchema } from "./output" import { getCityListDataCounter, getCityListDataFailCounter, getCityListDataSuccessCounter, + getCountryPageUrlsCounter, + getCountryPageUrlsFailCounter, + getCountryPageUrlsSuccessCounter, } from "./telemetry" import { ApiCountry, type Country } from "@/types/enums/country" @@ -18,7 +23,10 @@ import { DestinationCountryPageEnum } from "@/types/enums/destinationCountryPage import type { RequestOptionsWithOutBody } from "@/types/fetch" import type { System } from "@/types/requests/system" import type { GetDestinationCityListDataResponse } from "@/types/trpc/routers/contentstack/destinationCityPage" -import type { DestinationCountryPageRefs } from "@/types/trpc/routers/contentstack/destinationCountryPage" +import type { + DestinationCountryPageRefs, + GetCountryPageUrlsData, +} from "@/types/trpc/routers/contentstack/destinationCountryPage" import type { Lang } from "@/constants/languages" export function generatePageTags( @@ -178,3 +186,66 @@ export async function getCityPages( .flat() .filter((city): city is NonNullable => !!city) } + +export async function getCountryPageUrls(lang: Lang) { + getCountryPageUrlsCounter.add(1, { lang }) + console.info( + "contentstack.countryPageUrls start", + JSON.stringify({ query: { lang } }) + ) + + const tag = `${lang}:country_page_urls` + const response = await request( + GetCountryPageUrls, + { + locale: lang, + }, + { + cache: "force-cache", + next: { + tags: [tag], + }, + } + ) + + if (!response.data) { + getCountryPageUrlsFailCounter.add(1, { + lang, + error_type: "not_found", + error: `Country pages not found for lang: ${lang}`, + }) + console.error( + "contentstack.countryPageUrls not found error", + JSON.stringify({ query: { lang } }) + ) + return [] + } + + const validatedCountryPageUrls = countryPageUrlsSchema.safeParse( + response.data + ) + + if (!validatedCountryPageUrls.success) { + getCountryPageUrlsFailCounter.add(1, { + lang, + error_type: "validation_error", + error: JSON.stringify(validatedCountryPageUrls.error), + }) + console.error( + "contentstack.countryPageUrls validation error", + JSON.stringify({ + query: { lang }, + error: validatedCountryPageUrls.error, + }) + ) + return [] + } + + getCountryPageUrlsSuccessCounter.add(1, { lang }) + console.info( + "contentstack.countryPageUrls success", + JSON.stringify({ query: { lang } }) + ) + + return validatedCountryPageUrls.data +} diff --git a/server/routers/contentstack/destinationOverviewPage/output.ts b/server/routers/contentstack/destinationOverviewPage/output.ts index 7defa4017..88a883843 100644 --- a/server/routers/contentstack/destinationOverviewPage/output.ts +++ b/server/routers/contentstack/destinationOverviewPage/output.ts @@ -2,8 +2,6 @@ import { z } from "zod" import { discriminatedUnionArray } from "@/lib/discriminatedUnion" -import { removeMultipleSlashes } from "@/utils/url" - import { cardGalleryRefsSchema, cardGallerySchema, @@ -40,27 +38,6 @@ export const destinationOverviewPageSchema = z.object({ }), }) -export const countryPageUrlSchema = z - .object({ - all_destination_country_page: z.object({ - items: z.array( - z - .object({ - url: z.string(), - system: systemSchema, - }) - .transform((data) => { - return { - url: removeMultipleSlashes(`/${data.system.locale}/${data.url}`), - } - }) - ), - }), - }) - .transform( - ({ all_destination_country_page }) => all_destination_country_page.items[0] - ) - /** REFS */ const destinationOverviewPageCardGalleryRef = z .object({ diff --git a/server/routers/contentstack/destinationOverviewPage/query.ts b/server/routers/contentstack/destinationOverviewPage/query.ts index 809d80b96..802c45191 100644 --- a/server/routers/contentstack/destinationOverviewPage/query.ts +++ b/server/routers/contentstack/destinationOverviewPage/query.ts @@ -19,7 +19,8 @@ import { getCountries, getHotelIdsByCityId, } from "../../hotels/utils" -import { getCityListDataByCityIdentifier } from "../destinationCountryPage/utils" +import { getCityPageUrls } from "../destinationCityPage/utils" +import { getCountryPageUrls } from "../destinationCountryPage/utils" import { destinationOverviewPageRefsSchema, destinationOverviewPageSchema, @@ -32,7 +33,6 @@ import { getDestinationOverviewPageRefsSuccessCounter, getDestinationOverviewPageSuccessCounter, } from "./telemetry" -import { getCountryPageUrl } from "./utils" import type { DestinationsData } from "@/types/components/destinationOverviewPage/destinationsList/destinationsData" import { @@ -220,8 +220,8 @@ export const destinationOverviewPageQueryRouter = router({ revalidate: env.CACHE_TIME_HOTELS, }, } - const countries = await getCountries(options, params, ctx.lang) + const countryPages = await getCountryPageUrls(ctx.lang) if (!countries) { return null @@ -237,6 +237,8 @@ export const destinationOverviewPageQueryRouter = router({ true ) + const cityPages = await getCityPageUrls(ctx.lang) + const destinations: DestinationsData = await Promise.all( Object.entries(citiesByCountry).map(async ([country, cities]) => { const citiesWithHotelCount = await Promise.all( @@ -252,29 +254,27 @@ export const destinationOverviewPageQueryRouter = router({ hotelIdsParams ) - let cityUrl - if (city.cityIdentifier) { - cityUrl = await getCityListDataByCityIdentifier( - ctx.lang, - city.cityIdentifier - ) - } + const cityPage = cityPages.find( + (cityPage) => cityPage.city === city.cityIdentifier + ) return { id: city.id, name: city.name, hotelIds: hotels, hotelCount: hotels?.length ?? 0, - url: cityUrl?.url, + url: cityPage?.url, } }) ) - const countryUrl = await getCountryPageUrl(ctx.lang, country) + const countryPage = countryPages.find( + (countryPage) => countryPage.country === country + ) return { country, - countryUrl: countryUrl?.url, + countryUrl: countryPage?.url, numberOfHotels: citiesWithHotelCount.reduce( (acc, city) => acc + city.hotelCount, 0 diff --git a/server/routers/contentstack/destinationOverviewPage/telemetry.ts b/server/routers/contentstack/destinationOverviewPage/telemetry.ts index aef79e497..84d93764a 100644 --- a/server/routers/contentstack/destinationOverviewPage/telemetry.ts +++ b/server/routers/contentstack/destinationOverviewPage/telemetry.ts @@ -21,15 +21,3 @@ export const getDestinationOverviewPageSuccessCounter = meter.createCounter( export const getDestinationOverviewPageFailCounter = meter.createCounter( "trpc.contentstack.destinationOverviewPage.get-fail" ) - -export const getCountryPageUrlCounter = meter.createCounter( - "trpc.contentstack.getCountryPageUrl" -) - -export const getCountryPageUrlSuccessCounter = meter.createCounter( - "trpc.contentstack.getCountryPageUrl-success" -) - -export const getCountryPageUrlFailCounter = meter.createCounter( - "trpc.contentstack.getCountryPageUrl-fail" -) diff --git a/server/routers/contentstack/destinationOverviewPage/utils.ts b/server/routers/contentstack/destinationOverviewPage/utils.ts deleted file mode 100644 index 2500d4ebe..000000000 --- a/server/routers/contentstack/destinationOverviewPage/utils.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { GetCountryPageUrl } from "@/lib/graphql/Query/DestinationOverviewPage/DestinationOverviewPage.graphql" -import { request } from "@/lib/graphql/request" - -import { countryPageUrlSchema } from "./output" -import { - getCountryPageUrlCounter, - getCountryPageUrlFailCounter, - getCountryPageUrlSuccessCounter, -} from "./telemetry" - -import type { GetCountryPageUrlData } from "@/types/trpc/routers/contentstack/destinationOverviewPage" -import type { Lang } from "@/constants/languages" - -export async function getCountryPageUrl(lang: Lang, country: string) { - getCountryPageUrlCounter.add(1, { lang, country }) - console.info( - "contentstack.countryPageUrl start", - JSON.stringify({ query: { lang, country } }) - ) - - const tag = `${lang}:country_page_url:${country}` - const response = await request( - GetCountryPageUrl, - { - locale: lang, - country, - }, - { - cache: "force-cache", - next: { - tags: [tag], - }, - } - ) - - if (!response.data) { - getCountryPageUrlFailCounter.add(1, { - lang, - country, - error_type: "not_found", - error: `Country page not found for country: ${country}`, - }) - console.error( - "contentstack.countryPageUrl not found error", - JSON.stringify({ query: { lang, country } }) - ) - return null - } - - const validatedCountryPageUrl = countryPageUrlSchema.safeParse(response.data) - - if (!validatedCountryPageUrl.success) { - getCountryPageUrlFailCounter.add(1, { - lang, - country, - error_type: "validation_error", - error: JSON.stringify(validatedCountryPageUrl.error), - }) - console.error( - "contentstack.countryPageUrl validation error", - JSON.stringify({ - query: { lang, country }, - error: validatedCountryPageUrl.error, - }) - ) - return null - } - - getCountryPageUrlSuccessCounter.add(1, { lang, country }) - console.info( - "contentstack.countryPageUrl success", - JSON.stringify({ query: { lang, country } }) - ) - - return validatedCountryPageUrl.data -} diff --git a/server/routers/contentstack/hotelPage/output.ts b/server/routers/contentstack/hotelPage/output.ts index 9ba8f7d02..3e4b0bbe5 100644 --- a/server/routers/contentstack/hotelPage/output.ts +++ b/server/routers/contentstack/hotelPage/output.ts @@ -116,25 +116,23 @@ export const hotelPageRefsSchema = z.object({ }), }) -export const hotelPageUrlSchema = z +export const hotelPageUrlsSchema = z .object({ all_hotel_page: z.object({ - items: z - .array( - z.object({ + items: z.array( + z + .object({ url: z.string(), + hotel_page_id: z.string(), system: systemSchema, }) - ) - .max(1), + .transform((data) => { + return { + url: removeMultipleSlashes(`/${data.system.locale}/${data.url}`), + hotelId: data.hotel_page_id, + } + }) + ), }), }) - .transform((data) => { - const page = data.all_hotel_page.items[0] - if (!page) { - return null - } - - const lang = page.system.locale - return removeMultipleSlashes(`/${lang}/${page.url}`) - }) + .transform(({ all_hotel_page }) => all_hotel_page.items) diff --git a/server/routers/contentstack/hotelPage/telemetry.ts b/server/routers/contentstack/hotelPage/telemetry.ts index 81f967a6b..83117d350 100644 --- a/server/routers/contentstack/hotelPage/telemetry.ts +++ b/server/routers/contentstack/hotelPage/telemetry.ts @@ -22,12 +22,12 @@ export const getHotelPageFailCounter = meter.createCounter( "trpc.contentstack.hotelPage.get-fail" ) -export const getHotelPageUrlCounter = meter.createCounter( - "trpc.contentstack.hotelPageUrl.get" +export const getHotelPageUrlsCounter = meter.createCounter( + "trpc.contentstack.hotelPageUrls.get" ) -export const getHotelPageUrlSuccessCounter = meter.createCounter( - "trpc.contentstack.hotelPageUrl.get-success" +export const getHotelPageUrlsSuccessCounter = meter.createCounter( + "trpc.contentstack.hotelPageUrls.get-success" ) -export const getHotelPageUrlFailCounter = meter.createCounter( - "trpc.contentstack.hotelPageUrl.get-fail" +export const getHotelPageUrlsFailCounter = meter.createCounter( + "trpc.contentstack.hotelPageUrls.get-fail" ) diff --git a/server/routers/contentstack/hotelPage/utils.ts b/server/routers/contentstack/hotelPage/utils.ts index 4090d933f..605eade32 100644 --- a/server/routers/contentstack/hotelPage/utils.ts +++ b/server/routers/contentstack/hotelPage/utils.ts @@ -1,29 +1,25 @@ import { GetHotelPageRefs } from "@/lib/graphql/Query/HotelPage/HotelPage.graphql" -import { GetHotelPageUrl } from "@/lib/graphql/Query/HotelPage/HotelPageUrl.graphql" +import { GetHotelPageUrls } from "@/lib/graphql/Query/HotelPage/HotelPageUrl.graphql" import { request } from "@/lib/graphql/request" import { notFound } from "@/server/errors/trpc" -import { - generateHotelUrlTag, - generateTag, - generateTagsFromSystem, -} from "@/utils/generateTag" +import { generateTag, generateTagsFromSystem } from "@/utils/generateTag" -import { hotelPageRefsSchema, hotelPageUrlSchema } from "./output" +import { hotelPageRefsSchema, hotelPageUrlsSchema } from "./output" import { getHotelPageRefsCounter, getHotelPageRefsFailCounter, getHotelPageRefsSuccessCounter, - getHotelPageUrlCounter, - getHotelPageUrlFailCounter, - getHotelPageUrlSuccessCounter, + getHotelPageUrlsCounter, + getHotelPageUrlsFailCounter, + getHotelPageUrlsSuccessCounter, } from "./telemetry" import { HotelPageEnum } from "@/types/enums/hotelPage" import type { System } from "@/types/requests/system" import type { GetHotelPageRefsSchema, - GetHotelPageUrlData, + GetHotelPageUrlsData, HotelPageRefs, } from "@/types/trpc/routers/contentstack/hotelPage" import type { Lang } from "@/constants/languages" @@ -136,63 +132,61 @@ export function getConnections({ hotel_page }: HotelPageRefs) { return connections } -export async function getHotelPageUrl(lang: Lang, hotelId: string) { - getHotelPageUrlCounter.add(1, { lang, hotelId }) +export async function getHotelPageUrls(lang: Lang) { + getHotelPageUrlsCounter.add(1, { lang }) console.info( - "contentstack.hotelPageUrl start", - JSON.stringify({ query: { lang, hotelId } }) + "contentstack.hotelPageUrls start", + JSON.stringify({ query: { lang } }) ) - const response = await request( - GetHotelPageUrl, + const tags = [`${lang}:hotel_page_urls`] + const response = await request( + GetHotelPageUrls, { locale: lang, - hotelId, }, { cache: "force-cache", next: { - tags: [generateHotelUrlTag(lang, hotelId)], + tags, }, } ) if (!response.data) { - getHotelPageUrlFailCounter.add(1, { + getHotelPageUrlsFailCounter.add(1, { lang, - hotelId, error_type: "not_found", - error: `Hotel page not found for hotelId: ${hotelId}`, + error: `Hotel pages not found for lang: ${lang}`, }) console.error( - "contentstack.hotelPageUrl not found error", - JSON.stringify({ query: { lang, hotelId } }) + "contentstack.hotelPageUrls not found error", + JSON.stringify({ query: { lang } }) ) - return null + return [] } - const validatedHotelPageUrl = hotelPageUrlSchema.safeParse(response.data) + const validatedHotelPageUrls = hotelPageUrlsSchema.safeParse(response.data) - if (!validatedHotelPageUrl.success) { - getHotelPageUrlFailCounter.add(1, { + if (!validatedHotelPageUrls.success) { + getHotelPageUrlsFailCounter.add(1, { lang, - hotelId, error_type: "validation_error", - error: JSON.stringify(validatedHotelPageUrl.error), + error: JSON.stringify(validatedHotelPageUrls.error), }) console.error( - "contentstack.hotelPageUrl validation error", + "contentstack.hotelPageUrls validation error", JSON.stringify({ - query: { lang, hotelId }, - error: validatedHotelPageUrl.error, + query: { lang }, + error: validatedHotelPageUrls.error, }) ) - return null + return [] } - getHotelPageUrlSuccessCounter.add(1, { lang, hotelId }) + getHotelPageUrlsSuccessCounter.add(1, { lang }) console.info( "contentstack.hotelPageUrl success", - JSON.stringify({ query: { lang, hotelId } }) + JSON.stringify({ query: { lang } }) ) - return validatedHotelPageUrl.data + return validatedHotelPageUrls.data } diff --git a/server/routers/hotels/query.ts b/server/routers/hotels/query.ts index 3dbc1fb9e..d8bda5f6c 100644 --- a/server/routers/hotels/query.ts +++ b/server/routers/hotels/query.ts @@ -17,7 +17,7 @@ import { toApiLang } from "@/server/utils" import { generateChildrenString } from "@/components/HotelReservation/utils" import { cache } from "@/utils/cache" -import { getHotelPageUrl } from "../contentstack/hotelPage/utils" +import { getHotelPageUrls } from "../contentstack/hotelPage/utils" import { getVerifiedUser, parsedUser } from "../user/query" import { additionalDataSchema } from "./schemas/hotel/include/additionalData" import { meetingRoomsSchema } from "./schemas/meetingRoom" @@ -1100,21 +1100,21 @@ export const hotelQueryRouter = router({ ) return [] } - + const hotelPages = await getHotelPageUrls(language) const hotels = await Promise.all( hotelsToFetch.map(async (hotelId) => { - const [hotelData, url] = await Promise.all([ - getHotel( - { hotelId, isCardOnlyPayment: false, language }, - ctx.serviceToken - ), - getHotelPageUrl(language, hotelId), - ]) + const hotelData = await getHotel( + { hotelId, isCardOnlyPayment: false, language }, + ctx.serviceToken + ) + const hotelPage = hotelPages.find( + (page) => page.hotelId === hotelId + ) return hotelData ? { ...hotelData, - url, + url: hotelPage?.url ?? null, } : null }) diff --git a/server/routers/hotels/utils.ts b/server/routers/hotels/utils.ts index 6ed8e1709..947b4f2eb 100644 --- a/server/routers/hotels/utils.ts +++ b/server/routers/hotels/utils.ts @@ -6,7 +6,7 @@ import { env } from "@/env/server" import * as api from "@/lib/api" import { toApiLang } from "@/server/utils" -import { getHotelPageUrl } from "../contentstack/hotelPage/utils" +import { getHotelPageUrls } from "../contentstack/hotelPage/utils" import { metrics } from "./metrics" import { citiesByCountrySchema, @@ -484,17 +484,15 @@ export async function getHotelsByHotelIds( lang: Lang, serviceToken: string ) { + const hotelPages = await getHotelPageUrls(lang) const hotels = await Promise.all( hotelIds.map(async (hotelId) => { - const [hotelData, url] = await Promise.all([ - getHotel( - { hotelId, language: lang, isCardOnlyPayment: false }, - serviceToken - ), - getHotelPageUrl(lang, hotelId), - ]) - - return hotelData ? { ...hotelData, url } : null + const hotelData = await getHotel( + { hotelId, language: lang, isCardOnlyPayment: false }, + serviceToken + ) + const hotelPage = hotelPages.find((page) => page.hotelId === hotelId) + return hotelData ? { ...hotelData, url: hotelPage?.url ?? null } : null }) ) diff --git a/types/trpc/routers/contentstack/destinationCityPage.ts b/types/trpc/routers/contentstack/destinationCityPage.ts index 9b1e85c59..11ed23c25 100644 --- a/types/trpc/routers/contentstack/destinationCityPage.ts +++ b/types/trpc/routers/contentstack/destinationCityPage.ts @@ -2,6 +2,7 @@ import type { z } from "zod" import type { blocksSchema, + cityPageUrlsSchema, destinationCityListDataSchema, destinationCityPageRefsSchema, destinationCityPageSchema, @@ -18,6 +19,10 @@ export interface GetDestinationCityListDataResponse export interface DestinationCityListData extends z.output {} +export interface GetCityPageUrlsData + extends z.input {} + +export interface CityPageUrlsData extends z.output {} export interface DestinationCityListItem extends DestinationCityListData { cityName: string diff --git a/types/trpc/routers/contentstack/destinationCountryPage.ts b/types/trpc/routers/contentstack/destinationCountryPage.ts index 8e3f45162..975b452e3 100644 --- a/types/trpc/routers/contentstack/destinationCountryPage.ts +++ b/types/trpc/routers/contentstack/destinationCountryPage.ts @@ -2,6 +2,7 @@ import type { z } from "zod" import type { blocksSchema, + countryPageUrlsSchema, destinationCountryPageRefsSchema, destinationCountryPageSchema, } from "@/server/routers/contentstack/destinationCountryPage/output" @@ -19,3 +20,6 @@ export interface GetDestinationCountryPageRefsSchema export interface DestinationCountryPageRefs extends z.output {} + +export interface GetCountryPageUrlsData + extends z.input {} diff --git a/types/trpc/routers/contentstack/destinationOverviewPage.ts b/types/trpc/routers/contentstack/destinationOverviewPage.ts index 6efe0b581..3c94c3cb7 100644 --- a/types/trpc/routers/contentstack/destinationOverviewPage.ts +++ b/types/trpc/routers/contentstack/destinationOverviewPage.ts @@ -2,7 +2,6 @@ import type { z } from "zod" import type { blocksSchema, - countryPageUrlSchema, destinationOverviewPageRefsSchema, destinationOverviewPageSchema, } from "@/server/routers/contentstack/destinationOverviewPage/output" @@ -18,7 +17,4 @@ export interface GetDestinationOverviewPageRefsSchema export interface DestinationOverviewPageRefs extends z.output {} -export interface GetCountryPageUrlData - extends z.input {} - export type Block = z.output diff --git a/types/trpc/routers/contentstack/hotelPage.ts b/types/trpc/routers/contentstack/hotelPage.ts index 7a983d419..fede08e2d 100644 --- a/types/trpc/routers/contentstack/hotelPage.ts +++ b/types/trpc/routers/contentstack/hotelPage.ts @@ -4,7 +4,7 @@ import type { contentBlock, hotelPageRefsSchema, hotelPageSchema, - hotelPageUrlSchema, + hotelPageUrlsSchema, } from "@/server/routers/contentstack/hotelPage/output" import type { activitiesCardSchema } from "@/server/routers/contentstack/schemas/blocks/activitiesCard" import type { spaPageSchema } from "@/server/routers/contentstack/schemas/blocks/spaPage" @@ -22,7 +22,6 @@ export interface GetHotelPageRefsSchema extends z.input {} export interface HotelPageRefs extends z.output {} - -export interface GetHotelPageUrlData - extends z.input {} -export type HotelPageUrl = z.output +export interface GetHotelPageUrlsData + extends z.input {} +export type HotelPageUrls = z.output