Files
web/apps/scandic-web/server/routers/booking/mutation.ts
Anton Gunnarsson 43d3713f59 Merged in feat/SW-1810-prevent-starting-link-flow-when-linked (pull request #1469)
Prevent SAS link flow when already linked

* Implement check for checking sas_eb membership when starting link flow


Approved-by: Joakim Jäderberg
2025-03-05 08:17:02 +00:00

460 lines
12 KiB
TypeScript

import { metrics } from "@opentelemetry/api"
import * as api from "@/lib/api"
import { getVerifiedUser } from "@/server/routers/user/query"
import { router, safeProtectedServiceProcedure } from "@/server/trpc"
import { getFriendsMembership } from "@/utils/user"
import {
addPackageInput,
cancelBookingInput,
createBookingInput,
priceChangeInput,
removePackageInput,
} 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"
)
const priceChangeCounter = meter.createCounter("trpc.bookings.price-change")
const priceChangeSuccessCounter = meter.createCounter(
"trpc.bookings.price-change-success"
)
const priceChangeFailCounter = meter.createCounter(
"trpc.bookings.price-change-fail"
)
const cancelBookingCounter = meter.createCounter("trpc.bookings.cancel")
const cancelBookingSuccessCounter = meter.createCounter(
"trpc.bookings.cancel-success"
)
const cancelBookingFailCounter = meter.createCounter(
"trpc.bookings.cancel-fail"
)
const addPackageCounter = meter.createCounter("trpc.bookings.add-package")
const addPackageSuccessCounter = meter.createCounter(
"trpc.bookings.add-package-success"
)
const addPackageFailCounter = meter.createCounter(
"trpc.bookings.add-package-fail"
)
const removePackageCounter = meter.createCounter("trpc.bookings.remove-package")
const removePackageSuccessCounter = meter.createCounter(
"trpc.bookings.remove-package-success"
)
const removePackageFailCounter = meter.createCounter(
"trpc.bookings.remove-package-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 = getFriendsMembership(verifiedUser.data.memberships)
return membership?.membershipNumber
}
export const bookingMutationRouter = router({
create: safeProtectedServiceProcedure
.input(createBookingInput)
.mutation(async function ({ ctx, input }) {
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
const { language, ...inputWithoutLang } = input
const { hotelId, checkInDate, checkOutDate } = inputWithoutLang
const loggingAttributes = {
membershipNumber: await getMembershipNumber(ctx.session),
checkInDate,
checkOutDate,
hotelId,
language,
}
createBookingCounter.add(1, loggingAttributes)
console.info(
"api.booking.create start",
JSON.stringify({
query: loggingAttributes,
})
)
const headers = {
Authorization: `Bearer ${accessToken}`,
}
const apiResponse = await api.post(
api.endpoints.v1.Booking.bookings,
{
headers,
body: inputWithoutLang,
},
{ language }
)
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.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.create validation error",
JSON.stringify({
query: loggingAttributes,
error: verifiedData.error,
})
)
return null
}
createBookingSuccessCounter.add(1, {
hotelId,
checkInDate,
checkOutDate,
})
console.info(
"api.booking.create success",
JSON.stringify({
query: loggingAttributes,
})
)
return verifiedData.data
}),
priceChange: safeProtectedServiceProcedure
.input(priceChangeInput)
.mutation(async function ({ ctx, input }) {
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
const { confirmationNumber } = input
priceChangeCounter.add(1, { confirmationNumber })
const headers = {
Authorization: `Bearer ${accessToken}`,
}
const apiResponse = await api.put(
api.endpoints.v1.Booking.priceChange(confirmationNumber),
{
headers,
body: input,
}
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
priceChangeFailCounter.add(1, {
confirmationNumber,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
}),
})
console.error(
"api.booking.priceChange error",
JSON.stringify({
query: { confirmationNumber },
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
error: text,
},
})
)
return null
}
const apiJson = await apiResponse.json()
const verifiedData = createBookingSchema.safeParse(apiJson)
if (!verifiedData.success) {
priceChangeFailCounter.add(1, {
confirmationNumber,
error_type: "validation_error",
})
console.error(
"api.booking.priceChange validation error",
JSON.stringify({
query: { confirmationNumber },
error: verifiedData.error,
})
)
return null
}
priceChangeSuccessCounter.add(1, { confirmationNumber })
return verifiedData.data
}),
cancel: safeProtectedServiceProcedure
.input(cancelBookingInput)
.mutation(async function ({ ctx, input }) {
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
const { confirmationNumber, language } = input
const headers = {
Authorization: `Bearer ${accessToken}`,
}
const cancellationReason = {
reasonCode: "WEB-CANCEL",
reason: "WEB-CANCEL",
}
const loggingAttributes = {
confirmationNumber,
language,
}
cancelBookingCounter.add(1, loggingAttributes)
console.info(
"api.booking.cancel start",
JSON.stringify({
request: loggingAttributes,
headers,
})
)
const apiResponse = await api.remove(
api.endpoints.v1.Booking.cancel(confirmationNumber),
{
headers,
body: JSON.stringify(cancellationReason),
} as RequestInit,
{ language }
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
cancelBookingFailCounter.add(1, {
confirmationNumber,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
}),
})
console.error(
"api.booking.cancel error",
JSON.stringify({
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
text,
},
query: loggingAttributes,
})
)
return false
}
const apiJson = await apiResponse.json()
const verifiedData = createBookingSchema.safeParse(apiJson)
if (!verifiedData.success) {
cancelBookingFailCounter.add(1, {
confirmationNumber,
error_type: "validation_error",
})
console.error(
"api.booking.cancel validation error",
JSON.stringify({
query: loggingAttributes,
error: verifiedData.error,
})
)
return null
}
cancelBookingSuccessCounter.add(1, loggingAttributes)
console.info(
"api.booking.cancel success",
JSON.stringify({
query: loggingAttributes,
})
)
return verifiedData.data
}),
packages: safeProtectedServiceProcedure
.input(addPackageInput)
.mutation(async function ({ ctx, input }) {
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
const { confirmationNumber, ...body } = input
addPackageCounter.add(1, { confirmationNumber })
const headers = {
Authorization: `Bearer ${accessToken}`,
}
const apiResponse = await api.post(
api.endpoints.v1.Booking.packages(confirmationNumber),
{
headers,
body: body,
}
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
addPackageFailCounter.add(1, {
confirmationNumber,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
}),
})
console.error(
"api.booking.addPackage error",
JSON.stringify({
query: { confirmationNumber },
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
error: text,
},
})
)
return null
}
const apiJson = await apiResponse.json()
const verifiedData = createBookingSchema.safeParse(apiJson)
if (!verifiedData.success) {
addPackageFailCounter.add(1, {
confirmationNumber,
error_type: "validation_error",
})
console.error(
"api.booking.addPackage validation error",
JSON.stringify({
query: { confirmationNumber },
error: verifiedData.error,
})
)
return null
}
addPackageSuccessCounter.add(1, { confirmationNumber })
return verifiedData.data
}),
removePackage: safeProtectedServiceProcedure
.input(removePackageInput)
.mutation(async function ({ ctx, input }) {
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
const { confirmationNumber, codes, language } = input
const headers = {
Authorization: `Bearer ${accessToken}`,
}
const loggingAttributes = {
confirmationNumber,
codes,
language,
}
removePackageCounter.add(1, loggingAttributes)
console.info(
"api.booking.remove-package start",
JSON.stringify({
request: loggingAttributes,
headers,
})
)
const apiResponse = await api.remove(
api.endpoints.v1.Booking.packages(confirmationNumber),
{
headers,
} as RequestInit,
{ language, codes }
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
removePackageFailCounter.add(1, {
confirmationNumber,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
}),
})
console.error(
"api.booking.remove-package error",
JSON.stringify({
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
text,
},
query: loggingAttributes,
})
)
return false
}
removePackageSuccessCounter.add(1, loggingAttributes)
console.info(
"api.booking.remove-package success",
JSON.stringify({
query: loggingAttributes,
})
)
return true
}),
})