import { getCacheClient } from "@scandic-hotels/common/dataCache" import { createCounter } from "@scandic-hotels/common/telemetry" import { env } from "../../../../env/server" import * as api from "../../../api" import { badRequestError } from "../../../errors" import { toApiLang } from "../../../utils" import { hotelsAvailabilitySchema } from "../output" import type { Lang } from "@scandic-hotels/common/constants/language" import type { HotelsByHotelIdsAvailabilityInputSchema } from "../availability/hotelsByHotelIds" export async function getHotelsAvailabilityByHotelIds({ input, lang, userToken, serviceToken, userPoints = 0, }: { input: HotelsByHotelIdsAvailabilityInputSchema lang: Lang userToken: string | null | undefined serviceToken: string userPoints: number | undefined }) { const { hotelIds, roomStayStartDate, roomStayEndDate, adults, children, bookingCode, redemption, } = input const apiLang = toApiLang(lang) const params = new URLSearchParams([ ["roomStayStartDate", roomStayStartDate], ["roomStayEndDate", roomStayEndDate], ["adults", adults.toString()], ["children", children ?? ""], ["bookingCode", bookingCode], ["isRedemption", redemption.toString()], ["language", apiLang], ]) const getHotelsAvailabilityByHotelIdsCounter = createCounter( "hotel.getHotelsAvailabilityByHotelIds" ) const metricsGetHotelsAvailabilityByHotelIds = getHotelsAvailabilityByHotelIdsCounter.init({ apiLang, hotelIds, roomStayStartDate, roomStayEndDate, adults, children, bookingCode, redemption, }) metricsGetHotelsAvailabilityByHotelIds.start() const cacheClient = await getCacheClient() const result = cacheClient.cacheOrGet( `${apiLang}:hotels:availability:${hotelIds.join(",")}:${roomStayStartDate}:${roomStayEndDate}:${adults}:${children}:${bookingCode}`, async () => { /** * Since API expects the params appended and not just * a comma separated string we need to initialize the * SearchParams with a sequence of pairs * (hotelIds=810&hotelIds=879&hotelIds=222 etc.) **/ hotelIds.forEach((hotelId) => params.append("hotelIds", hotelId.toString()) ) const apiResponse = await api.get( api.endpoints.v1.Availability.hotels(), { headers: { Authorization: `Bearer ${userToken ?? serviceToken}`, }, }, params ) if (!apiResponse.ok) { await metricsGetHotelsAvailabilityByHotelIds.httpError(apiResponse) throw new Error("Failed to fetch hotels availability by hotelIds") } const apiJson = await apiResponse.json() const validateAvailabilityData = hotelsAvailabilitySchema.safeParse(apiJson) if (!validateAvailabilityData.success) { metricsGetHotelsAvailabilityByHotelIds.validationError( validateAvailabilityData.error ) throw badRequestError() } if (redemption) { validateAvailabilityData.data.data.forEach((data) => { data.attributes.productType?.redemptions?.forEach((r) => { r.hasEnoughPoints = userPoints >= r.localPrice.pointsPerStay }) }) } return { availability: validateAvailabilityData.data.data.flatMap( (hotels) => hotels.attributes ), } }, redemption ? "no cache" : env.CACHE_TIME_CITY_SEARCH ) metricsGetHotelsAvailabilityByHotelIds.success() return result }