Merged in feat/sw-2863-move-contentstack-router-to-trpc-package (pull request #2389)
feat(SW-2863): Move contentstack router to trpc package * Add exports to packages and lint rule to prevent relative imports * Add env to trpc package * Add eslint to trpc package * Apply lint rules * Use direct imports from trpc package * Add lint-staged config to trpc * Move lang enum to common * Restructure trpc package folder structure * WIP first step * update internal imports in trpc * Fix most errors in scandic-web Just 100 left... * Move Props type out of trpc * Fix CategorizedFilters types * Move more schemas in hotel router * Fix deps * fix getNonContentstackUrls * Fix import error * Fix entry error handling * Fix generateMetadata metrics * Fix alertType enum * Fix duplicated types * lint:fix * Merge branch 'master' into feat/sw-2863-move-contentstack-router-to-trpc-package * Fix broken imports * Merge branch 'master' into feat/sw-2863-move-contentstack-router-to-trpc-package Approved-by: Linus Flood
This commit is contained in:
@@ -1,49 +1,37 @@
|
||||
import deepmerge from "deepmerge"
|
||||
import stringify from "json-stable-stringify-without-jsonify"
|
||||
|
||||
import { Lang } from "@scandic-hotels/common/constants/language"
|
||||
import { getCacheClient } from "@scandic-hotels/common/dataCache"
|
||||
import { createCounter } from "@scandic-hotels/common/telemetry"
|
||||
import * as api from "@scandic-hotels/trpc/api"
|
||||
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||
import { AvailabilityEnum } from "@scandic-hotels/trpc/enums/selectHotel"
|
||||
import { badRequestError } from "@scandic-hotels/trpc/errors"
|
||||
import { type RoomFeaturesInput } from "@scandic-hotels/trpc/routers/hotels/input"
|
||||
import {
|
||||
hotelsAvailabilitySchema,
|
||||
packagesSchema,
|
||||
roomFeaturesSchema,
|
||||
roomsAvailabilitySchema,
|
||||
} from "@scandic-hotels/trpc/routers/hotels/output"
|
||||
import { toApiLang } from "@scandic-hotels/trpc/utils"
|
||||
import { sortRoomConfigs } from "@scandic-hotels/trpc/utils/sortRoomConfigs"
|
||||
|
||||
import { BookingErrorCodeEnum, REDEMPTION } from "@/constants/booking"
|
||||
import { selectRate } from "@/constants/routes/hotelReservation"
|
||||
import { env } from "@/env/server"
|
||||
import * as api from "@/lib/api"
|
||||
import { toApiLang } from "@/server/utils"
|
||||
|
||||
import { generateChildrenString } from "@/components/HotelReservation/utils"
|
||||
import { cache } from "@/utils/cache"
|
||||
import { chunk } from "@/utils/chunk"
|
||||
import { sortRoomConfigs } from "@/utils/sort"
|
||||
|
||||
import { getHotelPageUrls } from "../contentstack/hotelPage/utils"
|
||||
import { type RoomFeaturesInput } from "./input"
|
||||
import {
|
||||
type Cities,
|
||||
citiesByCountrySchema,
|
||||
citiesSchema,
|
||||
countriesSchema,
|
||||
getHotelIdsSchema,
|
||||
hotelsAvailabilitySchema,
|
||||
hotelSchema,
|
||||
locationsSchema,
|
||||
packagesSchema,
|
||||
roomFeaturesSchema,
|
||||
roomsAvailabilitySchema,
|
||||
} from "./output"
|
||||
|
||||
import type { z } from "zod"
|
||||
import type { Room as RoomCategory } from "@scandic-hotels/trpc/types/hotel"
|
||||
import type {
|
||||
Product,
|
||||
Products,
|
||||
RateDefinition,
|
||||
RedemptionsProduct,
|
||||
RoomConfiguration,
|
||||
} from "@scandic-hotels/trpc/types/roomAvailability"
|
||||
|
||||
import type { BedTypeSelection } from "@/types/components/hotelReservation/enterDetails/bedType"
|
||||
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
||||
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||
import { HotelTypeEnum } from "@/types/enums/hotelType"
|
||||
import { PointOfInterestGroupEnum } from "@/types/enums/pointOfInterest"
|
||||
import type {
|
||||
DestinationPagesHotelData,
|
||||
Room as RoomCategory,
|
||||
} from "@/types/hotel"
|
||||
import type { PackagesOutput } from "@/types/requests/packages"
|
||||
import type {
|
||||
HotelsAvailabilityInputSchema,
|
||||
@@ -52,529 +40,8 @@ import type {
|
||||
RoomsAvailabilityInputRoom,
|
||||
RoomsAvailabilityOutputSchema,
|
||||
} from "@/types/trpc/routers/hotel/availability"
|
||||
import type { HotelInput } from "@/types/trpc/routers/hotel/hotel"
|
||||
import type {
|
||||
CitiesGroupedByCountry,
|
||||
CityLocation,
|
||||
} from "@/types/trpc/routers/hotel/locations"
|
||||
import type {
|
||||
Product,
|
||||
Products,
|
||||
RateDefinition,
|
||||
RedemptionsProduct,
|
||||
RoomConfiguration,
|
||||
} from "@/types/trpc/routers/hotel/roomAvailability"
|
||||
import type { Endpoint } from "@/lib/api/endpoints"
|
||||
|
||||
export function getPoiGroupByCategoryName(category: string | undefined) {
|
||||
if (!category) return PointOfInterestGroupEnum.LOCATION
|
||||
switch (category) {
|
||||
case "Airport":
|
||||
case "Bus terminal":
|
||||
case "Transportations":
|
||||
return PointOfInterestGroupEnum.PUBLIC_TRANSPORT
|
||||
case "Amusement park":
|
||||
case "Museum":
|
||||
case "Sports":
|
||||
case "Theatre":
|
||||
case "Tourist":
|
||||
case "Zoo":
|
||||
return PointOfInterestGroupEnum.ATTRACTIONS
|
||||
case "Nearby companies":
|
||||
case "Fair":
|
||||
return PointOfInterestGroupEnum.BUSINESS
|
||||
case "Parking / Garage":
|
||||
return PointOfInterestGroupEnum.PARKING
|
||||
case "Shopping":
|
||||
case "Restaurant":
|
||||
return PointOfInterestGroupEnum.SHOPPING_DINING
|
||||
case "Hospital":
|
||||
default:
|
||||
return PointOfInterestGroupEnum.LOCATION
|
||||
}
|
||||
}
|
||||
|
||||
export const locationsAffix = "locations"
|
||||
|
||||
export const TWENTYFOUR_HOURS = 60 * 60 * 24
|
||||
export async function getCity({
|
||||
cityUrl,
|
||||
serviceToken,
|
||||
}: {
|
||||
cityUrl: string
|
||||
serviceToken: string
|
||||
}): Promise<Cities> {
|
||||
const cacheClient = await getCacheClient()
|
||||
return await cacheClient.cacheOrGet(
|
||||
cityUrl,
|
||||
async () => {
|
||||
const url = new URL(cityUrl)
|
||||
const cityResponse = await api.get(
|
||||
url.pathname as Endpoint,
|
||||
{ headers: { Authorization: `Bearer ${serviceToken}` } },
|
||||
url.searchParams
|
||||
)
|
||||
|
||||
if (!cityResponse.ok) {
|
||||
return null
|
||||
}
|
||||
|
||||
const cityJson = await cityResponse.json()
|
||||
const city = citiesSchema.safeParse(cityJson)
|
||||
if (!city.success) {
|
||||
console.info(`Validation of city failed`)
|
||||
console.info(`cityUrl: ${cityUrl}`)
|
||||
console.error(city.error)
|
||||
return null
|
||||
}
|
||||
|
||||
return city.data
|
||||
},
|
||||
"1d"
|
||||
)
|
||||
}
|
||||
|
||||
export async function getCountries({
|
||||
lang,
|
||||
serviceToken,
|
||||
warmup = false,
|
||||
}: {
|
||||
lang: Lang
|
||||
serviceToken: string
|
||||
warmup?: boolean
|
||||
}) {
|
||||
const cacheClient = await getCacheClient()
|
||||
return await cacheClient.cacheOrGet(
|
||||
`${lang}:${locationsAffix}:countries`,
|
||||
async () => {
|
||||
const params = new URLSearchParams({
|
||||
language: toApiLang(lang),
|
||||
})
|
||||
|
||||
const countryResponse = await api.get(
|
||||
api.endpoints.v1.Hotel.countries,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
|
||||
if (!countryResponse.ok) {
|
||||
throw new Error("Unable to fetch countries")
|
||||
}
|
||||
|
||||
const countriesJson = await countryResponse.json()
|
||||
const countries = countriesSchema.safeParse(countriesJson)
|
||||
if (!countries.success) {
|
||||
console.info(`Validation for countries failed`)
|
||||
console.error(countries.error)
|
||||
return null
|
||||
}
|
||||
|
||||
return countries.data
|
||||
},
|
||||
"1d",
|
||||
{
|
||||
cacheStrategy: warmup ? "fetch-then-cache" : "cache-first",
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export async function getCitiesByCountry({
|
||||
countries,
|
||||
lang,
|
||||
affix = locationsAffix,
|
||||
serviceToken,
|
||||
}: {
|
||||
countries: string[]
|
||||
lang: Lang
|
||||
affix?: string
|
||||
serviceToken: string
|
||||
}): Promise<CitiesGroupedByCountry> {
|
||||
const cacheClient = await getCacheClient()
|
||||
const allCitiesByCountries = await Promise.all(
|
||||
countries.map(async (country) => {
|
||||
return cacheClient.cacheOrGet(
|
||||
`${lang}:${affix}:cities-by-country:${country}`,
|
||||
async () => {
|
||||
const params = new URLSearchParams({
|
||||
language: toApiLang(lang),
|
||||
})
|
||||
const countryResponse = await api.get(
|
||||
api.endpoints.v1.Hotel.Cities.country(country),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
|
||||
if (!countryResponse.ok) {
|
||||
throw new Error(`Unable to fetch cities by country ${country}`)
|
||||
}
|
||||
|
||||
const countryJson = await countryResponse.json()
|
||||
const citiesByCountry = citiesByCountrySchema.safeParse(countryJson)
|
||||
if (!citiesByCountry.success) {
|
||||
console.error(`Unable to parse cities by country ${country}`)
|
||||
console.error(citiesByCountry.error)
|
||||
throw new Error(`Unable to parse cities by country ${country}`)
|
||||
}
|
||||
return { ...citiesByCountry.data, country }
|
||||
},
|
||||
"1d"
|
||||
)
|
||||
})
|
||||
)
|
||||
|
||||
const filteredCitiesByCountries = allCitiesByCountries.map((country) => ({
|
||||
...country,
|
||||
data: country.data.filter((city) => city.isPublished),
|
||||
}))
|
||||
|
||||
const groupedCitiesByCountry: CitiesGroupedByCountry =
|
||||
filteredCitiesByCountries.reduce((acc, { country, data }) => {
|
||||
acc[country] = data
|
||||
return acc
|
||||
}, {} as CitiesGroupedByCountry)
|
||||
|
||||
return groupedCitiesByCountry
|
||||
}
|
||||
|
||||
export async function getLocations({
|
||||
lang,
|
||||
citiesByCountry,
|
||||
serviceToken,
|
||||
}: {
|
||||
lang: Lang
|
||||
citiesByCountry: CitiesGroupedByCountry | null
|
||||
serviceToken: string
|
||||
}) {
|
||||
const cacheClient = await getCacheClient()
|
||||
const countryKeys = Object.keys(citiesByCountry ?? {})
|
||||
let cacheKey = `${lang}:locations`
|
||||
|
||||
if (countryKeys.length) {
|
||||
cacheKey += `:${countryKeys.join(",")}`
|
||||
}
|
||||
|
||||
return await cacheClient.cacheOrGet(
|
||||
cacheKey.toLowerCase(),
|
||||
async () => {
|
||||
const params = new URLSearchParams({
|
||||
language: toApiLang(lang),
|
||||
})
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Hotel.locations,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
if (!apiResponse.ok) {
|
||||
if (apiResponse.status === 401) {
|
||||
throw new Error("unauthorized")
|
||||
} else if (apiResponse.status === 403) {
|
||||
throw new Error("forbidden")
|
||||
}
|
||||
throw new Error("downstream error")
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const verifiedLocations = locationsSchema.safeParse(apiJson)
|
||||
if (!verifiedLocations.success) {
|
||||
console.info(`Locations Verification Failed`)
|
||||
console.error(verifiedLocations.error)
|
||||
throw new Error("Unable to parse locations")
|
||||
}
|
||||
const chunkedLocations = chunk(verifiedLocations.data.data, 10)
|
||||
|
||||
let locations: z.infer<typeof locationsSchema>["data"] = []
|
||||
|
||||
for (const chunk of chunkedLocations) {
|
||||
const chunkLocations = await Promise.all(
|
||||
chunk.map(async (location) => {
|
||||
if (location.type === "cities") {
|
||||
if (citiesByCountry) {
|
||||
const country = Object.keys(citiesByCountry).find((country) =>
|
||||
citiesByCountry[country].find(
|
||||
(loc) => loc.name === location.name
|
||||
)
|
||||
)
|
||||
if (country) {
|
||||
return {
|
||||
...location,
|
||||
country,
|
||||
}
|
||||
} else {
|
||||
console.info(
|
||||
`Location cannot be found in any of the countries cities`
|
||||
)
|
||||
console.info(location)
|
||||
}
|
||||
}
|
||||
} else if (location.type === "hotels") {
|
||||
if (location.relationships.city?.url) {
|
||||
const city = await getCity({
|
||||
cityUrl: location.relationships.city.url,
|
||||
serviceToken,
|
||||
})
|
||||
if (city) {
|
||||
return deepmerge(location, {
|
||||
relationships: {
|
||||
city,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return location
|
||||
})
|
||||
)
|
||||
|
||||
locations.push(...chunkLocations)
|
||||
}
|
||||
|
||||
return locations
|
||||
},
|
||||
"1d"
|
||||
)
|
||||
}
|
||||
|
||||
export async function getHotelIdsByCityId({
|
||||
cityId,
|
||||
serviceToken,
|
||||
}: {
|
||||
cityId: string
|
||||
serviceToken: string
|
||||
}) {
|
||||
const getHotelIdsByCityIdCounter = createCounter(
|
||||
"hotel",
|
||||
"getHotelIdsByCityId"
|
||||
)
|
||||
const metricsGetHotelIdsByCityId = getHotelIdsByCityIdCounter.init({
|
||||
cityId,
|
||||
})
|
||||
|
||||
metricsGetHotelIdsByCityId.start()
|
||||
|
||||
const cacheClient = await getCacheClient()
|
||||
const result = await cacheClient.cacheOrGet(
|
||||
`${cityId}:hotelsByCityId`,
|
||||
async () => {
|
||||
const searchParams = new URLSearchParams({
|
||||
city: cityId,
|
||||
})
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Hotel.hotels,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`,
|
||||
},
|
||||
},
|
||||
searchParams
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
await metricsGetHotelIdsByCityId.httpError(apiResponse)
|
||||
throw new Error("Unable to fetch hotelIds by cityId")
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const validatedHotelIds = getHotelIdsSchema.safeParse(apiJson)
|
||||
if (!validatedHotelIds.success) {
|
||||
metricsGetHotelIdsByCityId.validationError(validatedHotelIds.error)
|
||||
throw new Error("Unable to parse data for hotelIds by cityId")
|
||||
}
|
||||
|
||||
return validatedHotelIds.data
|
||||
},
|
||||
env.CACHE_TIME_HOTELS
|
||||
)
|
||||
|
||||
metricsGetHotelIdsByCityId.success()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export async function getHotelIdsByCountry({
|
||||
country,
|
||||
serviceToken,
|
||||
}: {
|
||||
country: string
|
||||
serviceToken: string
|
||||
}) {
|
||||
const getHotelIdsByCountryCounter = createCounter(
|
||||
"hotel",
|
||||
"getHotelIdsByCountry"
|
||||
)
|
||||
|
||||
const metricsGetHotelIdsByCountry = getHotelIdsByCountryCounter.init({
|
||||
country,
|
||||
})
|
||||
|
||||
metricsGetHotelIdsByCountry.start()
|
||||
|
||||
const cacheClient = await getCacheClient()
|
||||
|
||||
const result = await cacheClient.cacheOrGet(
|
||||
`${country}:hotelsByCountry`,
|
||||
async () => {
|
||||
const hotelIdsParams = new URLSearchParams({
|
||||
country,
|
||||
})
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Hotel.hotels,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`,
|
||||
},
|
||||
},
|
||||
hotelIdsParams
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
await metricsGetHotelIdsByCountry.httpError(apiResponse)
|
||||
throw new Error("Unable to fetch hotelIds by country")
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const validatedHotelIds = getHotelIdsSchema.safeParse(apiJson)
|
||||
if (!validatedHotelIds.success) {
|
||||
metricsGetHotelIdsByCountry.validationError(validatedHotelIds.error)
|
||||
throw new Error("Unable to parse hotelIds by country")
|
||||
}
|
||||
|
||||
return validatedHotelIds.data
|
||||
},
|
||||
env.CACHE_TIME_HOTELS
|
||||
)
|
||||
|
||||
metricsGetHotelIdsByCountry.success()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export async function getHotelIdsByCityIdentifier(
|
||||
cityIdentifier: string,
|
||||
serviceToken: string
|
||||
) {
|
||||
const city = await getCityByCityIdentifier({
|
||||
cityIdentifier,
|
||||
lang: Lang.en,
|
||||
serviceToken,
|
||||
})
|
||||
|
||||
if (!city) {
|
||||
return []
|
||||
}
|
||||
|
||||
const hotelIds = await getHotelIdsByCityId({
|
||||
cityId: city.id,
|
||||
serviceToken,
|
||||
})
|
||||
return hotelIds
|
||||
}
|
||||
|
||||
export async function getCityByCityIdentifier({
|
||||
cityIdentifier,
|
||||
lang,
|
||||
serviceToken,
|
||||
}: {
|
||||
cityIdentifier: string
|
||||
lang: Lang
|
||||
serviceToken: string
|
||||
}) {
|
||||
const locations = await getLocations({
|
||||
lang,
|
||||
citiesByCountry: null,
|
||||
serviceToken,
|
||||
})
|
||||
if (!locations || "error" in locations) {
|
||||
return null
|
||||
}
|
||||
|
||||
const city = locations
|
||||
.filter((loc): loc is CityLocation => loc.type === "cities")
|
||||
.find((loc) => loc.cityIdentifier === cityIdentifier)
|
||||
|
||||
return city ?? null
|
||||
}
|
||||
|
||||
export async function getHotelsByHotelIds({
|
||||
hotelIds,
|
||||
lang,
|
||||
serviceToken,
|
||||
}: {
|
||||
hotelIds: string[]
|
||||
lang: Lang
|
||||
serviceToken: string
|
||||
}) {
|
||||
const cacheClient = await getCacheClient()
|
||||
const cacheKey = `${lang}:getHotelsByHotelIds:hotels:${hotelIds.sort().join(",")}`
|
||||
|
||||
return await cacheClient.cacheOrGet(
|
||||
cacheKey,
|
||||
async () => {
|
||||
const hotelPages = await getHotelPageUrls(lang)
|
||||
const chunkedHotelIds = chunk(hotelIds, 10)
|
||||
|
||||
const hotels: DestinationPagesHotelData[] = []
|
||||
for (const hotelIdChunk of chunkedHotelIds) {
|
||||
const chunkedHotels = await Promise.all(
|
||||
hotelIdChunk.map(async (hotelId) => {
|
||||
const hotelResponse = await getHotel(
|
||||
{ hotelId, language: lang, isCardOnlyPayment: false },
|
||||
serviceToken
|
||||
)
|
||||
|
||||
if (!hotelResponse) {
|
||||
throw new Error(`Hotel not found: ${hotelId}`)
|
||||
}
|
||||
|
||||
const hotelPage = hotelPages.find(
|
||||
(page) => page.hotelId === hotelId
|
||||
)
|
||||
const { hotel, cities } = hotelResponse
|
||||
const data: DestinationPagesHotelData = {
|
||||
hotel: {
|
||||
id: hotel.id,
|
||||
galleryImages: hotel.galleryImages,
|
||||
name: hotel.name,
|
||||
tripadvisor: hotel.ratings?.tripAdvisor?.rating,
|
||||
detailedFacilities: hotel.detailedFacilities || [],
|
||||
location: hotel.location,
|
||||
hotelType: hotel.hotelType,
|
||||
type: hotel.type,
|
||||
address: hotel.address,
|
||||
cityIdentifier: cities?.[0]?.cityIdentifier,
|
||||
hotelDescription: hotel.hotelContent?.texts.descriptions?.short,
|
||||
},
|
||||
url: hotelPage?.url ?? "",
|
||||
}
|
||||
|
||||
return data
|
||||
})
|
||||
)
|
||||
|
||||
hotels.push(...chunkedHotels)
|
||||
}
|
||||
return hotels.filter(
|
||||
(hotel): hotel is DestinationPagesHotelData => !!hotel
|
||||
)
|
||||
},
|
||||
"1d"
|
||||
)
|
||||
}
|
||||
|
||||
function findProduct(product: Products, rateDefinition: RateDefinition) {
|
||||
if ("corporateCheque" in product) {
|
||||
@@ -604,89 +71,6 @@ function findProduct(product: Products, rateDefinition: RateDefinition) {
|
||||
}
|
||||
}
|
||||
|
||||
export const getHotel = cache(
|
||||
async (input: HotelInput, serviceToken: string) => {
|
||||
const { hotelId, language, isCardOnlyPayment } = input
|
||||
|
||||
const getHotelCounter = createCounter("hotel", "getHotel")
|
||||
const metricsGetHotel = getHotelCounter.init({
|
||||
hotelId,
|
||||
language,
|
||||
isCardOnlyPayment,
|
||||
})
|
||||
|
||||
metricsGetHotel.start()
|
||||
|
||||
const cacheClient = await getCacheClient()
|
||||
|
||||
const result = await cacheClient.cacheOrGet(
|
||||
`${input.language}:hotel:${input.hotelId}:${!!input.isCardOnlyPayment}`,
|
||||
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
|
||||
* (include=City&include=NearbyHotels&include=Restaurants etc.)
|
||||
**/
|
||||
const params = new URLSearchParams([
|
||||
["include", "AdditionalData"],
|
||||
["include", "City"],
|
||||
["include", "NearbyHotels"],
|
||||
["include", "Restaurants"],
|
||||
["include", "RoomCategories"],
|
||||
["language", toApiLang(language)],
|
||||
])
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Hotel.Hotels.hotel(hotelId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
await metricsGetHotel.httpError(apiResponse)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const validateHotelData = hotelSchema.safeParse(apiJson)
|
||||
|
||||
if (!validateHotelData.success) {
|
||||
metricsGetHotel.validationError(validateHotelData.error)
|
||||
throw badRequestError()
|
||||
}
|
||||
|
||||
const hotelData = validateHotelData.data
|
||||
|
||||
if (isCardOnlyPayment) {
|
||||
hotelData.hotel.merchantInformationData.alternatePaymentOptions = []
|
||||
}
|
||||
|
||||
const gallery = hotelData.additionalData?.gallery
|
||||
if (gallery) {
|
||||
const smallerImages = gallery.smallerImages
|
||||
const hotelGalleryImages =
|
||||
hotelData.hotel.hotelType === HotelTypeEnum.Signature
|
||||
? smallerImages.slice(0, 10)
|
||||
: smallerImages.slice(0, 6)
|
||||
hotelData.hotel.galleryImages = hotelGalleryImages
|
||||
}
|
||||
|
||||
return hotelData
|
||||
},
|
||||
env.CACHE_TIME_HOTELS
|
||||
)
|
||||
|
||||
metricsGetHotel.success()
|
||||
|
||||
return result
|
||||
}
|
||||
)
|
||||
|
||||
export async function getHotelsAvailabilityByCity(
|
||||
input: HotelsAvailabilityInputSchema,
|
||||
apiLang: string,
|
||||
|
||||
Reference in New Issue
Block a user