Merged in feature/SW-3572-hotel-data-endpoint (pull request #3051)
SW-3572 API route for listing hotels per city or country * wip hotel data endpoint * Correct route params type * wip * skip static paths call * timeout when getting destinations take too long * call noStore when we get a timeout * add cache-control headers * . * . * . * wip * wip * wip * wip * add route for getting hotels per country * include city when listing by country * fix distance SI unit * fix sorting * Merge branch 'master' of bitbucket.org:scandic-swap/web into feature/SW-3572-hotel-data-endpoint * packages/tracking passWithNoTests * revalidate must be static value * remove oxc reference * cleanup * cleanup hotel api route * feat(SW-3572): cleanup error handling Approved-by: Anton Gunnarsson
This commit is contained in:
@@ -1,24 +1,11 @@
|
||||
import deepmerge from "deepmerge"
|
||||
|
||||
import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
|
||||
import { getCacheClient } from "@scandic-hotels/common/dataCache"
|
||||
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
|
||||
import { chunk } from "@scandic-hotels/common/utils/chunk"
|
||||
|
||||
import * as api from "../../api"
|
||||
import { BookingErrorCodeEnum } from "../../enums/bookingErrorCode"
|
||||
import { AvailabilityEnum } from "../../enums/selectHotel"
|
||||
import { toApiLang } from "../../utils"
|
||||
import { sortRoomConfigs } from "../../utils/sortRoomConfigs"
|
||||
import { getCity } from "./services/getCity"
|
||||
import { locationsSchema } from "./output"
|
||||
|
||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||
import type { z } from "zod"
|
||||
|
||||
import type { BedTypeSelection } from "../../types/bedTypeSelection"
|
||||
import type { Room as RoomCategory } from "../../types/hotel"
|
||||
import type { CitiesGroupedByCountry } from "../../types/locations"
|
||||
import type {
|
||||
Product,
|
||||
Products,
|
||||
@@ -29,114 +16,6 @@ import type {
|
||||
import type { RoomsAvailabilityExtendedInputSchema } from "./availability/enterDetails"
|
||||
export const locationsAffix = "locations"
|
||||
|
||||
const hotelUtilsLogger = createLogger("hotelUtils")
|
||||
|
||||
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) {
|
||||
hotelUtilsLogger.error(
|
||||
`Locations Verification Failed`,
|
||||
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 {
|
||||
hotelUtilsLogger.error(
|
||||
`Location cannot be found in any of the countries cities`,
|
||||
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 const TWENTYFOUR_HOURS = 60 * 60 * 24
|
||||
|
||||
function findProduct(product: Products, rateDefinition: RateDefinition) {
|
||||
|
||||
Reference in New Issue
Block a user