Merged in chore/refactor-trpc-booking-routes (pull request #3510)

feat(BOOK-750): refactor booking endpoints

* WIP

* wip

* wip

* parse dates in UTC

* wip

* no more errors

* Merge branch 'master' of bitbucket.org:scandic-swap/web into chore/refactor-trpc-booking-routes

* .

* cleanup

* import named z from zod

* fix(BOOK-750): updateBooking api endpoint expects dateOnly, we passed ISO date


Approved-by: Anton Gunnarsson
This commit is contained in:
Joakim Jäderberg
2026-02-02 14:28:14 +00:00
parent 8ac2c4ba22
commit 16cc26632e
44 changed files with 1621 additions and 1041 deletions

View File

@@ -0,0 +1,65 @@
import { createCounter } from "@scandic-hotels/common/telemetry"
import * as api from "../../../api"
import {
badRequestError,
extractResponseDetails,
serverErrorByStatus,
} from "../../../errors"
import { toApiLang } from "../../../utils"
import { bookingStatusSchema } from "./schema"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { BookingStatus } from "./schema"
export type { BookingStatus } from "./schema"
export async function getBookingStatus(
{ confirmationNumber, lang }: { confirmationNumber: string; lang: Lang },
serviceToken: string
): Promise<BookingStatus> {
const language = toApiLang(lang)
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 ${serviceToken}`,
},
},
{
language,
}
)
if (!apiResponse.ok) {
await metricsGetBookingStatus.httpError(apiResponse)
throw serverErrorByStatus(
apiResponse.status,
await extractResponseDetails(apiResponse),
"getBookingStatus failed"
)
}
const apiJson = await apiResponse.json()
const verifiedData = bookingStatusSchema.safeParse(apiJson)
if (!verifiedData.success) {
metricsGetBookingStatus.validationError(verifiedData.error)
throw badRequestError({
message: "Invalid booking data",
errorDetails: verifiedData.error.formErrors,
})
}
metricsGetBookingStatus.success()
return verifiedData.data
}

View File

@@ -0,0 +1,61 @@
import { z } from "zod"
import { calculateRefId } from "../../../utils/refId"
import { bookingReservationStatusSchema } from "../schema/bookingReservationStatusSchema"
export type BookingStatus = z.infer<typeof bookingStatusSchema>
export const bookingStatusSchema = z
.object({
data: z.object({
attributes: z.object({
reservationStatus: bookingReservationStatusSchema,
paymentUrl: z.string().nullable().optional(),
paymentMethod: z.string().nullable().optional(),
rooms: z
.array(
z.object({
confirmationNumber: z.string(),
cancellationNumber: z.string().nullable(),
priceChangedMetadata: z
.object({
roomPrice: z.number(),
totalPrice: z.number(),
})
.nullable()
.optional(),
})
)
.default([]),
errors: z
.array(
z.object({
confirmationNumber: z.string().nullable().optional(),
errorCode: z.string(),
description: z.string().nullable().optional(),
meta: z
.record(z.string(), z.union([z.string(), z.number()]))
.nullable()
.optional(),
})
)
.default([]),
}),
type: z.string(),
id: z.string(),
}),
})
.transform((d) => ({
id: d.data.id,
type: d.data.type,
reservationStatus: d.data.attributes.reservationStatus,
paymentUrl: d.data.attributes.paymentUrl,
paymentMethod: d.data.attributes.paymentMethod,
errors: d.data.attributes.errors,
rooms: d.data.attributes.rooms.map((room) => {
const lastName = ""
return {
...room,
refId: calculateRefId(room.confirmationNumber, lastName),
}
}),
}))