import * as api from "@/lib/api" import { badRequestError, serverErrorByStatus } from "@/server/errors/trpc" import { createCounter } from "@/server/telemetry" import { getUserOrServiceToken } from "@/server/tokenManager" import { toApiLang } from "@/server/utils" import { getCacheClient } from "@/services/dataCache" import { bookingSchema, createBookingSchema } from "./output" import type { BookingSchema } from "@/types/trpc/routers/booking/confirmation" import type { Lang } from "@/constants/languages" export async function getBooking(confirmationNumber: string, lang: Lang) { const getBookingCounter = createCounter("booking", "get") const metricsGetBooking = getBookingCounter.init({ confirmationNumber }) metricsGetBooking.start() const cacheKey = `${lang}:booking:${confirmationNumber}` const cache = await getCacheClient() const result: BookingSchema | null = await cache.cacheOrGet( cacheKey, async () => { const token = getUserOrServiceToken() const apiResponse = await api.get( api.endpoints.v1.Booking.booking(confirmationNumber), { headers: { Authorization: `Bearer ${token}`, }, }, { language: toApiLang(lang) } ) 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 = bookingSchema.safeParse(apiJson) if (!booking.success) { metricsGetBooking.validationError(booking.error) throw badRequestError() } return booking.data }, "1h" ) metricsGetBooking.success() return result } export async function getBookings(confirmationNumbers: string[], lang: Lang) { const results = await Promise.allSettled( confirmationNumbers.map((confirmationNumber) => { return getBooking(confirmationNumber, lang) }) ) return results.map((result) => { if (result.status === "fulfilled" && result.value) { return result.value } return null }) } export async function getLinkedReservations( confirmationNumber: string, lang: Lang ) { const booking = await getBooking(confirmationNumber, lang) if (!booking) { return null } if (booking.linkedReservations.length > 0) { const confirmationNumbers = booking.linkedReservations.map( (linkedReservation) => { return linkedReservation.confirmationNumber } ) const bookings = await getBookings(confirmationNumbers, lang) const linkedReservations = bookings.map((booking, i) => { if (booking === null) { return { confirmationNumber: confirmationNumbers[i], error: true, } as const } return booking }) return linkedReservations } return [] } export async function cancelBooking(confirmationNumber: string, lang: Lang) { const cancelBookingCounter = createCounter("booking", "cancel") const metricsCancelBooking = cancelBookingCounter.init({ confirmationNumber, lang, }) metricsCancelBooking.start() const token = getUserOrServiceToken() const headers = { Authorization: `Bearer ${token}`, } const booking = await getBooking(confirmationNumber, lang) if (!booking) { metricsCancelBooking.noDataError({ confirmationNumber }) return null } const { firstName, lastName, email } = booking.guest const apiResponse = await api.remove( api.endpoints.v1.Booking.cancel(confirmationNumber), { headers, body: { firstName, lastName, email }, }, { language: toApiLang(lang) } ) if (!apiResponse.ok) { await metricsCancelBooking.httpError(apiResponse) return null } const apiJson = await apiResponse.json() const verifiedData = createBookingSchema.safeParse(apiJson) if (!verifiedData.success) { metricsCancelBooking.validationError(verifiedData.error) return null } metricsCancelBooking.success() return verifiedData.data } export async function findBooking( confirmationNumber: string, lang: Lang, token: string, lastName?: string, firstName?: string, email?: string ) { const findBookingCounter = createCounter("booking", "find") const metricsGetBooking = findBookingCounter.init({ confirmationNumber, lastName, firstName, email, }) metricsGetBooking.start() const apiResponse = await api.post( api.endpoints.v1.Booking.find(confirmationNumber), { headers: { Authorization: `Bearer ${token}`, }, body: { lastName, firstName, email, }, }, { language: toApiLang(lang) } ) 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 = bookingSchema.safeParse(apiJson) if (!booking.success) { metricsGetBooking.validationError(booking.error) throw badRequestError() } metricsGetBooking.success() return booking.data }