feat: get breakfast package from API
This commit is contained in:
@@ -39,3 +39,7 @@ export const getlHotelDataInputSchema = z.object({
|
||||
.array(z.enum(["RoomCategories", "NearbyHotels", "Restaurants", "City"]))
|
||||
.optional(),
|
||||
})
|
||||
|
||||
export const getBreakfastPackageInput = z.object({
|
||||
hotelId: z.string().min(1, { message: "hotelId is required" }),
|
||||
})
|
||||
|
||||
@@ -9,7 +9,9 @@ import { getPoiGroupByCategoryName } from "./utils"
|
||||
|
||||
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||
import { AlertTypeEnum } from "@/types/enums/alert"
|
||||
import { CurrencyEnum } from "@/types/enums/currency"
|
||||
import { FacilityEnum } from "@/types/enums/facilities"
|
||||
import { PackageTypeEnum } from "@/types/enums/packages"
|
||||
import { PointOfInterestCategoryNameEnum } from "@/types/hotel"
|
||||
|
||||
const ratingsSchema = z
|
||||
@@ -653,7 +655,7 @@ export const apiCountriesSchema = z.object({
|
||||
name: z.string(),
|
||||
}),
|
||||
hotelInformationSystemId: z.number().optional(),
|
||||
id: z.string().optional(),
|
||||
id: z.string().optional().default(""),
|
||||
language: z.string().optional(),
|
||||
type: z.literal("countries"),
|
||||
})
|
||||
@@ -794,3 +796,30 @@ export const apiLocationsSchema = z.object({
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
export const breakfastPackageSchema = z.object({
|
||||
code: z.string(),
|
||||
currency: z.nativeEnum(CurrencyEnum),
|
||||
description: z.string(),
|
||||
originalPrice: z.number().default(0),
|
||||
packagePrice: z.number(),
|
||||
packageType: z.enum([
|
||||
PackageTypeEnum.BreakfastAdult,
|
||||
PackageTypeEnum.BreakfastChildren,
|
||||
]),
|
||||
totalPrice: z.number(),
|
||||
})
|
||||
|
||||
export const breakfastPackagesSchema = z
|
||||
.object({
|
||||
data: z.object({
|
||||
attributes: z.object({
|
||||
hotelId: z.number(),
|
||||
packages: z.array(breakfastPackageSchema),
|
||||
}),
|
||||
type: z.literal("breakfastpackage"),
|
||||
}),
|
||||
})
|
||||
.transform(({ data }) =>
|
||||
data.attributes.packages.filter((pkg) => pkg.code.match(/^(BRF\d+)$/gm))
|
||||
)
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
contentStackUidWithServiceProcedure,
|
||||
publicProcedure,
|
||||
router,
|
||||
safeProtectedServiceProcedure,
|
||||
serviceProcedure,
|
||||
} from "@/server/trpc"
|
||||
import { toApiLang } from "@/server/utils"
|
||||
@@ -24,11 +25,13 @@ import {
|
||||
getHotelPageCounter,
|
||||
validateHotelPageRefs,
|
||||
} from "../contentstack/hotelPage/utils"
|
||||
import { getVerifiedUser, parsedUser } from "../user/query"
|
||||
import {
|
||||
getRoomPackagesInputSchema,
|
||||
getRoomPackagesSchema,
|
||||
} from "./schemas/packages"
|
||||
import {
|
||||
getBreakfastPackageInput,
|
||||
getHotelInputSchema,
|
||||
getHotelsAvailabilityInputSchema,
|
||||
getlHotelDataInputSchema,
|
||||
@@ -36,6 +39,7 @@ import {
|
||||
getRoomsAvailabilityInputSchema,
|
||||
} from "./input"
|
||||
import {
|
||||
breakfastPackagesSchema,
|
||||
getHotelDataSchema,
|
||||
getHotelsAvailabilitySchema,
|
||||
getRatesSchema,
|
||||
@@ -51,6 +55,7 @@ import {
|
||||
|
||||
import { FacilityCardTypeEnum } from "@/types/components/hotelPage/facilities"
|
||||
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
||||
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||
import type { RequestOptionsWithOutBody } from "@/types/fetch"
|
||||
import type { Facility } from "@/types/hotel"
|
||||
import type { GetHotelPageData } from "@/types/trpc/routers/contentstack/hotelPage"
|
||||
@@ -88,6 +93,14 @@ const roomsAvailabilityFailCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.rooms-fail"
|
||||
)
|
||||
|
||||
const breakfastPackagesCounter = meter.createCounter("trpc.package.breakfast")
|
||||
const breakfastPackagesSuccessCounter = meter.createCounter(
|
||||
"trpc.package.breakfast-success"
|
||||
)
|
||||
const breakfastPackagesFailCounter = meter.createCounter(
|
||||
"trpc.package.breakfast-fail"
|
||||
)
|
||||
|
||||
async function getContentstackData(lang: Lang, uid?: string | null) {
|
||||
if (!uid) {
|
||||
return null
|
||||
@@ -169,7 +182,7 @@ export const hotelQueryRouter = router({
|
||||
})
|
||||
)
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.hotels}/${hotelId}`,
|
||||
api.endpoints.v1.Hotel.Hotels.hotel(hotelId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
@@ -320,7 +333,7 @@ export const hotelQueryRouter = router({
|
||||
JSON.stringify({ query: { cityId, params } })
|
||||
)
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.hotelsAvailability}/${cityId}`,
|
||||
api.endpoints.v1.Availability.city(cityId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
@@ -444,7 +457,7 @@ export const hotelQueryRouter = router({
|
||||
JSON.stringify({ query: { hotelId, params } })
|
||||
)
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.roomsAvailability}/${hotelId}`,
|
||||
api.endpoints.v1.Availability.hotel(hotelId.toString()),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
@@ -587,7 +600,7 @@ export const hotelQueryRouter = router({
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.hotels}/${hotelId}`,
|
||||
api.endpoints.v1.Hotel.Hotels.hotel(hotelId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
@@ -734,7 +747,7 @@ export const hotelQueryRouter = router({
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.packages}/${hotelId}`,
|
||||
api.endpoints.v1.Package.Packages.hotel(hotelId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
@@ -789,5 +802,114 @@ export const hotelQueryRouter = router({
|
||||
|
||||
return validatedPackagesData.data
|
||||
}),
|
||||
breakfast: safeProtectedServiceProcedure
|
||||
.input(getBreakfastPackageInput)
|
||||
.query(async function ({ ctx, input }) {
|
||||
const params = {
|
||||
Adults: 2,
|
||||
EndDate: "2024-10-28",
|
||||
StartDate: "2024-10-25",
|
||||
}
|
||||
const metricsData = { ...input, ...params }
|
||||
breakfastPackagesCounter.add(1, metricsData)
|
||||
console.info(
|
||||
"api.package.breakfast start",
|
||||
JSON.stringify({ query: metricsData })
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Package.Breakfast.hotel(input.hotelId),
|
||||
{
|
||||
cache: undefined,
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
},
|
||||
next: {
|
||||
revalidate: 60,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
breakfastPackagesFailCounter.add(1, {
|
||||
...metricsData,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.hotelsAvailability error",
|
||||
JSON.stringify({
|
||||
query: metricsData,
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const breakfastPackages = breakfastPackagesSchema.safeParse(apiJson)
|
||||
if (!breakfastPackages.success) {
|
||||
hotelsAvailabilityFailCounter.add(1, {
|
||||
...metricsData,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(breakfastPackages.error),
|
||||
})
|
||||
console.error(
|
||||
"api.package.breakfast validation error",
|
||||
JSON.stringify({
|
||||
query: metricsData,
|
||||
error: breakfastPackages.error,
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
breakfastPackagesSuccessCounter.add(1, metricsData)
|
||||
console.info(
|
||||
"api.package.breakfast success",
|
||||
JSON.stringify({
|
||||
query: metricsData,
|
||||
})
|
||||
)
|
||||
|
||||
if (ctx.session?.token) {
|
||||
const apiUser = await getVerifiedUser({ session: ctx.session })
|
||||
if (apiUser && !("error" in apiUser)) {
|
||||
const user = parsedUser(apiUser.data, false)
|
||||
if (
|
||||
user.membership &&
|
||||
["L6", "L7"].includes(user.membership.membershipLevel)
|
||||
) {
|
||||
const originalBreakfastPackage = breakfastPackages.data.find(
|
||||
(pkg) => pkg.code === BreakfastPackageEnum.REGULAR_BREAKFAST
|
||||
)
|
||||
const freeBreakfastPackage = breakfastPackages.data.find(
|
||||
(pkg) => pkg.code === BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
|
||||
)
|
||||
if (freeBreakfastPackage) {
|
||||
if (originalBreakfastPackage) {
|
||||
freeBreakfastPackage.originalPrice =
|
||||
originalBreakfastPackage.packagePrice
|
||||
}
|
||||
return [freeBreakfastPackage]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return breakfastPackages.data.filter(
|
||||
(pkg) => pkg.code !== BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
|
||||
)
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -96,7 +96,7 @@ export async function getCountries(
|
||||
return unstable_cache(
|
||||
async function (searchParams) {
|
||||
const countryResponse = await api.get(
|
||||
api.endpoints.v1.countries,
|
||||
api.endpoints.v1.Hotel.countries,
|
||||
options,
|
||||
searchParams
|
||||
)
|
||||
@@ -136,7 +136,7 @@ export async function getCitiesByCountry(
|
||||
await Promise.all(
|
||||
searchedCountries.data.map(async (country) => {
|
||||
const countryResponse = await api.get(
|
||||
`${api.endpoints.v1.citiesCountry}/${country.name}`,
|
||||
api.endpoints.v1.Hotel.Cities.country(country.name),
|
||||
options,
|
||||
searchParams
|
||||
)
|
||||
@@ -182,7 +182,7 @@ export async function getLocations(
|
||||
groupedCitiesByCountry: CitiesGroupedByCountry | null
|
||||
) {
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.locations,
|
||||
api.endpoints.v1.Hotel.locations,
|
||||
options,
|
||||
searchParams
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user