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:
Joakim Jäderberg
2025-11-03 12:10:22 +00:00
parent e8626d56af
commit 15a2da333d
25 changed files with 1227 additions and 249 deletions

View File

@@ -0,0 +1,52 @@
import { z } from "zod"
import { Lang } from "@scandic-hotels/common/constants/language"
import { getCacheClient } from "@scandic-hotels/common/dataCache"
import { serviceProcedure } from "../../../procedures"
import { getCitiesByCountry } from "../services/getCitiesByCountry"
import { getCountries } from "../services/getCountries"
import { getLocationsByCountries } from "../services/getLocationsByCountries"
const getLocationsInput = z.object({
lang: z.nativeEnum(Lang),
})
export const get = serviceProcedure
.input(getLocationsInput)
.query(async function ({ ctx, input }) {
const lang = input.lang ?? ctx.lang
const cacheClient = await getCacheClient()
return await cacheClient.cacheOrGet(
`${lang}:getLocations`,
async () => {
const countries = await getCountries({
lang: lang,
serviceToken: ctx.serviceToken,
})
if (!countries) {
throw new Error("Unable to fetch countries")
}
const countryNames = countries.data.map((country) => country.name)
const citiesByCountry = await getCitiesByCountry({
countries: countryNames,
serviceToken: ctx.serviceToken,
lang,
})
const locations = await getLocationsByCountries({
lang,
serviceToken: ctx.serviceToken,
citiesByCountry,
})
if (!locations) {
throw new Error("Unable to fetch locations")
}
return locations
},
"max"
)
})

View File

@@ -0,0 +1,8 @@
import { router } from "../../.."
import { get } from "./get"
import { urls } from "./urls"
export const locationsRouter = router({
get,
urls,
})

View File

@@ -0,0 +1,49 @@
import { z } from "zod"
import { Lang } from "@scandic-hotels/common/constants/language"
import { createCounter } from "@scandic-hotels/common/telemetry"
import { publicProcedure } from "../../../procedures"
import { getCityPageUrls } from "../../contentstack/destinationCityPage/utils"
import { getHotelPageUrls } from "../../contentstack/hotelPage/utils"
const getLocationsUrlsInput = z.object({
lang: z.nativeEnum(Lang),
})
export const urls = publicProcedure
.input(getLocationsUrlsInput)
.query(async ({ input }) => {
const { lang } = input
const locationsUrlsCounter = createCounter("trpc.hotel.locations", "urls")
const metricsLocationsUrls = locationsUrlsCounter.init({
lang,
})
metricsLocationsUrls.start()
const [hotelPageUrlsResult, cityPageUrlsResult] = await Promise.allSettled([
getHotelPageUrls(lang),
getCityPageUrls(lang),
])
if (
hotelPageUrlsResult.status === "rejected" ||
cityPageUrlsResult.status === "rejected"
) {
metricsLocationsUrls.dataError(`Failed to get data for page URLs`, {
hotelPageUrlsResult,
cityPageUrlsResult,
})
return null
}
metricsLocationsUrls.success()
return {
hotels: hotelPageUrlsResult.value,
cities: cityPageUrlsResult.value,
}
})