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, } from "./input" import { bookingConfirmationSchema, createBookingSchema } from "./output" import { getBookedHotelRoom } from "./utils" export const bookingQueryRouter = router({ get: safeProtectedServiceProcedure .input(getBookingInput) .query(async function ({ ctx, input: { confirmationNumber, lang: inputLang }, }) { const getBookingCounter = createCounter("trpc.booking", "get") const metricsGetBooking = getBookingCounter.init({ confirmationNumber }) metricsGetBooking.start() let lang = ctx.lang ?? inputLang const token = ctx.session?.token.access_token ?? ctx.serviceToken const apiResponse = await api.get( api.endpoints.v1.Booking.booking(confirmationNumber), { headers: { Authorization: `Bearer ${token}`, }, } ) if (!apiResponse.ok) { await metricsGetBooking.httpError(apiResponse) // If the booking is not found, return null. // This scenario is expected to happen when a logged in user trying to access a booking that doesn't belong to them. if (apiResponse.status === 400) { return null } throw serverErrorByStatus(apiResponse.status, apiResponse) } const apiJson = await apiResponse.json() const booking = bookingConfirmationSchema.safeParse(apiJson) if (!booking.success) { metricsGetBooking.validationError(booking.error) throw badRequestError() } const hotelData = await getHotel( { hotelId: booking.data.hotelId, isCardOnlyPayment: false, language: lang, }, ctx.serviceToken ) if (!hotelData) { metricsGetBooking.dataError( `Failed to get hotel data for ${booking.data.hotelId}`, { hotelId: booking.data.hotelId, } ) throw serverErrorByStatus(404) } metricsGetBooking.success() return { ...hotelData, booking: booking.data, room: getBookedHotelRoom( hotelData.roomCategories, booking.data.roomTypeCode ), } }), 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, } }), })