import * as api from "@/lib/api" import { badRequestError, serverErrorByStatus } from "@/server/errors/trpc" import { createCounter } from "@/server/telemetry" import { router, safeProtectedServiceProcedure, serviceProcedure, } from "@/server/trpc" import { getHotel } from "../hotels/utils" import { encrypt } from "../utils/encryption" import { createRefIdInput, getBookingInput, getBookingStatusInput, getLinkedReservationsInput, } from "./input" import { createBookingSchema } from "./output" import { getBookedHotelRoom, getBooking } from "./utils" export const bookingQueryRouter = router({ get: safeProtectedServiceProcedure .input(getBookingInput) .use(async ({ ctx, input, next }) => { const lang = input.lang ?? ctx.lang const token = ctx.session?.token.access_token ?? ctx.serviceToken return next({ ctx: { lang, token, }, }) }) .query(async function ({ ctx, input: { confirmationNumber } }) { const getBookingCounter = createCounter("trpc.booking", "get") const metricsGetBooking = getBookingCounter.init({ confirmationNumber }) metricsGetBooking.start() const booking = await getBooking(confirmationNumber, ctx.lang, ctx.token) if (!booking) { metricsGetBooking.dataError( `Fail to get booking data for ${confirmationNumber}`, { confirmationNumber } ) return null } const hotelData = await getHotel( { hotelId: booking.hotelId, isCardOnlyPayment: false, language: ctx.lang, }, ctx.serviceToken ) if (!hotelData) { metricsGetBooking.dataError( `Failed to get hotel data for ${booking.hotelId}`, { hotelId: booking.hotelId, } ) throw serverErrorByStatus(404) } metricsGetBooking.success() return { ...hotelData, booking, room: getBookedHotelRoom( hotelData.roomCategories, booking.roomTypeCode ), } }), linkedReservations: safeProtectedServiceProcedure .input(getLinkedReservationsInput) .use(async ({ ctx, input, next }) => { const lang = input.lang ?? ctx.lang const token = ctx.session?.token.access_token ?? ctx.serviceToken return next({ ctx: { lang, token, }, }) }) .query(async function ({ ctx, input: { rooms } }) { const getLinkedReservationsCounter = createCounter( "trpc.booking", "linkedReservations" ) const metricsGetLinkedReservations = getLinkedReservationsCounter.init({ confirmationNumbers: rooms, }) metricsGetLinkedReservations.start() const linkedReservationsResult = await Promise.allSettled( rooms.map((room) => getBooking(room.confirmationNumber, ctx.lang, ctx.token) ) ) const linkedReservations = [] for (const booking of linkedReservationsResult) { if (booking.status === "fulfilled") { if (booking.value) { linkedReservations.push(booking.value) } else { metricsGetLinkedReservations.dataError( `Unexpected value for linked reservation` ) } } else { metricsGetLinkedReservations.dataError( `Failed to get linked reservation` ) } } metricsGetLinkedReservations.success() return linkedReservations }), status: serviceProcedure.input(getBookingStatusInput).query(async function ({ ctx, input, }) { const { confirmationNumber } = input const getBookingStatusCounter = createCounter("trpc.booking", "status") const metricsGetBookingStatus = getBookingStatusCounter.init({ confirmationNumber, }) metricsGetBookingStatus.start() const apiResponse = await api.get( api.endpoints.v1.Booking.status(confirmationNumber), { headers: { Authorization: `Bearer ${ctx.serviceToken}`, }, } ) if (!apiResponse.ok) { await metricsGetBookingStatus.httpError(apiResponse) throw serverErrorByStatus(apiResponse.status, apiResponse) } const apiJson = await apiResponse.json() const verifiedData = createBookingSchema.safeParse(apiJson) if (!verifiedData.success) { metricsGetBookingStatus.validationError(verifiedData.error) throw badRequestError() } metricsGetBookingStatus.success() return verifiedData.data }), createRefId: serviceProcedure .input(createRefIdInput) .mutation(async function ({ input }) { const { confirmationNumber, lastName } = input const encryptedRefId = encrypt(`${confirmationNumber},${lastName}`) if (!encryptedRefId) { throw serverErrorByStatus(422, "Was not able to encrypt ref id") } return { refId: encryptedRefId, } }), })