Merge branch 'develop' into feature/tracking

This commit is contained in:
Linus Flood
2024-10-08 15:13:16 +02:00
178 changed files with 3745 additions and 1291 deletions

View File

@@ -1,5 +1,9 @@
import { mergeRouters } from "@/server/trpc"
import { bookingMutationRouter } from "./mutation"
import { bookingQueryRouter } from "./query"
export const bookingRouter = mergeRouters(bookingMutationRouter)
export const bookingRouter = mergeRouters(
bookingMutationRouter,
bookingQueryRouter
)

View File

@@ -1,38 +1,68 @@
import { z } from "zod"
// Query
const roomsSchema = z.array(
z.object({
adults: z.number().int().nonnegative(),
childrenAges: z
.array(
z.object({
age: z.number().int().nonnegative(),
bedType: z.string(),
})
)
.default([]),
rateCode: z.string(),
roomTypeCode: z.string(),
guest: z.object({
title: z.string(),
firstName: z.string(),
lastName: z.string(),
email: z.string().email(),
phoneCountryCodePrefix: z.string(),
phoneNumber: z.string(),
countryCode: z.string(),
membershipNumber: z.string().optional(),
}),
smsConfirmationRequested: z.boolean(),
packages: z.object({
breakfast: z.boolean(),
allergyFriendly: z.boolean(),
petFriendly: z.boolean(),
accessibility: z.boolean(),
}),
})
)
const paymentSchema = z.object({
paymentMethod: z.string(),
card: z
.object({
alias: z.string(),
expiryDate: z.string(),
cardType: z.string(),
})
.optional(),
cardHolder: z.object({
email: z.string().email(),
name: z.string(),
phoneCountryCode: z.string(),
phoneSubscriber: z.string(),
}),
success: z.string(),
error: z.string(),
cancel: z.string(),
})
// Mutation
export const createBookingInput = z.object({
hotelId: z.string(),
checkInDate: z.string(),
checkOutDate: z.string(),
rooms: z.array(
z.object({
adults: z.number().int().nonnegative(),
children: z.number().int().nonnegative(),
rateCode: z.string(),
roomTypeCode: z.string(),
guest: z.object({
title: z.string(),
firstName: z.string(),
lastName: z.string(),
email: z.string().email(),
phoneCountryCodePrefix: z.string(),
phoneNumber: z.string(),
countryCode: z.string(),
}),
smsConfirmationRequested: z.boolean(),
})
),
payment: z.object({
cardHolder: z.object({
Email: z.string().email(),
Name: z.string(),
PhoneCountryCode: z.string(),
PhoneSubscriber: z.string(),
}),
success: z.string(),
error: z.string(),
cancel: z.string(),
}),
rooms: roomsSchema,
payment: paymentSchema,
})
// Query
export const getBookingStatusInput = z.object({
confirmationNumber: z.string(),
})

View File

