Merged in feat/sw-1975-get-profile-v2 (pull request #1651)
Use get Profile V2 endpoint Approved-by: Linus Flood
This commit is contained in:
@@ -6,7 +6,6 @@ import { getVerifiedUser } from "@/server/routers/user/query"
|
||||
import { router, safeProtectedServiceProcedure } from "@/server/trpc"
|
||||
|
||||
import { isValidSession } from "@/utils/session"
|
||||
import { getFriendsMembership } from "@/utils/user"
|
||||
|
||||
import {
|
||||
addPackageInput,
|
||||
@@ -85,8 +84,7 @@ async function getMembershipNumber(
|
||||
return undefined
|
||||
}
|
||||
|
||||
const membership = getFriendsMembership(verifiedUser.data.memberships)
|
||||
return membership?.membershipNumber
|
||||
return verifiedUser.data.membershipNumber
|
||||
}
|
||||
|
||||
export const bookingMutationRouter = router({
|
||||
|
||||
@@ -7,20 +7,20 @@ import { getIntl } from "@/i18n"
|
||||
import { safeTry } from "@/utils/safeTry"
|
||||
import { getEurobonusMembership } from "@/utils/user"
|
||||
|
||||
import type { Membership } from "@/types/user"
|
||||
import type { UserLoyalty } from "@/types/user"
|
||||
import type { Lang } from "@/constants/languages"
|
||||
import type { MyPagesLink } from "./MyPagesLink"
|
||||
|
||||
export const getPrimaryLinks = cache(
|
||||
async ({
|
||||
lang,
|
||||
memberships,
|
||||
userLoyalty,
|
||||
}: {
|
||||
lang: Lang
|
||||
memberships: Membership[]
|
||||
userLoyalty: UserLoyalty
|
||||
}): Promise<MyPagesLink[]> => {
|
||||
const intl = await getIntl()
|
||||
const showSASLink = isScandicXSASActive(memberships)
|
||||
const showSASLink = isScandicXSASActive(userLoyalty)
|
||||
const [showTeamMemberLink] = await safeTry(showTeamMemberCard())
|
||||
|
||||
const menuItems: MyPagesLink[] = [
|
||||
@@ -66,8 +66,8 @@ export const getPrimaryLinks = cache(
|
||||
}
|
||||
)
|
||||
|
||||
const isScandicXSASActive = (memberships: Membership[]) => {
|
||||
const eurobonusMembership = getEurobonusMembership(memberships)
|
||||
const isScandicXSASActive = (loyalty: UserLoyalty) => {
|
||||
const eurobonusMembership = getEurobonusMembership(loyalty)
|
||||
const isLinked = Boolean(eurobonusMembership)
|
||||
|
||||
return env.SAS_ENABLED && isLinked
|
||||
|
||||
@@ -45,7 +45,7 @@ export const myPagesNavigation = safeProtectedProcedure
|
||||
}
|
||||
|
||||
const [primaryLinks, secondaryLinks] = await Promise.all([
|
||||
getPrimaryLinks({ lang, memberships: user.data.memberships }),
|
||||
getPrimaryLinks({ lang, userLoyalty: user.data.loyalty }),
|
||||
getSecondaryLinks({ lang }),
|
||||
])
|
||||
|
||||
|
||||
@@ -2,53 +2,85 @@ import { z } from "zod"
|
||||
|
||||
import { countriesMap } from "@/constants/countries"
|
||||
|
||||
import { getFriendsMembership, scandicMemberships } from "@/utils/user"
|
||||
import { getFriendsMembership } from "@/utils/user"
|
||||
|
||||
import { imageSchema } from "../hotels/schemas/image"
|
||||
|
||||
const scandicFriendsTier = z.enum(["L1", "L2", "L3", "L4", "L5", "L6", "L7"])
|
||||
const sasEurobonusTier = z.enum(["EBB", "EBS", "EBG", "EBD", "EBP"])
|
||||
|
||||
const commonMembershipSchema = z.object({
|
||||
currentPoints: z.number().optional(),
|
||||
expirationDate: z.string().optional(),
|
||||
membershipLevel: z.string().optional(),
|
||||
nextLevel: z.string().optional(),
|
||||
nightsToTopTier: z.number().optional(),
|
||||
pointsExpiryDate: z.string().optional(),
|
||||
pointsRequiredToNextlevel: z.number().optional(),
|
||||
pointsToExpire: z.number().optional(),
|
||||
tierExpirationDate: z.string().optional(),
|
||||
membershipNumber: z.string(),
|
||||
tierExpires: z.string(),
|
||||
memberSince: z.string().nullish(),
|
||||
})
|
||||
|
||||
const toLowerCaseString = z.string().transform((s) => s.toLowerCase())
|
||||
const membershipType = (membershipType: scandicMemberships) =>
|
||||
toLowerCaseString
|
||||
// The memberships enum is in lower case so this makes sure it will match regardless of casing in the API response.
|
||||
.pipe(z.literal(membershipType))
|
||||
|
||||
const friendsMembershipSchema = z
|
||||
// This prevents validation errors if the API returns an unhandled membership type
|
||||
const otherMembershipSchema = z
|
||||
.object({
|
||||
membershipType: membershipType(scandicMemberships.guestpr),
|
||||
membershipNumber: z.string(),
|
||||
memberSince: z.string(),
|
||||
// This ensures that `type` won't widen into "string", losing the literal types, when used in a union
|
||||
type: z.string().refine((val): val is string & {} => true),
|
||||
})
|
||||
.merge(commonMembershipSchema)
|
||||
|
||||
const otherMembershipSchema = z
|
||||
export const sasMembershipSchema = z
|
||||
.object({
|
||||
membershipType: toLowerCaseString,
|
||||
membershipNumber: z.string().optional(),
|
||||
memberSince: z.string().optional(),
|
||||
type: z.literal("SAS_EB"),
|
||||
tier: sasEurobonusTier,
|
||||
nextTier: sasEurobonusTier.nullish(),
|
||||
})
|
||||
.merge(commonMembershipSchema)
|
||||
|
||||
export const friendsMembershipSchema = z
|
||||
.object({
|
||||
type: z.literal("SCANDIC_NATIVE"),
|
||||
tier: scandicFriendsTier,
|
||||
nextTier: scandicFriendsTier.nullish(),
|
||||
pointsToNextTier: z.number().nullish(),
|
||||
nightsToTopTier: z.number().nullish(),
|
||||
})
|
||||
.merge(commonMembershipSchema)
|
||||
|
||||
export const membershipSchema = z.union([
|
||||
friendsMembershipSchema,
|
||||
sasMembershipSchema,
|
||||
otherMembershipSchema,
|
||||
])
|
||||
|
||||
const pointExpirationSchema = z.object({
|
||||
points: z.number().int(),
|
||||
expires: z.string(),
|
||||
})
|
||||
|
||||
export const userLoyaltySchema = z.object({
|
||||
memberships: z.array(membershipSchema),
|
||||
points: z.object({
|
||||
spendable: z.number().int(),
|
||||
earned: z.number().int(),
|
||||
spent: z.number().int(),
|
||||
}),
|
||||
tier: scandicFriendsTier,
|
||||
tierExpires: z.string(),
|
||||
tierBoostedBy: z.string().nullish(),
|
||||
pointExpirations: z.array(pointExpirationSchema),
|
||||
})
|
||||
|
||||
export const getUserSchema = z
|
||||
.object({
|
||||
data: z.object({
|
||||
attributes: z.object({
|
||||
dateOfBirth: z.string().optional().default("1900-01-01"),
|
||||
email: z.string().email(),
|
||||
firstName: z.string(),
|
||||
language: z
|
||||
.string()
|
||||
// Preserve Profile v1 formatting for now so it matches ApiLang enum
|
||||
.transform((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
||||
.optional(),
|
||||
lastName: z.string(),
|
||||
phoneNumber: z.string().optional(),
|
||||
profileId: z.string(),
|
||||
membershipNumber: z.string(),
|
||||
address: z
|
||||
.object({
|
||||
city: z.string().optional(),
|
||||
@@ -59,14 +91,7 @@ export const getUserSchema = z
|
||||
})
|
||||
.optional()
|
||||
.nullable(),
|
||||
dateOfBirth: z.string().optional().default("1900-01-01"),
|
||||
email: z.string().email(),
|
||||
firstName: z.string(),
|
||||
language: z.string().optional(),
|
||||
lastName: z.string(),
|
||||
memberships: z.array(membershipSchema),
|
||||
phoneNumber: z.string().optional(),
|
||||
profileId: z.string(),
|
||||
loyalty: userLoyaltySchema,
|
||||
}),
|
||||
type: z.string(),
|
||||
}),
|
||||
@@ -74,7 +99,7 @@ export const getUserSchema = z
|
||||
.transform((apiResponse) => {
|
||||
return {
|
||||
...apiResponse.data.attributes,
|
||||
membership: getFriendsMembership(apiResponse.data.attributes.memberships),
|
||||
membership: getFriendsMembership(apiResponse.data.attributes.loyalty),
|
||||
name: `${apiResponse.data.attributes.firstName} ${apiResponse.data.attributes.lastName}`,
|
||||
}
|
||||
})
|
||||
@@ -224,16 +249,6 @@ export const creditCardsSchema = z.object({
|
||||
data: z.array(creditCardSchema),
|
||||
})
|
||||
|
||||
export const getMembershipCardsSchema = z.array(
|
||||
z.object({
|
||||
currentPoints: z.number(),
|
||||
expirationDate: z.string(),
|
||||
membershipNumber: z.string(),
|
||||
memberSince: z.string(),
|
||||
membershipType: z.string(),
|
||||
})
|
||||
)
|
||||
|
||||
export const initiateSaveCardSchema = z.object({
|
||||
data: z.object({
|
||||
attribute: z.object({
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
import {
|
||||
creditCardsSchema,
|
||||
getFriendTransactionsSchema,
|
||||
getMembershipCardsSchema,
|
||||
getStaysSchema,
|
||||
getUserSchema,
|
||||
} from "./output"
|
||||
@@ -37,7 +36,6 @@ import type {
|
||||
} from "@/types/components/tracking"
|
||||
import { Transactions } from "@/types/enums/transactions"
|
||||
import type { User } from "@/types/user"
|
||||
import type { MembershipLevel } from "@/constants/membershipLevels"
|
||||
|
||||
// OpenTelemetry metrics: User
|
||||
const meter = metrics.getMeter("trpc.user")
|
||||
@@ -46,10 +44,6 @@ const getVerifiedUserSuccessCounter = meter.createCounter(
|
||||
"trpc.user.get-success"
|
||||
)
|
||||
const getVerifiedUserFailCounter = meter.createCounter("trpc.user.get-fail")
|
||||
const getProfileSuccessCounter = meter.createCounter(
|
||||
"trpc.user.profile-success"
|
||||
)
|
||||
const getProfileFailCounter = meter.createCounter("trpc.user.profile-fail")
|
||||
|
||||
// OpenTelemetry metrics: Stays
|
||||
const getPreviousStaysCounter = meter.createCounter("trpc.user.stays.previous")
|
||||
@@ -95,7 +89,7 @@ export const getVerifiedUser = cache(
|
||||
}
|
||||
getVerifiedUserCounter.add(1)
|
||||
console.info("api.user.profile getVerifiedUser start", JSON.stringify({}))
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.profile, {
|
||||
const apiResponse = await api.get(api.endpoints.v2.Profile.profile, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${session.token.access_token}`,
|
||||
},
|
||||
@@ -186,8 +180,9 @@ export function parsedUser(data: User, isMFA: boolean) {
|
||||
firstName: data.firstName,
|
||||
language: data.language,
|
||||
lastName: data.lastName,
|
||||
membership: getFriendsMembership(data.memberships),
|
||||
memberships: data.memberships,
|
||||
membershipNumber: data.membershipNumber,
|
||||
membership: getFriendsMembership(data.loyalty),
|
||||
loyalty: data.loyalty,
|
||||
name: `${data.firstName} ${data.lastName}`,
|
||||
phoneNumber: data.phoneNumber,
|
||||
profileId: data.profileId,
|
||||
@@ -343,7 +338,7 @@ export const userQueryRouter = router({
|
||||
return null
|
||||
}
|
||||
|
||||
const membershipLevel = getFriendsMembership(verifiedData.data.memberships)
|
||||
const membershipLevel = getFriendsMembership(verifiedData.data.loyalty)
|
||||
return membershipLevel
|
||||
}),
|
||||
safeMembershipLevel: safeProtectedProcedure.query(async function ({ ctx }) {
|
||||
@@ -356,7 +351,7 @@ export const userQueryRouter = router({
|
||||
return null
|
||||
}
|
||||
|
||||
const membershipLevel = getFriendsMembership(verifiedData.data.memberships)
|
||||
const membershipLevel = getFriendsMembership(verifiedData.data.loyalty)
|
||||
return membershipLevel
|
||||
}),
|
||||
userTrackingInfo: safeProtectedProcedure.query(async function ({ ctx }) {
|
||||
@@ -429,14 +424,14 @@ export const userQueryRouter = router({
|
||||
getPreviousStaysSuccessCounter.add(1)
|
||||
console.info("api.booking.stays.past success", JSON.stringify({}))
|
||||
|
||||
const membership = getFriendsMembership(verifiedUserData.data.memberships)
|
||||
const membership = getFriendsMembership(verifiedUserData.data.loyalty)
|
||||
|
||||
const loggedInUserTrackingData: TrackingSDKUserData = {
|
||||
loginStatus: "logged in",
|
||||
loginType: ctx.session.token.loginType as LoginType,
|
||||
memberId: verifiedUserData.data.profileId,
|
||||
membershipNumber: membership?.membershipNumber,
|
||||
memberLevel: membership?.membershipLevel as MembershipLevel,
|
||||
memberLevel: membership?.membershipLevel,
|
||||
noOfNightsStayed: verifiedPreviousStaysData.data.links?.totalCount ?? 0,
|
||||
totalPointsAvailableToSpend: membership?.currentPoints,
|
||||
loginAction: "login success",
|
||||
@@ -795,23 +790,6 @@ export const userQueryRouter = router({
|
||||
return null
|
||||
}
|
||||
|
||||
const verifiedData = getMembershipCardsSchema.safeParse(
|
||||
userData.data.memberships
|
||||
)
|
||||
|
||||
if (!verifiedData.success) {
|
||||
getProfileFailCounter.add(1, {
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(verifiedData),
|
||||
})
|
||||
console.error(
|
||||
"api.profile validation error",
|
||||
JSON.stringify({ error: verifiedData })
|
||||
)
|
||||
return null
|
||||
}
|
||||
getProfileSuccessCounter.add(1)
|
||||
|
||||
return getMembershipCards(verifiedData.data)
|
||||
return getMembershipCards(userData.data.loyalty)
|
||||
}),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user