import { metrics } from "@opentelemetry/api" import * as api from "@/lib/api" import { GetRewards as GetRewards, GetRewardsRef as GetRewardsRef, } from "@/lib/graphql/Query/RewardsWithRedeem.graphql" import { request } from "@/lib/graphql/request" import { notFound } from "@/server/errors/trpc" import { getCacheClient } from "@/services/dataCache" import { generateLoyaltyConfigTag, generateRefsResponseTag, } from "@/utils/generateTag" import { rewardRefsSchema, validateApiAllTiersSchema, validateCmsRewardsSchema, } from "./output" import type { CMSRewardsResponse, GetRewardRefsSchema, } from "@/types/trpc/routers/contentstack/reward" 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" ) export const getAllCMSRewardRefsCounter = meter.createCounter( "trpc.contentstack.reward.all" ) export const getAllCMSRewardRefsFailCounter = meter.createCounter( "trpc.contentstack.reward.all-fail" ) export const getAllCMSRewardRefsSuccessCounter = meter.createCounter( "trpc.contentstack.reward.all-success" ) export function getUniqueRewardIds(rewardIds: string[]) { const uniqueRewardIds = new Set(rewardIds) return Array.from(uniqueRewardIds) } /** * Cached for 1 hour. */ export async function getCachedAllTierRewards(token: string) { const cacheClient = await getCacheClient() return await cacheClient.cacheOrGet( "getAllTierRewards", async () => { 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 }, "1h" ) } export async function getCmsRewards(lang: Lang, rewardIds: string[]) { const tags = rewardIds.map((id) => generateLoyaltyConfigTag(lang, "reward", id) ) getAllCMSRewardRefsCounter.add(1, { lang, rewardIds }) console.info( "contentstack.reward.refs start", JSON.stringify({ query: { lang, rewardIds }, }) ) const refsResponse = await request( GetRewardsRef, { locale: lang, rewardIds, }, { key: rewardIds.map((rewardId) => generateRefsResponseTag(lang, rewardId)), ttl: "max", } ) if (!refsResponse.data) { const notFoundError = notFound(refsResponse) getAllCMSRewardRefsFailCounter.add(1, { lang, rewardIds, error_type: "not_found", error: JSON.stringify({ code: notFoundError.code }), }) console.error( "contentstack.reward.refs not found error", JSON.stringify({ query: { lang, rewardIds }, error: { code: notFoundError.code }, }) ) throw notFoundError } const validatedRefsData = rewardRefsSchema.safeParse(refsResponse) if (!validatedRefsData.success) { getAllCMSRewardRefsFailCounter.add(1, { lang, rewardIds, error_type: "validation_error", error: JSON.stringify(validatedRefsData.error), }) console.error( "contentstack.reward.refs validation error", JSON.stringify({ query: { lang, rewardIds }, error: validatedRefsData.error, }) ) return null } getAllCMSRewardRefsSuccessCounter.add(1, { lang, rewardIds }) console.info( "contentstack.startPage.refs success", JSON.stringify({ query: { lang, rewardIds }, }) ) const cmsRewardsResponse = await request( GetRewards, { locale: lang, rewardIds, }, { key: tags, ttl: "max", } ) if (!cmsRewardsResponse.data) { getAllRewardFailCounter.add(1, { lang, error_type: "validation_error", error: JSON.stringify(cmsRewardsResponse.data), }) const notFoundError = notFound(cmsRewardsResponse) console.error( "contentstack.rewards not found error", JSON.stringify({ query: { locale: lang, rewardIds, }, error: { code: notFoundError.code }, }) ) throw notFoundError } const validatedCmsRewards = validateCmsRewardsSchema.safeParse(cmsRewardsResponse) if (!validatedCmsRewards.success) { getAllRewardFailCounter.add(1, { locale: lang, rewardIds, error_type: "validation_error", error: JSON.stringify(validatedCmsRewards.error), }) console.error(validatedCmsRewards.error) console.error( "contentstack.rewards validation error", JSON.stringify({ query: { locale: lang, rewardIds }, error: validatedCmsRewards.error, }) ) return null } return validatedCmsRewards.data }