@@ -2,7 +2,7 @@ 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 { bookingServiceProcedure, router } from "@/server/trpc"
import { getMembership } from "@/utils/user"
@@ -36,13 +36,15 @@ async function getMembershipNumber(
export const bookingMutationRouter = router({
booking: router({
create: safeProtectedProcedure
create: bookingServiceProcedure
.input(createBookingInput)
.mutation(async function ({ ctx, input }) {
const { checkInDate, checkOutDate, hotelId } = input
// TODO: add support for user token OR service token in procedure
// then we can fetch membership number if user token exists
const loggingAttributes = {
membershipNumber: await getMembershipNumber(ctx.session),
// membershipNumber: await getMembershipNumber(ctx.session),
checkInDate,
checkOutDate,
hotelId,
@@ -56,11 +58,10 @@ export const bookingMutationRouter = router({
query: loggingAttributes,
})
)
const headers = ctx.session
? {
Authorization: `Bearer ${ctx.session?.token.access_token}`,
}
: undefined
const headers = {
Authorization: `Bearer ${ctx.serviceToken}`,
}
const apiResponse = await api.post(api.endpoints.v1.booking, {
headers,
body: input,

View File

@@ -5,9 +5,9 @@ export const createBookingSchema = z
data: z.object({
attributes: z.object({
confirmationNumber: z.string(),
cancellationNumber: z.string().nullable(),
cancellationNumber: z.string().optional(),
reservationStatus: z.string(),
paymentUrl: z.string().nullable(),
paymentUrl: z.string().optional(),
}),
type: z.string(),
id: z.string(),

View File

@@ -0,0 +1,85 @@
import { metrics } from "@opentelemetry/api"
import * as api from "@/lib/api"
import { badRequestError, serverErrorByStatus } from "@/server/errors/trpc"
import { bookingServiceProcedure, router } from "@/server/trpc"
import { getBookingStatusInput } from "./input"
import { createBookingSchema } from "./output"
const meter = metrics.getMeter("trpc.booking")
const getBookingStatusCounter = meter.createCounter("trpc.booking.status")
const getBookingStatusSuccessCounter = meter.createCounter(
"trpc.booking.status-success"
)
const getBookingStatusFailCounter = meter.createCounter(
"trpc.booking.status-fail"
)
export const bookingQueryRouter = router({
status: bookingServiceProcedure
.input(getBookingStatusInput)
.query(async function ({ ctx, input }) {
const { confirmationNumber } = input
getBookingStatusCounter.add(1, { confirmationNumber })
const apiResponse = await api.get(
`${api.endpoints.v1.booking}/${confirmationNumber}/status`,
{
headers: {
Authorization: `Bearer ${ctx.serviceToken}`,
},
}
)
if (!apiResponse.ok) {
const responseMessage = await apiResponse.text()
getBookingStatusFailCounter.add(1, {
confirmationNumber,
error_type: "http_error",
error: responseMessage,
})
console.error(
"api.booking.status error",
JSON.stringify({
query: { confirmationNumber },
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
text: responseMessage,
},
})
)
throw serverErrorByStatus(apiResponse.status, apiResponse)
}
const apiJson = await apiResponse.json()
const verifiedData = createBookingSchema.safeParse(apiJson)
if (!verifiedData.success) {
getBookingStatusFailCounter.add(1, {
confirmationNumber,
error_type: "validation_error",
error: JSON.stringify(verifiedData.error),
})
console.error(
"api.booking.status validation error",
JSON.stringify({
query: { confirmationNumber },
error: verifiedData.error,
})
)
throw badRequestError()
}
getBookingStatusSuccessCounter.add(1, { confirmationNumber })
console.info(
"api.booking.status success",
JSON.stringify({
query: { confirmationNumber },
})
)
return verifiedData.data
}),
})

View File

@@ -551,10 +551,13 @@ const linkSchema = z
})
.transform((data) => {
if (data.linkConnection.edges.length) {
const link = pageLinks.transform(data.linkConnection.edges[0].node)
if (link) {
return {
link,
const linkNode = data.linkConnection.edges[0].node
if (linkNode) {
const link = pageLinks.transform(linkNode)
if (link) {
return {
link,
}
}
}
}

View File

@@ -31,6 +31,17 @@ const getAllLoyaltyLevelFailCounter = meter.createCounter(
"trpc.contentstack.loyaltyLevel.all-fail"
)
const getByLevelLoyaltyLevelCounter = meter.createCounter(
"trpc.contentstack.loyaltyLevel.byLevel"
)
const getByLevelLoyaltyLevelSuccessCounter = meter.createCounter(
"trpc.contentstack.loyaltyLevel.byLevel-success"
)
const getByLevelLoyaltyLevelFailCounter = meter.createCounter(
"trpc.contentstack.loyaltyLevel.byLevel-fail"
)
export async function getAllLoyaltyLevels(ctx: Context) {
getAllLoyaltyLevelCounter.add(1)
@@ -87,7 +98,9 @@ export async function getAllLoyaltyLevels(ctx: Context) {
}
export async function getLoyaltyLevel(ctx: Context, level_id: MembershipLevel) {
getAllLoyaltyLevelCounter.add(1)
getByLevelLoyaltyLevelCounter.add(1, {
query: JSON.stringify({ lang: ctx.lang, level_id }),
})
const loyaltyLevelsConfigResponse = await request<LoyaltyLevelsResponse>(
GetLoyaltyLevel,
@@ -103,10 +116,10 @@ export async function getLoyaltyLevel(ctx: Context, level_id: MembershipLevel) {
!loyaltyLevelsConfigResponse.data ||
!loyaltyLevelsConfigResponse.data.all_loyalty_level.items.length
) {
getAllLoyaltyLevelFailCounter.add(1)
getByLevelLoyaltyLevelFailCounter.add(1)
const notFoundError = notFound(loyaltyLevelsConfigResponse)
console.error(
"contentstack.loyaltyLevels not found error",
"contentstack.loyaltyLevel not found error",
JSON.stringify({
query: { lang: ctx.lang, level_id },
error: { code: notFoundError.code },
@@ -119,10 +132,10 @@ export async function getLoyaltyLevel(ctx: Context, level_id: MembershipLevel) {
loyaltyLevelsConfigResponse.data
)
if (!validatedLoyaltyLevels.success) {
getAllLoyaltyLevelFailCounter.add(1)
getByLevelLoyaltyLevelFailCounter.add(1)
console.error(validatedLoyaltyLevels.error)
console.error(
"contentstack.rewards validation error",
"contentstack.loyaltyLevel validation error",
JSON.stringify({
query: { lang: ctx.lang, level_id },
error: validatedLoyaltyLevels.error,
@@ -131,7 +144,7 @@ export async function getLoyaltyLevel(ctx: Context, level_id: MembershipLevel) {
return null
}
getAllLoyaltyLevelSuccessCounter.add(1)
getByLevelLoyaltyLevelSuccessCounter.add(1)
return validatedLoyaltyLevels.data[0]
}

View File

@@ -47,13 +47,13 @@ export const activitiesCard = z.object({
}
}
return {
background_image: data.background_image,
body_text: data.body_text,
backgroundImage: data.background_image,
bodyText: data.body_text,
contentPage,
cta_text: data.cta_text,
ctaText: data.cta_text,
heading: data.heading,
open_in_new_tab: !!data.open_in_new_tab,
scripted_title: data.scripted_title,
openInNewTab: !!data.open_in_new_tab,
scriptedTopTitle: data.scripted_title,
}
}),
})

View File

@@ -25,10 +25,13 @@ export const linkConnectionSchema = z
})
.transform((data) => {
if (data.linkConnection.edges.length) {
const link = pageLinks.transform(data.linkConnection.edges[0].node)
if (link) {
return {
link,
const linkNode = data.linkConnection.edges[0].node
if (linkNode) {
const link = pageLinks.transform(linkNode)
if (link) {
return {
link,
}
}
}
}
@@ -54,17 +57,20 @@ export const linkConnectionRefs = z
linkConnection: z.object({
edges: z.array(
z.object({
node: linkRefsUnionSchema,
node: discriminatedUnion(linkRefsUnionSchema.options),
})
),
}),
})
.transform((data) => {
if (data.linkConnection.edges.length) {
const link = pageLinks.transformRef(data.linkConnection.edges[0].node)
if (link) {
return {
link,
const linkNode = data.linkConnection.edges[0].node
if (linkNode) {
const link = pageLinks.transformRef(linkNode)
if (link) {
return {
link,
}
}
}
}

View File

@@ -6,7 +6,7 @@ export const getHotelInputSchema = z.object({
.optional(),
})
export const getAvailabilityInputSchema = z.object({
export const getHotelsAvailabilityInputSchema = z.object({
cityId: z.string(),
roomStayStartDate: z.string(),
roomStayEndDate: z.string(),

View File

@@ -164,6 +164,16 @@ const detailedFacilitySchema = z.object({
filter: z.string().optional(),
})
export const facilitySchema = z.object({
headingText: z.string(),
heroImages: z.array(
z.object({
metaData: imageMetaDataSchema,
imageSizes: imageSizesSchema,
})
),
})
const healthFacilitySchema = z.object({
type: z.string(),
content: z.object({
@@ -436,6 +446,22 @@ export const roomSchema = z.object({
type: z.enum(["roomcategories"]),
})
const merchantInformationSchema = z.object({
webMerchantId: z.string(),
cards: z.record(z.string(), z.boolean()).transform((val) => {
return Object.entries(val)
.filter(([_, enabled]) => enabled)
.map(([key]) => key)
}),
alternatePaymentOptions: z
.record(z.string(), z.boolean())
.transform((val) => {
return Object.entries(val)
.filter(([_, enabled]) => enabled)
.map(([key]) => key)
}),
})
// NOTE: Find schema at: https://aks-test.scandichotels.com/hotel/swagger/v1/index.html
export const getHotelDataSchema = z.object({
data: z.object({
@@ -471,6 +497,7 @@ export const getHotelDataSchema = z.object({
hotelContent: hotelContentSchema,
detailedFacilities: z.array(detailedFacilitySchema),
healthFacilities: z.array(healthFacilitySchema),
merchantInformationData: merchantInformationSchema,
rewardNight: rewardNightSchema,
pointsOfInterest: z
.array(pointOfInterestSchema)
@@ -480,6 +507,9 @@ export const getHotelDataSchema = z.object({
socialMedia: socialMediaSchema,
meta: metaSchema.optional(),
isActive: z.boolean(),
conferencesAndMeetings: facilitySchema.optional(),
healthAndWellness: facilitySchema.optional(),
restaurantImages: facilitySchema.optional(),
}),
relationships: relationshipsSchema,
}),
@@ -495,26 +525,18 @@ const occupancySchema = z.object({
const bestPricePerStaySchema = z.object({
currency: z.string(),
amount: z.number(),
regularAmount: z.number(),
memberAmount: z.number(),
discountRate: z.number(),
discountAmount: z.number(),
points: z.number(),
numberOfVouchers: z.number(),
numberOfBonusCheques: z.number(),
// TODO: remove optional when API is ready
regularAmount: z.string().optional(),
// TODO: remove optional when API is ready
memberAmount: z.string().optional(),
})
const bestPricePerNightSchema = z.object({
currency: z.string(),
amount: z.number(),
regularAmount: z.number(),
memberAmount: z.number(),
discountRate: z.number(),
discountAmount: z.number(),
points: z.number(),
numberOfVouchers: z.number(),
numberOfBonusCheques: z.number(),
// TODO: remove optional when API is ready
regularAmount: z.string().optional(),
// TODO: remove optional when API is ready
memberAmount: z.string().optional(),
})
const linksSchema = z.object({
@@ -526,7 +548,7 @@ const linksSchema = z.object({
),
})
const availabilitySchema = z.object({
const hotelsAvailabilitySchema = z.object({
data: z.array(
z.object({
attributes: z.object({
@@ -545,10 +567,10 @@ const availabilitySchema = z.object({
),
})
export const getAvailabilitySchema = availabilitySchema
export type Availability = z.infer<typeof availabilitySchema>
export type AvailabilityPrices =
Availability["data"][number]["attributes"]["bestPricePerNight"]
export const getHotelsAvailabilitySchema = hotelsAvailabilitySchema
export type HotelsAvailability = z.infer<typeof hotelsAvailabilitySchema>
export type HotelsAvailabilityPrices =
HotelsAvailability["data"][number]["attributes"]["bestPricePerNight"]
const flexibilityPrice = z.object({
standard: z.number(),

View File

@@ -20,14 +20,14 @@ import { toApiLang } from "@/server/utils"
import { hotelPageSchema } from "../contentstack/hotelPage/output"
import {
getAvailabilityInputSchema,
getHotelInputSchema,
getHotelsAvailabilityInputSchema,
getlHotelDataInputSchema,
getRatesInputSchema,
} from "./input"
import {
getAvailabilitySchema,
getHotelDataSchema,
getHotelsAvailabilitySchema,
getRatesSchema,
roomSchema,
} from "./output"
@@ -40,8 +40,10 @@ import {
TWENTYFOUR_HOURS,
} from "./utils"
import { FacilityEnum } from "@/types/components/hotelPage/facilities"
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
import type { RequestOptionsWithOutBody } from "@/types/fetch"
import type { Facility } from "@/types/hotel"
import type { GetHotelPageData } from "@/types/trpc/routers/contentstack/hotelPage"
const meter = metrics.getMeter("trpc.hotels")
@@ -49,12 +51,14 @@ const getHotelCounter = meter.createCounter("trpc.hotel.get")
const getHotelSuccessCounter = meter.createCounter("trpc.hotel.get-success")
const getHotelFailCounter = meter.createCounter("trpc.hotel.get-fail")
const availabilityCounter = meter.createCounter("trpc.hotel.availability")
const availabilitySuccessCounter = meter.createCounter(
"trpc.hotel.availability-success"
const hotelsAvailabilityCounter = meter.createCounter(
"trpc.hotel.availability.hotels"
)
const availabilityFailCounter = meter.createCounter(
"trpc.hotel.availability-fail"
const hotelsAvailabilitySuccessCounter = meter.createCounter(
"trpc.hotel.availability.hotels-success"
)
const hotelsAvailabilityFailCounter = meter.createCounter(
"trpc.hotel.availability.hotels-fail"
)
async function getContentstackData(
@@ -173,7 +177,6 @@ export const hotelQueryRouter = router({
const included = validatedHotelData.data.included || []
const hotelAttributes = validatedHotelData.data.data.attributes
const images = extractHotelImages(hotelAttributes)
const roomCategories = included
@@ -212,6 +215,21 @@ export const hotelQueryRouter = router({
? contentstackData?.content[0]
: null
const facilities: Facility[] = [
{
...apiJson.data.attributes.restaurantImages,
id: FacilityEnum.restaurant,
},
{
...apiJson.data.attributes.conferencesAndMeetings,
id: FacilityEnum.conference,
},
{
...apiJson.data.attributes.healthAndWellness,
id: FacilityEnum.wellness,
},
]
getHotelSuccessCounter.add(1, { hotelId, lang, include })
console.info(
"api.hotels.hotel success",
@@ -230,11 +248,12 @@ export const hotelQueryRouter = router({
pointsOfInterest: hotelAttributes.pointsOfInterest,
roomCategories,
activitiesCard: activities?.upcoming_activities_card,
facilities,
}
}),
availability: router({
get: hotelServiceProcedure
.input(getAvailabilityInputSchema)
hotels: hotelServiceProcedure
.input(getHotelsAvailabilityInputSchema)
.query(async ({ input, ctx }) => {
const {
cityId,
@@ -257,7 +276,7 @@ export const hotelQueryRouter = router({
attachedProfileId,
}
availabilityCounter.add(1, {
hotelsAvailabilityCounter.add(1, {
cityId,
roomStayStartDate,
roomStayEndDate,
@@ -267,11 +286,11 @@ export const hotelQueryRouter = router({
reservationProfileType,
})
console.info(
"api.hotels.availability start",
"api.hotels.hotelsAvailability start",
JSON.stringify({ query: { cityId, params } })
)
const apiResponse = await api.get(
`${api.endpoints.v1.availability}/${cityId}`,
`${api.endpoints.v1.hotelsAvailability}/${cityId}`,
{
headers: {
Authorization: `Bearer ${ctx.serviceToken}`,
@@ -281,7 +300,7 @@ export const hotelQueryRouter = router({
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
availabilityFailCounter.add(1, {
hotelsAvailabilityFailCounter.add(1, {
cityId,
roomStayStartDate,
roomStayEndDate,
@@ -297,7 +316,7 @@ export const hotelQueryRouter = router({
}),
})
console.error(
"api.hotels.availability error",
"api.hotels.hotelsAvailability error",
JSON.stringify({
query: { cityId, params },
error: {
@@ -311,9 +330,9 @@ export const hotelQueryRouter = router({
}
const apiJson = await apiResponse.json()
const validateAvailabilityData =
getAvailabilitySchema.safeParse(apiJson)
getHotelsAvailabilitySchema.safeParse(apiJson)
if (!validateAvailabilityData.success) {
availabilityFailCounter.add(1, {
hotelsAvailabilityFailCounter.add(1, {
cityId,
roomStayStartDate,
roomStayEndDate,
@@ -325,7 +344,7 @@ export const hotelQueryRouter = router({
error: JSON.stringify(validateAvailabilityData.error),
})
console.error(
"api.hotels.availability validation error",
"api.hotels.hotelsAvailability validation error",
JSON.stringify({
query: { cityId, params },
error: validateAvailabilityData.error,
@@ -333,7 +352,7 @@ export const hotelQueryRouter = router({
)
throw badRequestError()
}
availabilitySuccessCounter.add(1, {
hotelsAvailabilitySuccessCounter.add(1, {
cityId,
roomStayStartDate,
roomStayEndDate,
@@ -343,7 +362,7 @@ export const hotelQueryRouter = router({
reservationProfileType,
})
console.info(
"api.hotels.availability success",
"api.hotels.hotelsAvailability success",
JSON.stringify({
query: { cityId, params: params },
})

View File

@@ -1,5 +1,11 @@
import { metrics } from "@opentelemetry/api"
import { env } from "@/env/server"
import * as api from "@/lib/api"
import { initiateSaveCardSchema } from "@/server/routers/user/output"
import {
initiateSaveCardSchema,
subscriberIdSchema,
} from "@/server/routers/user/output"
import { protectedProcedure, router } from "@/server/trpc"
import {
@@ -8,6 +14,17 @@ import {
saveCreditCardInput,
} from "./input"
const meter = metrics.getMeter("trpc.user")
const generatePreferencesLinkCounter = meter.createCounter(
"trpc.user.generatePreferencesLink"
)
const generatePreferencesLinkSuccessCounter = meter.createCounter(
"trpc.user.generatePreferencesLink-success"
)
const generatePreferencesLinkFailCounter = meter.createCounter(
"trpc.user.generatePreferencesLink-fail"
)
export const userMutationRouter = router({
creditCard: router({
add: protectedProcedure.input(addCreditCardInput).mutation(async function ({
@@ -128,4 +145,62 @@ export const userMutationRouter = router({
return true
}),
}),
generatePreferencesLink: protectedProcedure.mutation(async function ({
ctx,
}) {
generatePreferencesLinkCounter.add(1)
const apiResponse = await api.get(api.endpoints.v1.subscriberId, {
headers: {
Authorization: `Bearer ${ctx.session.token.access_token}`,
},
})
if (!apiResponse.ok) {
const text = await apiResponse.text()
generatePreferencesLinkFailCounter.add(1, {
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
statusText: apiResponse.statusText,
text,
}),
})
console.error(
"api.user.subscriberId error ",
JSON.stringify({
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
text,
},
})
)
return null
}
const data = await apiResponse.json()
const validatedData = subscriberIdSchema.safeParse(data)
if (!validatedData.success) {
generatePreferencesLinkSuccessCounter.add(1, {
error_type: "validation_error",
error: JSON.stringify(validatedData.error),
})
console.error(
"api.user.generatePreferencesLink validation error",
JSON.stringify({
error: validatedData.error,
})
)
console.error(validatedData.error.format())
return null
}
const preferencesLink = new URL(env.SALESFORCE_PREFERENCE_BASE_URL)
preferencesLink.searchParams.set("subKey", validatedData.data.subscriberId)
generatePreferencesLinkSuccessCounter.add(1)
return preferencesLink.toString()
}),
})

View File

@@ -234,3 +234,7 @@ export const initiateSaveCardSchema = z.object({
type: z.string(),
}),
})
export const subscriberIdSchema = z.object({
subscriberId: z.string(),
})

View File

@@ -1,4 +1,5 @@
import { metrics } from "@opentelemetry/api"
import { SafeParseSuccess } from "zod"
import * as api from "@/lib/api"
import {
@@ -27,8 +28,8 @@ import type {
LoginType,
TrackingSDKUserData,
} from "@/types/components/tracking"
import { BlocksEnums } from "@/types/enums/blocks"
import { Transactions } from "@/types/enums/transactions"
import { User } from "@/types/user"
import type { MembershipLevel } from "@/constants/membershipLevels"
// OpenTelemetry metrics: User
@@ -161,6 +162,51 @@ export async function getVerifiedUser({ session }: { session: Session }) {
return verifiedData
}
function parsedUser(data: User, isMFA: boolean) {
const country = countries.find((c) => c.code === data.address.countryCode)
const user = {
address: {
city: data.address.city,
country: country?.name ?? "",
countryCode: data.address.countryCode,
streetAddress: data.address.streetAddress,
zipCode: data.address.zipCode,
},
dateOfBirth: data.dateOfBirth,
email: data.email,
firstName: data.firstName,
language: data.language,
lastName: data.lastName,
membership: getMembership(data.memberships),
memberships: data.memberships,
name: `${data.firstName} ${data.lastName}`,
phoneNumber: data.phoneNumber,
profileId: data.profileId,
}
if (!isMFA) {
if (user.address.city) {
user.address.city = maskValue.text(user.address.city)
}
if (user.address.streetAddress) {
user.address.streetAddress = maskValue.text(user.address.streetAddress)
}
user.address.zipCode = data.address?.zipCode
? maskValue.text(data.address.zipCode)
: ""
user.dateOfBirth = maskValue.all(user.dateOfBirth)
user.email = maskValue.email(user.email)
user.phoneNumber = user.phoneNumber ? maskValue.phone(user.phoneNumber) : ""
}
return user
}
export const userQueryRouter = router({
get: protectedProcedure
.use(async function (opts) {
@@ -184,57 +230,25 @@ export const userQueryRouter = router({
return data
}
const verifiedData = data
const country = countries.find(
(c) => c.code === verifiedData.data.address.countryCode
)
const user = {
address: {
city: verifiedData.data.address.city,
country: country?.name ?? "",
countryCode: verifiedData.data.address.countryCode,
streetAddress: verifiedData.data.address.streetAddress,
zipCode: verifiedData.data.address.zipCode,
},
dateOfBirth: verifiedData.data.dateOfBirth,
email: verifiedData.data.email,
firstName: verifiedData.data.firstName,
language: verifiedData.data.language,
lastName: verifiedData.data.lastName,
membership: getMembership(verifiedData.data.memberships),
memberships: verifiedData.data.memberships,
name: `${verifiedData.data.firstName} ${verifiedData.data.lastName}`,
phoneNumber: verifiedData.data.phoneNumber,
profileId: verifiedData.data.profileId,
}
if (!ctx.isMFA) {
if (user.address.city) {
user.address.city = maskValue.text(user.address.city)
}
if (user.address.streetAddress) {
user.address.streetAddress = maskValue.text(
user.address.streetAddress
)
}
user.address.zipCode = verifiedData.data.address?.zipCode
? maskValue.text(verifiedData.data.address.zipCode)
: ""
user.dateOfBirth = maskValue.all(user.dateOfBirth)
user.email = maskValue.email(user.email)
user.phoneNumber = user.phoneNumber
? maskValue.phone(user.phoneNumber)
: ""
}
return user
return parsedUser(data.data, ctx.isMFA)
}),
getSafely: safeProtectedProcedure.query(async function getUser({ ctx }) {
if (!ctx.session) {
return null
}
const data = await getVerifiedUser({ session: ctx.session })
if (!data) {
return null
}
if ("error" in data) {
return data
}
return parsedUser(data.data, true)
}),
name: safeProtectedProcedure.query(async function ({ ctx }) {
if (!ctx.session) {
return null

View File

@@ -121,29 +121,24 @@ export const safeProtectedProcedure = t.procedure.use(async function (opts) {
})
})
export const profileServiceProcedure = t.procedure.use(async (opts) => {
const { access_token } = await fetchServiceToken(["profile"])
if (!access_token) {
throw internalServerError("Failed to obtain profile service token")
}
return opts.next({
ctx: {
serviceToken: access_token,
},
function createServiceProcedure(serviceName: string) {
return t.procedure.use(async (opts) => {
const { access_token } = await fetchServiceToken([serviceName])
if (!access_token) {
throw internalServerError(`Failed to obtain ${serviceName} service token`)
}
return opts.next({
ctx: {
serviceToken: access_token,
},
})
})
})
}
export const bookingServiceProcedure = createServiceProcedure("booking")
export const hotelServiceProcedure = createServiceProcedure("hotel")
export const profileServiceProcedure = createServiceProcedure("profile")
export const hotelServiceProcedure = t.procedure.use(async (opts) => {
const { access_token } = await fetchServiceToken(["hotel"])
if (!access_token) {
throw internalServerError("Failed to obtain hotel service token")
}
return opts.next({
ctx: {
serviceToken: access_token,
},
})
})
export const serverActionProcedure = t.procedure.experimental_caller(
experimental_nextAppDirCaller({
createContext,