Files
web/server/routers/booking/mutation.ts
Niclas Edenvin b9dbcf7d90 Merged in feat/booking-flow-submit (pull request #580)
This implements the actual call to the API to create a booking. That’s the only thing it does, it doesn’t handle the response in any way.

This PR is just to get it there and the new booking sub team will handle it further, with payment etc.

Approved-by: Michael Zetterberg
Approved-by: Fredrik Thorsson
Approved-by: Simon.Emanuelsson
2024-09-20 13:05:23 +00:00

130 lines
3.5 KiB
TypeScript

import { metrics } from "@opentelemetry/api"
import * as api from "@/lib/api"
import { getVerifiedUser } from "@/server/routers/user/query"
import { router, safeProtectedProcedure } from "@/server/trpc"
import { getMembership } from "@/utils/user"
import { createBookingInput } from "./input"
import { createBookingSchema } from "./output"
import type { Session } from "next-auth"
const meter = metrics.getMeter("trpc.bookings")
const createBookingCounter = meter.createCounter("trpc.bookings.create")
const createBookingSuccessCounter = meter.createCounter(
"trpc.bookings.create-success"
)
const createBookingFailCounter = meter.createCounter(
"trpc.bookings.create-fail"
)
async function getMembershipNumber(
session: Session | null
): Promise<string | undefined> {
if (!session) return undefined
const verifiedUser = await getVerifiedUser({ session })
if (!verifiedUser || "error" in verifiedUser) {
return undefined
}
const membership = getMembership(verifiedUser.data.memberships)
return membership?.membershipNumber
}
export const bookingMutationRouter = router({
booking: router({
create: safeProtectedProcedure
.input(createBookingInput)
.mutation(async function ({ ctx, input }) {
const { checkInDate, checkOutDate, hotelId } = input
const loggingAttributes = {
membershipNumber: await getMembershipNumber(ctx.session),
checkInDate,
checkOutDate,
hotelId,
}
createBookingCounter.add(1, { hotelId, checkInDate, checkOutDate })
console.info(
"api.booking.booking.create start",
JSON.stringify({
query: loggingAttributes,
})
)
const headers = ctx.session
? {
Authorization: `Bearer ${ctx.session?.token.access_token}`,
}
: undefined
const apiResponse = await api.post(api.endpoints.v1.booking, {
headers,
body: input,
})
if (!apiResponse.ok) {
const text = await apiResponse.text()
createBookingFailCounter.add(1, {
hotelId,
checkInDate,
checkOutDate,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
}),
})
console.error(
"api.booking.booking.create error",
JSON.stringify({
query: loggingAttributes,
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
error: text,
},
})
)
return null
}
const apiJson = await apiResponse.json()
const verifiedData = createBookingSchema.safeParse(apiJson)
if (!verifiedData.success) {
createBookingFailCounter.add(1, {
hotelId,
checkInDate,
checkOutDate,
error_type: "validation_error",
})
console.error(
"api.booking.booking.create validation error",
JSON.stringify({
query: loggingAttributes,
error: verifiedData.error,
})
)
return null
}
createBookingSuccessCounter.add(1, {
hotelId,
checkInDate,
checkOutDate,
})
console.info(
"api.booking.booking.create success",
JSON.stringify({
query: loggingAttributes,
})
)
return verifiedData.data
}),
}),
})