Files
web/server/routers/contentstack/reward/utils.ts
Joakim Jäderberg 46ebbbba8f Merged in feature/sas-login (pull request #1256)
First steps towards the SAS partnership

* otp flow now pretends to do the linking

* Update LinkAccountForm header

* Update redirect times

* Clean up comments

* Set maxAge on sas cookies

* make all SAS routes protected

* Merge remote-tracking branch 'refs/remotes/origin/feature/sas-login' into feature/sas-login

* Require auth for sas link flow

* Fix resend otp

* Add error support to OneTimePasswordForm

* Add Sentry to SAS error boundary

* Move SAS_REQUEST_OTP_STATE_STORAGE_COOKIE_NAME

* Add missing translations

* Merge branch 'master' of bitbucket.org:scandic-swap/web into feature/sas-login

* Merge branch 'feature/sas-login' of bitbucket.org:scandic-swap/web into feature/sas-login

* Add TooManyCodesError component

* Refactor GenericError to support new errors

* Add FailedAttemptsError

* remove removed component <VWOScript/>

* Merge branch 'feature/sas-login' of bitbucket.org:scandic-swap/web into feature/sas-login

* remove local cookie-bot reference

* Fix sas campaign logo scaling

* feature toggle the SAS stuff

* Merge branch 'feature/sas-login' of bitbucket.org:scandic-swap/web into feature/sas-login

* fix: use env vars for SAS endpoints


Approved-by: Linus Flood
2025-02-05 14:43:14 +00:00

267 lines
7.3 KiB
TypeScript

import { metrics } from "@opentelemetry/api"
import { unstable_cache } from "next/cache"
import { env } from "@/env/server"
import * as api from "@/lib/api"
import { GetRewards } from "@/lib/graphql/Query/Rewards.graphql"
import { GetRewards as GetRewardsWithReedem } from "@/lib/graphql/Query/RewardsWithRedeem.graphql"
import { request } from "@/lib/graphql/request"
import { notFound } from "@/server/errors/trpc"
import { generateLoyaltyConfigTag } from "@/utils/generateTag"
import {
type CmsRewardsResponse,
type CmsRewardsWithRedeemResponse,
validateApiAllTiersSchema,
validateApiTierRewardsSchema,
validateCmsRewardsSchema,
validateCmsRewardsWithRedeemSchema,
} from "./output"
import type { Lang } from "@/constants/languages"
const meter = metrics.getMeter("trpc.reward")
export const getAllRewardCounter = meter.createCounter(
"trpc.contentstack.reward.all"
)
export const getAllRewardFailCounter = meter.createCounter(
"trpc.contentstack.reward.all-fail"
)
export const getAllRewardSuccessCounter = meter.createCounter(
"trpc.contentstack.reward.all-success"
)
export const getCurrentRewardCounter = meter.createCounter(
"trpc.contentstack.reward.current"
)
export const getCurrentRewardFailCounter = meter.createCounter(
"trpc.contentstack.reward.current-fail"
)
export const getCurrentRewardSuccessCounter = meter.createCounter(
"trpc.contentstack.reward.current-success"
)
export const getByLevelRewardCounter = meter.createCounter(
"trpc.contentstack.reward.byLevel"
)
export const getByLevelRewardFailCounter = meter.createCounter(
"trpc.contentstack.reward.byLevel-fail"
)
export const getByLevelRewardSuccessCounter = meter.createCounter(
"trpc.contentstack.reward.byLevel-success"
)
export const getUnwrapSurpriseCounter = meter.createCounter(
"trpc.contentstack.reward.unwrap"
)
export const getUnwrapSurpriseFailCounter = meter.createCounter(
"trpc.contentstack.reward.unwrap-fail"
)
export const getUnwrapSurpriseSuccessCounter = meter.createCounter(
"trpc.contentstack.reward.unwrap-success"
)
export const getRedeemCounter = meter.createCounter(
"trpc.contentstack.reward.redeem"
)
export const getRedeemFailCounter = meter.createCounter(
"trpc.contentstack.reward.redeem-fail"
)
export const getRedeemSuccessCounter = meter.createCounter(
"trpc.contentstack.reward.redeem-success"
)
const ONE_HOUR = 60 * 60
export function getUniqueRewardIds(rewardIds: string[]) {
const uniqueRewardIds = new Set(rewardIds)
return Array.from(uniqueRewardIds)
}
/**
* Uses the legacy profile/v1/Profile/tierRewards endpoint.
* TODO: Delete when the new endpoint is out in production.
*/
export const getAllCachedApiRewards = unstable_cache(
async function (token) {
const apiResponse = await api.get(api.endpoints.v1.Profile.tierRewards, {
headers: {
Authorization: `Bearer ${token}`,
},
})
if (!apiResponse.ok) {
const text = await apiResponse.text()
getAllRewardFailCounter.add(1, {
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
statusText: apiResponse.statusText,
text,
}),
})
console.error(
"api.rewards.tierRewards error ",
JSON.stringify({
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
text,
},
})
)
throw apiResponse
}
const data = await apiResponse.json()
const validatedApiTierRewards = validateApiTierRewardsSchema.safeParse(data)
if (!validatedApiTierRewards.success) {
getAllRewardFailCounter.add(1, {
error_type: "validation_error",
error: JSON.stringify(validatedApiTierRewards.error),
})
console.error(validatedApiTierRewards.error)
console.error(
"api.rewards validation error",
JSON.stringify({
error: validatedApiTierRewards.error,
})
)
throw validatedApiTierRewards.error
}
return validatedApiTierRewards.data
},
["getAllApiRewards"],
{ revalidate: ONE_HOUR }
)
/**
* Cached for 1 hour.
*/
export const getCachedAllTierRewards = unstable_cache(
async function (token) {
const apiResponse = await api.get(
api.endpoints.v1.Profile.Reward.allTiers,
{
headers: {
Authorization: `Bearer ${token}`,
},
}
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
getAllRewardFailCounter.add(1, {
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
statusText: apiResponse.statusText,
text,
}),
})
console.error(
"api.rewards.allTiers error ",
JSON.stringify({
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
text,
},
})
)
throw apiResponse
}
const data = await apiResponse.json()
const validatedApiAllTierRewards = validateApiAllTiersSchema.safeParse(data)
if (!validatedApiAllTierRewards.success) {
getAllRewardFailCounter.add(1, {
error_type: "validation_error",
error: JSON.stringify(validatedApiAllTierRewards.error),
})
console.error(validatedApiAllTierRewards.error)
console.error(
"api.rewards validation error",
JSON.stringify({
error: validatedApiAllTierRewards.error,
})
)
throw validatedApiAllTierRewards.error
}
return validatedApiAllTierRewards.data
},
["getApiAllTierRewards"],
{ revalidate: ONE_HOUR }
)
export async function getCmsRewards(locale: Lang, rewardIds: string[]) {
const tags = rewardIds.map((id) =>
generateLoyaltyConfigTag(locale, "reward", id)
)
const cmsRewardsResponse = env.USE_NEW_REWARD_MODEL
? await request<CmsRewardsWithRedeemResponse>(
GetRewardsWithReedem,
{
locale: locale,
rewardIds,
},
{ next: { tags }, cache: "force-cache" }
)
: await request<CmsRewardsResponse>(
GetRewards,
{
locale: locale,
rewardIds,
},
{ next: { tags }, cache: "force-cache" }
)
if (!cmsRewardsResponse.data) {
getAllRewardFailCounter.add(1, {
lang: locale,
error_type: "validation_error",
error: JSON.stringify(cmsRewardsResponse.data),
})
const notFoundError = notFound(cmsRewardsResponse)
console.error(
"contentstack.rewards not found error",
JSON.stringify({
query: {
locale,
rewardIds,
},
error: { code: notFoundError.code },
})
)
throw notFoundError
}
const validatedCmsRewards = env.USE_NEW_REWARD_MODEL
? validateCmsRewardsWithRedeemSchema.safeParse(cmsRewardsResponse)
: validateCmsRewardsSchema.safeParse(cmsRewardsResponse)
if (!validatedCmsRewards.success) {
getAllRewardFailCounter.add(1, {
locale,
rewardIds,
error_type: "validation_error",
error: JSON.stringify(validatedCmsRewards.error),
})
console.error(validatedCmsRewards.error)
console.error(
"contentstack.rewards validation error",
JSON.stringify({
query: { locale, rewardIds },
error: validatedCmsRewards.error,
})
)
return null
}
return validatedCmsRewards.data
}