feat: harmonize log and metrics
This commit is contained in:
@@ -2,6 +2,7 @@ import { type NextRequest, NextResponse } from "next/server"
|
||||
|
||||
import { env } from "@/env/server"
|
||||
import { dt } from "@/lib/dt"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
|
||||
import {
|
||||
getEntries,
|
||||
@@ -13,22 +14,16 @@ import {
|
||||
} from "@/utils/sitemap"
|
||||
|
||||
import { contentstackSync } from "./sync"
|
||||
import {
|
||||
generateSitemapCounter,
|
||||
generateSitemapFailCounter,
|
||||
generateSitemapSuccessCounter,
|
||||
saveEntriesCounter,
|
||||
saveSitemapDataCounter,
|
||||
saveSyncTokenCounter,
|
||||
} from "./telemetry"
|
||||
import { mapEntriesToSitemapData, mergeEntries } from "./utils"
|
||||
|
||||
export const dynamic = "force-dynamic"
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const generateSitemapCounter = createCounter("sitemap", "generate")
|
||||
const metricsGenerateSitemap = generateSitemapCounter.init()
|
||||
try {
|
||||
generateSitemapCounter.add(1)
|
||||
console.info("sitemap.generate start")
|
||||
metricsGenerateSitemap.start()
|
||||
|
||||
const headersList = request.headers
|
||||
const secret = headersList.get("x-sitemap-sync-secret")
|
||||
|
||||
@@ -44,49 +39,46 @@ export async function GET(request: NextRequest) {
|
||||
const responseData = await contentstackSync(syncToken)
|
||||
const mergedEntries = mergeEntries(currentEntries, responseData.entries)
|
||||
|
||||
saveEntriesCounter.add(1, { entriesCount: mergedEntries.length })
|
||||
console.info(
|
||||
"sitemap.entries.save",
|
||||
JSON.stringify({ entriesCount: mergedEntries.length })
|
||||
)
|
||||
const entriesSaveCounter = createCounter("sitemap", "entries.save")
|
||||
const metricsEntriesSave = entriesSaveCounter.init({
|
||||
entriesCount: mergedEntries.length,
|
||||
})
|
||||
|
||||
metricsEntriesSave.start()
|
||||
await saveEntries(mergedEntries)
|
||||
metricsEntriesSave.success()
|
||||
|
||||
const sitemapData = mapEntriesToSitemapData(mergedEntries)
|
||||
const lastUpdated = dt().utc().format()
|
||||
saveSitemapDataCounter.add(1, {
|
||||
|
||||
const saveDataCounter = createCounter("sitemap", "data.save")
|
||||
const metricsDataSave = saveDataCounter.init({
|
||||
sitemapEntriesCount: sitemapData.length,
|
||||
lastUpdated,
|
||||
})
|
||||
console.info(
|
||||
"sitemap.data.save",
|
||||
JSON.stringify({
|
||||
sitemapEntriesCount: sitemapData.length,
|
||||
lastUpdated,
|
||||
})
|
||||
)
|
||||
metricsDataSave.start()
|
||||
await saveSitemapData(sitemapData)
|
||||
await saveLastUpdatedDate(lastUpdated)
|
||||
metricsDataSave.success()
|
||||
|
||||
if (syncToken !== responseData.syncToken) {
|
||||
saveSyncTokenCounter.add(1, {
|
||||
const syncTokenSaveCounter = createCounter("sitemap", "syncToken.save")
|
||||
const metricsSyncTokenSave = syncTokenSaveCounter.init({
|
||||
syncToken: responseData.syncToken,
|
||||
})
|
||||
console.info(
|
||||
"sitemap.synctoken.save",
|
||||
JSON.stringify({ syncToken: responseData.syncToken })
|
||||
)
|
||||
metricsSyncTokenSave.start()
|
||||
await saveSyncToken(responseData.syncToken)
|
||||
metricsSyncTokenSave.success()
|
||||
}
|
||||
|
||||
generateSitemapSuccessCounter.add(1)
|
||||
metricsGenerateSitemap.success()
|
||||
|
||||
return NextResponse.json({
|
||||
message: "Sitemap data generated and stored successfully!",
|
||||
now: dt().utc().format(),
|
||||
})
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : JSON.stringify(error)
|
||||
generateSitemapFailCounter.add(1, { error: errorMessage })
|
||||
console.error("sitemap.generate.fail", errorMessage)
|
||||
metricsGenerateSitemap.fail(error)
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
import { Region, Stack } from "contentstack"
|
||||
|
||||
import { env } from "@/env/server"
|
||||
|
||||
import {
|
||||
syncEntriesCounter,
|
||||
syncEntriesFailCounter,
|
||||
syncEntriesPaginationCounter,
|
||||
syncEntriesSuccessCounter,
|
||||
} from "./telemetry"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
|
||||
import type { SyncResponse } from "@/types/sitemap"
|
||||
|
||||
@@ -24,72 +18,50 @@ export async function contentstackSync(syncToken: string | null) {
|
||||
const entries = []
|
||||
const syncOptions = syncToken ? { sync_token: syncToken } : { init: true }
|
||||
|
||||
syncEntriesCounter.add(1, {
|
||||
const entriesSyncCounter = createCounter("sitemap", "entries.sync")
|
||||
const metricsEntriesSync = entriesSyncCounter.init({
|
||||
environment,
|
||||
...syncOptions,
|
||||
})
|
||||
console.info(
|
||||
"sitemap.entries.sync start",
|
||||
JSON.stringify({
|
||||
environment,
|
||||
...syncOptions,
|
||||
})
|
||||
)
|
||||
|
||||
metricsEntriesSync.start()
|
||||
|
||||
try {
|
||||
let syncResponse: SyncResponse = await stack.sync(syncOptions)
|
||||
|
||||
entries.push(...syncResponse.items)
|
||||
|
||||
const entriesSyncPaginationCounter = createCounter(
|
||||
"sitemap",
|
||||
"entries.sync.pagination"
|
||||
)
|
||||
const metricsEntriesSyncPagination = entriesSyncPaginationCounter.init({
|
||||
environment,
|
||||
...syncOptions,
|
||||
})
|
||||
// Check if there is a pagination token, and fetch more data if needed
|
||||
while (syncResponse.pagination_token && !syncResponse.sync_token) {
|
||||
syncEntriesPaginationCounter.add(1, {
|
||||
environment,
|
||||
metricsEntriesSyncPagination.start({
|
||||
paginationToken: syncResponse.pagination_token,
|
||||
})
|
||||
console.info(
|
||||
"sitemap.entries.sync.pagination start",
|
||||
JSON.stringify({
|
||||
environment,
|
||||
paginationToken: syncResponse.pagination_token,
|
||||
})
|
||||
)
|
||||
|
||||
syncResponse = await stack.sync({
|
||||
pagination_token: syncResponse.pagination_token,
|
||||
})
|
||||
|
||||
entries.push(...syncResponse.items)
|
||||
|
||||
syncEntriesPaginationCounter.add(1, {
|
||||
environment,
|
||||
metricsEntriesSyncPagination.success({
|
||||
paginationToken: syncResponse.pagination_token,
|
||||
entriesCount: syncResponse.items.length,
|
||||
})
|
||||
console.info(
|
||||
"sitemap.entries.sync.pagination success",
|
||||
JSON.stringify({
|
||||
environment,
|
||||
paginationToken: syncResponse.pagination_token,
|
||||
entriesCount: syncResponse.items.length,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (syncResponse.sync_token) {
|
||||
syncEntriesSuccessCounter.add(1, {
|
||||
environment,
|
||||
...syncOptions,
|
||||
metricsEntriesSync.success({
|
||||
newSyncToken: syncResponse.sync_token,
|
||||
entriesCount: entries.length,
|
||||
})
|
||||
console.info(
|
||||
"sitemap.entries.sync success",
|
||||
JSON.stringify({
|
||||
environment,
|
||||
...syncOptions,
|
||||
newSyncToken: syncResponse.sync_token,
|
||||
entriesCount: entries.length,
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
syncToken: syncResponse.sync_token,
|
||||
entries,
|
||||
@@ -98,14 +70,7 @@ export async function contentstackSync(syncToken: string | null) {
|
||||
throw new Error("No sync token received, something went wrong")
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : JSON.stringify(error)
|
||||
syncEntriesFailCounter.add(1, {
|
||||
environment,
|
||||
...syncOptions,
|
||||
error: errorMessage,
|
||||
})
|
||||
console.error("sitemap.entries.sync error", errorMessage)
|
||||
metricsEntriesSync.fail(error)
|
||||
|
||||
throw new Error("Failed to sync entries")
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
const meter = metrics.getMeter("sitemap")
|
||||
|
||||
// OpenTelemetry metrics
|
||||
export const generateSitemapCounter = meter.createCounter("sitemap.generate")
|
||||
export const generateSitemapSuccessCounter = meter.createCounter(
|
||||
"sitemap.generate-success"
|
||||
)
|
||||
export const generateSitemapFailCounter = meter.createCounter(
|
||||
"sitemap.generate-fail"
|
||||
)
|
||||
|
||||
export const syncEntriesCounter = meter.createCounter("sitemap.entries.sync")
|
||||
export const syncEntriesSuccessCounter = meter.createCounter(
|
||||
"sitemap.entries.sync-success"
|
||||
)
|
||||
export const syncEntriesFailCounter = meter.createCounter(
|
||||
"sitemap.entries.sync-fail"
|
||||
)
|
||||
export const syncEntriesPaginationCounter = meter.createCounter(
|
||||
"sitemap.entries.sync.pagination"
|
||||
)
|
||||
export const syncEntriesPaginationSuccessCounter = meter.createCounter(
|
||||
"sitemap.entries.sync.pagination-success"
|
||||
)
|
||||
|
||||
export const mergeEntriesCounter = meter.createCounter("sitemap.entries.merge")
|
||||
export const mergeEntriesSuccessCounter = meter.createCounter(
|
||||
"sitemap.entries.merge-success"
|
||||
)
|
||||
export const saveEntriesCounter = meter.createCounter("sitemap.entries.save")
|
||||
|
||||
export const transformEntriesCounter = meter.createCounter(
|
||||
"sitemap.entries.transform"
|
||||
)
|
||||
export const transformEntriesSuccessCounter = meter.createCounter(
|
||||
"sitemap.entries.transform-success"
|
||||
)
|
||||
export const saveSitemapDataCounter = meter.createCounter("sitemap.data.save")
|
||||
export const saveSyncTokenCounter = meter.createCounter(
|
||||
"sitemap.synctoken.save"
|
||||
)
|
||||
@@ -1,32 +1,23 @@
|
||||
import { Lang } from "@/constants/languages"
|
||||
import { env } from "@/env/server"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
|
||||
import { removeTrailingSlash } from "@/utils/url"
|
||||
|
||||
import {
|
||||
mergeEntriesCounter,
|
||||
mergeEntriesSuccessCounter,
|
||||
transformEntriesCounter,
|
||||
transformEntriesSuccessCounter,
|
||||
} from "./telemetry"
|
||||
|
||||
import type { SitemapEntry, SyncItem } from "@/types/sitemap"
|
||||
|
||||
export function mergeEntries(
|
||||
currentEntries: SyncItem[],
|
||||
newEntries: SyncItem[]
|
||||
) {
|
||||
mergeEntriesCounter.add(1, {
|
||||
const entriesMergeCounter = createCounter("sitemap", "entries.merge")
|
||||
const metricsEntriesMerge = entriesMergeCounter.init({
|
||||
currentEntriesCount: currentEntries.length,
|
||||
newEntriesCount: newEntries.length,
|
||||
})
|
||||
console.info(
|
||||
"sitemap.entries.merge start",
|
||||
JSON.stringify({
|
||||
currentEntriesCount: currentEntries.length,
|
||||
newEntriesCount: newEntries.length,
|
||||
})
|
||||
)
|
||||
|
||||
metricsEntriesMerge.start()
|
||||
|
||||
const entries = [...currentEntries]
|
||||
newEntries.forEach((entry) => {
|
||||
const index = entries.findIndex(
|
||||
@@ -40,27 +31,20 @@ export function mergeEntries(
|
||||
}
|
||||
})
|
||||
|
||||
mergeEntriesSuccessCounter.add(1, {
|
||||
metricsEntriesMerge.success({
|
||||
entriesCount: entries.length,
|
||||
})
|
||||
console.info(
|
||||
"sitemap.entries.merge success",
|
||||
JSON.stringify({
|
||||
entriesCount: entries.length,
|
||||
})
|
||||
)
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
export function mapEntriesToSitemapData(entries: SyncItem[]) {
|
||||
transformEntriesCounter.add(1, { entriesCount: entries.length })
|
||||
console.info(
|
||||
"sitemap.entries.transform start",
|
||||
JSON.stringify({
|
||||
entriesCount: entries.length,
|
||||
})
|
||||
)
|
||||
const entriesTransformCounter = createCounter("sitemap", "entries.transform")
|
||||
const metricsEntriesTransform = entriesTransformCounter.init({
|
||||
entriesCount: entries.length,
|
||||
})
|
||||
|
||||
metricsEntriesTransform.start()
|
||||
|
||||
const filteredEntries = filterEntriesToSitemapEntries(entries)
|
||||
|
||||
@@ -69,17 +53,10 @@ export function mapEntriesToSitemapData(entries: SyncItem[]) {
|
||||
.map(([_, entries]) => mapEntriesToSitemapEntry(entries))
|
||||
.filter((entry): entry is SitemapEntry => !!entry)
|
||||
|
||||
transformEntriesSuccessCounter.add(1, {
|
||||
metricsEntriesTransform.success({
|
||||
entriesCount: entries.length,
|
||||
sitemapEntriesCount: sitemapEntries.length,
|
||||
})
|
||||
console.info(
|
||||
"sitemap.entries.transform success",
|
||||
JSON.stringify({
|
||||
entriesCount: entries.length,
|
||||
sitemapEntriesCount: sitemapEntries.length,
|
||||
})
|
||||
)
|
||||
|
||||
return sitemapEntries
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ export default function HotelPin({
|
||||
)}
|
||||
</span>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
||||
<p>{isNotAvailable ? "—" : formatPrice(intl, hotelPrice, currency)}</p>
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
@@ -3,15 +3,18 @@
|
||||
* https://jestjs.io/docs/configuration
|
||||
*/
|
||||
import nextJest from "next/jest.js"
|
||||
import { createJsWithTsEsmPreset } from "ts-jest"
|
||||
|
||||
import type { Config } from "jest"
|
||||
|
||||
const presetConfig = createJsWithTsEsmPreset()
|
||||
|
||||
const createJestConfig = nextJest({
|
||||
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
|
||||
dir: "./",
|
||||
})
|
||||
|
||||
const config: Config = {
|
||||
const config = {
|
||||
// All imported modules in your tests should be mocked automatically
|
||||
// automock: false,
|
||||
|
||||
@@ -202,6 +205,8 @@ const config: Config = {
|
||||
|
||||
// Whether to use watchman for file crawling
|
||||
// watchman: true,
|
||||
}
|
||||
|
||||
...presetConfig,
|
||||
} satisfies Config
|
||||
|
||||
export default createJestConfig(config)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import "@testing-library/jest-dom/jest-globals"
|
||||
import "@testing-library/jest-dom"
|
||||
|
||||
import { jest } from "@jest/globals"
|
||||
|
||||
jest.mock("next/navigation", () => ({
|
||||
useRouter: jest.fn(),
|
||||
usePathname: jest.fn().mockReturnValue("/"),
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
"test:e2e:headless": "start-server-and-test test:setup http://127.0.0.1:3000/en/sponsoring \"cypress run --e2e\"",
|
||||
"test:setup": "yarn build && yarn start",
|
||||
"preinstall": "/bin/sh -c \"export $(cat .env.local | grep -v '^#' | xargs)\"",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test": "node --experimental-vm-modules $(yarn bin jest)",
|
||||
"test:watch": "node --experimental-vm-modules $(yarn bin jest) --watch",
|
||||
"ci:build": "yarn lint && yarn test && yarn build",
|
||||
"clean": "rm -rf .next",
|
||||
"i18n:extract": "formatjs extract \"{actions,app,components,constants,hooks,i18n,lib,middlewares,server,stores,utils}/**/*.{ts,tsx}\" --format i18n/tooling/formatter.mjs --out-file i18n/tooling/extracted.json",
|
||||
@@ -76,6 +76,7 @@
|
||||
"embla-carousel-react": "^8.5.2",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fetch-retry": "^6.0.0",
|
||||
"flat": "^6.0.1",
|
||||
"framer-motion": "^11.3.28",
|
||||
"fuse.js": "^7.1.0",
|
||||
"graphql": "^16.8.1",
|
||||
@@ -88,6 +89,7 @@
|
||||
"ioredis": "^5.5.0",
|
||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||
"libphonenumber-js": "^1.10.60",
|
||||
"lodash-es": "^4.17.21",
|
||||
"nanoid": "^5.0.9",
|
||||
"next": "^14.2.25",
|
||||
"next-auth": "5.0.0-beta.19",
|
||||
@@ -101,7 +103,6 @@
|
||||
"react-international-phone": "^4.2.6",
|
||||
"react-intl": "^6.6.8",
|
||||
"react-to-print": "^3.0.2",
|
||||
"secure-json-parse": "^4.0.0",
|
||||
"server-only": "^0.0.1",
|
||||
"slugify": "^1.6.6",
|
||||
"sonner": "^1.7.0",
|
||||
@@ -124,6 +125,7 @@
|
||||
"@types/adm-zip": "^0.5.7",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/json-stable-stringify-without-jsonify": "^1.0.2",
|
||||
"@types/lodash-es": "^4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
@@ -148,6 +150,7 @@
|
||||
"react-material-symbols": "^4.4.0",
|
||||
"schema-dts": "^1.1.2",
|
||||
"start-server-and-test": "^2.0.3",
|
||||
"ts-jest": "^29.3.2",
|
||||
"ts-morph": "^25.0.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "5.4.5",
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
import sjson from "secure-json-parse"
|
||||
|
||||
import * as api from "@/lib/api"
|
||||
import { getVerifiedUser } from "@/server/routers/user/query"
|
||||
import { getMembershipNumber } from "@/server/routers/user/utils"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import { router, safeProtectedServiceProcedure } from "@/server/trpc"
|
||||
|
||||
import { isValidSession } from "@/utils/session"
|
||||
|
||||
import {
|
||||
addPackageInput,
|
||||
cancelBookingInput,
|
||||
@@ -18,75 +14,6 @@ import {
|
||||
} from "./input"
|
||||
import { bookingConfirmationSchema, 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 guaranteeBookingCounter = meter.createCounter("trpc.bookings.guarantee")
|
||||
const guaranteeBookingSuccessCounter = meter.createCounter(
|
||||
"trpc.bookings.guarantee-success"
|
||||
)
|
||||
const guaranteeBookingFailCounter = meter.createCounter(
|
||||
"trpc.bookings.guarantee-fail"
|
||||
)
|
||||
const updateBookingCounter = meter.createCounter("trpc.bookings.update-booking")
|
||||
const updateBookingSuccessCounter = meter.createCounter(
|
||||
"trpc.bookings.update-booking-success"
|
||||
)
|
||||
const updateBookingFailCounter = meter.createCounter(
|
||||
"trpc.bookings.update-booking-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 (!isValidSession(session)) return undefined
|
||||
|
||||
const verifiedUser = await getVerifiedUser({ session })
|
||||
if (!verifiedUser || "error" in verifiedUser) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return verifiedUser.data.membershipNumber
|
||||
}
|
||||
|
||||
export const bookingMutationRouter = router({
|
||||
create: safeProtectedServiceProcedure
|
||||
.input(createBookingInput)
|
||||
@@ -95,22 +22,17 @@ export const bookingMutationRouter = router({
|
||||
const { language, ...inputWithoutLang } = input
|
||||
const { hotelId, checkInDate, checkOutDate } = inputWithoutLang
|
||||
|
||||
const loggingAttributes = {
|
||||
const createBookingCounter = createCounter("trpc.booking", "create")
|
||||
const metricsCreateBooking = createBookingCounter.init({
|
||||
membershipNumber: await getMembershipNumber(ctx.session),
|
||||
checkInDate,
|
||||
checkOutDate,
|
||||
hotelId,
|
||||
language,
|
||||
}
|
||||
})
|
||||
|
||||
createBookingCounter.add(1, loggingAttributes)
|
||||
metricsCreateBooking.start()
|
||||
|
||||
console.info(
|
||||
"api.booking.create start",
|
||||
JSON.stringify({
|
||||
query: loggingAttributes,
|
||||
})
|
||||
)
|
||||
const headers = {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
}
|
||||
@@ -125,29 +47,9 @@ export const bookingMutationRouter = router({
|
||||
)
|
||||
|
||||
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,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
await metricsCreateBooking.httpError(apiResponse)
|
||||
|
||||
const apiJson = sjson.safeParse(text)
|
||||
const apiJson = await apiResponse.json()
|
||||
if ("errors" in apiJson && apiJson.errors.length) {
|
||||
const error = apiJson.errors[0]
|
||||
return { error: true, cause: error.code } as const
|
||||
@@ -160,45 +62,25 @@ export const bookingMutationRouter = router({
|
||||
|
||||
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,
|
||||
})
|
||||
)
|
||||
metricsCreateBooking.validationError(verifiedData.error)
|
||||
return null
|
||||
}
|
||||
|
||||
createBookingSuccessCounter.add(1, {
|
||||
hotelId,
|
||||
checkInDate,
|
||||
checkOutDate,
|
||||
})
|
||||
|
||||
console.info(
|
||||
"api.booking.create success",
|
||||
JSON.stringify({
|
||||
query: loggingAttributes,
|
||||
})
|
||||
)
|
||||
metricsCreateBooking.success()
|
||||
|
||||
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 priceChangeCounter = createCounter("trpc.booking", "price-change")
|
||||
const metricsPriceChange = priceChangeCounter.init({ confirmationNumber })
|
||||
|
||||
metricsPriceChange.start()
|
||||
|
||||
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
|
||||
|
||||
const headers = {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
@@ -213,46 +95,18 @@ export const bookingMutationRouter = router({
|
||||
)
|
||||
|
||||
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,
|
||||
},
|
||||
})
|
||||
)
|
||||
await metricsPriceChange.httpError(apiResponse)
|
||||
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,
|
||||
})
|
||||
)
|
||||
metricsPriceChange.validationError(verifiedData.error)
|
||||
return null
|
||||
}
|
||||
|
||||
priceChangeSuccessCounter.add(1, { confirmationNumber })
|
||||
metricsPriceChange.success()
|
||||
|
||||
return verifiedData.data
|
||||
}),
|
||||
@@ -262,6 +116,14 @@ export const bookingMutationRouter = router({
|
||||
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
|
||||
const { confirmationNumber, language } = input
|
||||
|
||||
const cancelBookingCounter = createCounter("trpc.booking", "cancel")
|
||||
const metricsCancelBooking = cancelBookingCounter.init({
|
||||
confirmationNumber,
|
||||
language,
|
||||
})
|
||||
|
||||
metricsCancelBooking.start()
|
||||
|
||||
const headers = {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
}
|
||||
@@ -271,21 +133,6 @@ export const bookingMutationRouter = router({
|
||||
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),
|
||||
{
|
||||
@@ -296,25 +143,7 @@ export const bookingMutationRouter = router({
|
||||
)
|
||||
|
||||
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,
|
||||
})
|
||||
)
|
||||
await metricsCancelBooking.httpError(apiResponse)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -323,29 +152,11 @@ export const bookingMutationRouter = router({
|
||||
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,
|
||||
})
|
||||
)
|
||||
metricsCancelBooking.validationError(verifiedData.error)
|
||||
return null
|
||||
}
|
||||
|
||||
cancelBookingSuccessCounter.add(1, loggingAttributes)
|
||||
|
||||
console.info(
|
||||
"api.booking.cancel success",
|
||||
JSON.stringify({
|
||||
query: loggingAttributes,
|
||||
})
|
||||
)
|
||||
metricsCancelBooking.success()
|
||||
|
||||
return verifiedData.data
|
||||
}),
|
||||
@@ -355,7 +166,10 @@ export const bookingMutationRouter = router({
|
||||
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
|
||||
const { confirmationNumber, ...body } = input
|
||||
|
||||
addPackageCounter.add(1, { confirmationNumber })
|
||||
const addPackageCounter = createCounter("trpc.booking", "package.add")
|
||||
const metricsAddPackage = addPackageCounter.init({ confirmationNumber })
|
||||
|
||||
metricsAddPackage.start()
|
||||
|
||||
const headers = {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
@@ -370,46 +184,18 @@ export const bookingMutationRouter = router({
|
||||
)
|
||||
|
||||
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,
|
||||
},
|
||||
})
|
||||
)
|
||||
await metricsAddPackage.httpError(apiResponse)
|
||||
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,
|
||||
})
|
||||
)
|
||||
metricsAddPackage.validationError(verifiedData.error)
|
||||
return null
|
||||
}
|
||||
|
||||
addPackageSuccessCounter.add(1, { confirmationNumber })
|
||||
metricsAddPackage.success()
|
||||
|
||||
return verifiedData.data
|
||||
}),
|
||||
@@ -419,7 +205,12 @@ export const bookingMutationRouter = router({
|
||||
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
|
||||
const { confirmationNumber, language, ...body } = input
|
||||
|
||||
guaranteeBookingCounter.add(1, { confirmationNumber })
|
||||
const guaranteeBookingCounter = createCounter("trpc.booking", "guarantee")
|
||||
const metricsGuaranteeBooking = guaranteeBookingCounter.init({
|
||||
confirmationNumber,
|
||||
})
|
||||
|
||||
metricsGuaranteeBooking.start()
|
||||
|
||||
const headers = {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
@@ -435,46 +226,18 @@ export const bookingMutationRouter = router({
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
guaranteeBookingFailCounter.add(1, {
|
||||
confirmationNumber,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.booking.guarantee error",
|
||||
JSON.stringify({
|
||||
query: { confirmationNumber },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
error: text,
|
||||
},
|
||||
})
|
||||
)
|
||||
await metricsGuaranteeBooking.httpError(apiResponse)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const verifiedData = createBookingSchema.safeParse(apiJson)
|
||||
if (!verifiedData.success) {
|
||||
guaranteeBookingFailCounter.add(1, {
|
||||
confirmationNumber,
|
||||
error_type: "validation_error",
|
||||
})
|
||||
console.error(
|
||||
"api.booking.guarantee validation error",
|
||||
JSON.stringify({
|
||||
query: { confirmationNumber },
|
||||
error: verifiedData.error,
|
||||
})
|
||||
)
|
||||
metricsGuaranteeBooking.validationError(verifiedData.error)
|
||||
return null
|
||||
}
|
||||
|
||||
guaranteeBookingSuccessCounter.add(1, { confirmationNumber })
|
||||
metricsGuaranteeBooking.success()
|
||||
|
||||
return verifiedData.data
|
||||
}),
|
||||
@@ -484,7 +247,12 @@ export const bookingMutationRouter = router({
|
||||
const accessToken = ctx.session?.token.access_token || ctx.serviceToken
|
||||
const { confirmationNumber, ...body } = input
|
||||
|
||||
updateBookingCounter.add(1, { confirmationNumber })
|
||||
const updateBookingCounter = createCounter("trpc.booking", "update")
|
||||
const metricsUpdateBooking = updateBookingCounter.init({
|
||||
confirmationNumber,
|
||||
})
|
||||
|
||||
metricsUpdateBooking.start()
|
||||
|
||||
const apiResponse = await api.put(
|
||||
api.endpoints.v1.Booking.booking(confirmationNumber),
|
||||
@@ -497,25 +265,7 @@ export const bookingMutationRouter = router({
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
updateBookingFailCounter.add(1, {
|
||||
confirmationNumber,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.booking.updateBooking error",
|
||||
JSON.stringify({
|
||||
query: { confirmationNumber },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
error: text,
|
||||
},
|
||||
})
|
||||
)
|
||||
await metricsUpdateBooking.httpError(apiResponse)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -523,21 +273,11 @@ export const bookingMutationRouter = router({
|
||||
|
||||
const verifiedData = bookingConfirmationSchema.safeParse(apiJson)
|
||||
if (!verifiedData.success) {
|
||||
updateBookingFailCounter.add(1, {
|
||||
confirmationNumber,
|
||||
error_type: "validation_error",
|
||||
})
|
||||
console.error(
|
||||
"api.booking.updateBooking validation error",
|
||||
JSON.stringify({
|
||||
query: { confirmationNumber },
|
||||
error: verifiedData.error,
|
||||
})
|
||||
)
|
||||
metricsUpdateBooking.validationError(verifiedData.error)
|
||||
return null
|
||||
}
|
||||
|
||||
updateBookingSuccessCounter.add(1, { confirmationNumber })
|
||||
metricsUpdateBooking.success()
|
||||
|
||||
return verifiedData.data
|
||||
}),
|
||||
@@ -547,26 +287,22 @@ export const bookingMutationRouter = router({
|
||||
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
|
||||
const { confirmationNumber, codes, language } = input
|
||||
|
||||
const headers = {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
}
|
||||
|
||||
const loggingAttributes = {
|
||||
const removePackageCounter = createCounter(
|
||||
"trpc.booking",
|
||||
"package.remove"
|
||||
)
|
||||
const metricsRemovePackage = removePackageCounter.init({
|
||||
confirmationNumber,
|
||||
codes,
|
||||
language,
|
||||
})
|
||||
|
||||
metricsRemovePackage.start()
|
||||
|
||||
const headers = {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
}
|
||||
|
||||
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),
|
||||
{
|
||||
@@ -576,36 +312,11 @@ export const bookingMutationRouter = router({
|
||||
)
|
||||
|
||||
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,
|
||||
})
|
||||
)
|
||||
await metricsRemovePackage.httpError(apiResponse)
|
||||
return false
|
||||
}
|
||||
|
||||
removePackageSuccessCounter.add(1, loggingAttributes)
|
||||
|
||||
console.info(
|
||||
"api.booking.remove-package success",
|
||||
JSON.stringify({
|
||||
query: loggingAttributes,
|
||||
})
|
||||
)
|
||||
metricsRemovePackage.success()
|
||||
|
||||
return true
|
||||
}),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
import * as api from "@/lib/api"
|
||||
import { badRequestError, serverErrorByStatus } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import {
|
||||
router,
|
||||
safeProtectedServiceProcedure,
|
||||
@@ -18,19 +17,6 @@ import {
|
||||
import { bookingConfirmationSchema, createBookingSchema } from "./output"
|
||||
import { getBookedHotelRoom } from "./utils"
|
||||
|
||||
const meter = metrics.getMeter("trpc.booking")
|
||||
const getBookingCounter = meter.createCounter("trpc.booking.get")
|
||||
const getBookingSuccessCounter = meter.createCounter("trpc.booking.get-success")
|
||||
const getBookingFailCounter = meter.createCounter("trpc.booking.get-fail")
|
||||
|
||||
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({
|
||||
get: safeProtectedServiceProcedure
|
||||
.input(getBookingInput)
|
||||
@@ -38,7 +24,10 @@ export const bookingQueryRouter = router({
|
||||
ctx,
|
||||
input: { confirmationNumber, lang: inputLang },
|
||||
}) {
|
||||
getBookingCounter.add(1, { confirmationNumber })
|
||||
const getBookingCounter = createCounter("trpc.booking", "get")
|
||||
const metricsGetBooking = getBookingCounter.init({ confirmationNumber })
|
||||
|
||||
metricsGetBooking.start()
|
||||
|
||||
let lang = ctx.lang ?? inputLang
|
||||
|
||||
@@ -54,23 +43,7 @@ export const bookingQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const responseMessage = await apiResponse.text()
|
||||
getBookingFailCounter.add(1, {
|
||||
confirmationNumber,
|
||||
error_type: "http_error",
|
||||
error: responseMessage,
|
||||
})
|
||||
console.error(
|
||||
"api.booking.confirmation error",
|
||||
JSON.stringify({
|
||||
query: { confirmationNumber },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text: responseMessage,
|
||||
},
|
||||
})
|
||||
)
|
||||
await metricsGetBooking.httpError(apiResponse)
|
||||
|
||||
// If the booking is not found, return null.
|
||||
// This scenario is expected to happen when a logged in user trying to access a booking that doesn't belong to them.
|
||||
@@ -84,18 +57,7 @@ export const bookingQueryRouter = router({
|
||||
const apiJson = await apiResponse.json()
|
||||
const booking = bookingConfirmationSchema.safeParse(apiJson)
|
||||
if (!booking.success) {
|
||||
getBookingFailCounter.add(1, {
|
||||
confirmationNumber,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(booking.error),
|
||||
})
|
||||
console.error(
|
||||
"api.booking.confirmation validation error",
|
||||
JSON.stringify({
|
||||
query: { confirmationNumber },
|
||||
error: booking.error,
|
||||
})
|
||||
)
|
||||
metricsGetBooking.validationError(booking.error)
|
||||
throw badRequestError()
|
||||
}
|
||||
|
||||
@@ -109,34 +71,17 @@ export const bookingQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!hotelData) {
|
||||
getBookingFailCounter.add(1, {
|
||||
confirmationNumber,
|
||||
hotelId: booking.data.hotelId,
|
||||
error_type: "http_error",
|
||||
error: "Couldn`t get hotel",
|
||||
})
|
||||
console.error(
|
||||
"api.booking.confirmation error",
|
||||
JSON.stringify({
|
||||
query: { confirmationNumber, hotelId: booking.data.hotelId },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text: "Couldn`t get hotel",
|
||||
},
|
||||
})
|
||||
metricsGetBooking.dataError(
|
||||
`Failed to get hotel data for ${booking.data.hotelId}`,
|
||||
{
|
||||
hotelId: booking.data.hotelId,
|
||||
}
|
||||
)
|
||||
|
||||
throw serverErrorByStatus(404)
|
||||
}
|
||||
|
||||
getBookingSuccessCounter.add(1, { confirmationNumber })
|
||||
console.info(
|
||||
"api.booking.confirmation success",
|
||||
JSON.stringify({
|
||||
query: { confirmationNumber },
|
||||
})
|
||||
)
|
||||
metricsGetBooking.success()
|
||||
|
||||
return {
|
||||
...hotelData,
|
||||
@@ -152,7 +97,13 @@ export const bookingQueryRouter = router({
|
||||
input,
|
||||
}) {
|
||||
const { confirmationNumber } = input
|
||||
getBookingStatusCounter.add(1, { confirmationNumber })
|
||||
|
||||
const getBookingStatusCounter = createCounter("trpc.booking", "status")
|
||||
const metricsGetBookingStatus = getBookingStatusCounter.init({
|
||||
confirmationNumber,
|
||||
})
|
||||
|
||||
metricsGetBookingStatus.start()
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Booking.status(confirmationNumber),
|
||||
@@ -164,52 +115,18 @@ export const bookingQueryRouter = router({
|
||||
)
|
||||
|
||||
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,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
await metricsGetBookingStatus.httpError(apiResponse)
|
||||
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,
|
||||
})
|
||||
)
|
||||
metricsGetBookingStatus.validationError(verifiedData.error)
|
||||
throw badRequestError()
|
||||
}
|
||||
|
||||
getBookingStatusSuccessCounter.add(1, { confirmationNumber })
|
||||
console.info(
|
||||
"api.booking.status success",
|
||||
JSON.stringify({
|
||||
query: { confirmationNumber },
|
||||
})
|
||||
)
|
||||
metricsGetBookingStatus.success()
|
||||
|
||||
return verifiedData.data
|
||||
}),
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
import {
|
||||
GetAccountPage,
|
||||
GetAccountPageRefs,
|
||||
} from "@/lib/graphql/Query/AccountPage/AccountPage.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
||||
|
||||
import {
|
||||
@@ -26,36 +25,17 @@ import type {
|
||||
GetAccountPageSchema,
|
||||
} from "@/types/trpc/routers/contentstack/accountPage"
|
||||
|
||||
const meter = metrics.getMeter("trpc.accountPage")
|
||||
|
||||
// OpenTelemetry metrics
|
||||
const getAccountPageRefsCounter = meter.createCounter(
|
||||
"trpc.contentstack.accountPage.get"
|
||||
)
|
||||
const getAccountPageRefsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.accountPage.get-success"
|
||||
)
|
||||
const getAccountPageRefsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.accountPage.get-fail"
|
||||
)
|
||||
const getAccountPageCounter = meter.createCounter(
|
||||
"trpc.contentstack.accountPage.get"
|
||||
)
|
||||
const getAccountPageSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.accountPage.get-success"
|
||||
)
|
||||
const getAccountPageFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.accountPage.get-fail"
|
||||
)
|
||||
|
||||
export const accountPageQueryRouter = router({
|
||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
||||
const { lang, uid } = ctx
|
||||
getAccountPageRefsCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.accountPage.refs start",
|
||||
JSON.stringify({ query: { lang, uid } })
|
||||
|
||||
const getAccountPageRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"accountPage.get.refs"
|
||||
)
|
||||
const metricsRefs = getAccountPageRefsCounter.init({ lang, uid })
|
||||
metricsRefs.start()
|
||||
|
||||
const refsResponse = await request<GetAccountPageRefsSchema>(
|
||||
GetAccountPageRefs,
|
||||
{
|
||||
@@ -70,19 +50,7 @@ export const accountPageQueryRouter = router({
|
||||
|
||||
if (!refsResponse.data) {
|
||||
const notFoundError = notFound(refsResponse)
|
||||
getAccountPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.accountPage.refs not found error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsRefs.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -90,19 +58,7 @@ export const accountPageQueryRouter = router({
|
||||
refsResponse.data
|
||||
)
|
||||
if (!validatedAccountPageRefs.success) {
|
||||
getAccountPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedAccountPageRefs.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.accountPage.refs validation error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: validatedAccountPageRefs.error,
|
||||
})
|
||||
)
|
||||
metricsRefs.validationError(validatedAccountPageRefs.error)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -112,12 +68,16 @@ export const accountPageQueryRouter = router({
|
||||
generateTagsFromSystem(lang, connections),
|
||||
generateTag(lang, validatedAccountPageRefs.data.account_page.system.uid),
|
||||
].flat()
|
||||
getAccountPageRefsSuccessCounter.add(1, { lang, uid, tags })
|
||||
getAccountPageCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.accountPage start",
|
||||
JSON.stringify({ query: { lang, uid } })
|
||||
|
||||
metricsRefs.success()
|
||||
|
||||
const getAccountPageCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"accountPage.get"
|
||||
)
|
||||
const metrics = getAccountPageCounter.init({ lang, uid })
|
||||
metrics.start()
|
||||
|
||||
const response = await request<GetAccountPageSchema>(
|
||||
GetAccountPage,
|
||||
{
|
||||
@@ -132,45 +92,18 @@ export const accountPageQueryRouter = router({
|
||||
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
getAccountPageFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.accountPage not found error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metrics.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const validatedAccountPage = accountPageSchema.safeParse(response.data)
|
||||
|
||||
if (!validatedAccountPage.success) {
|
||||
getAccountPageFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedAccountPage.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.accountPage validation error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: validatedAccountPage.error,
|
||||
})
|
||||
)
|
||||
metrics.validationError(validatedAccountPage.error)
|
||||
return null
|
||||
}
|
||||
getAccountPageSuccessCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.accountPage success",
|
||||
JSON.stringify({ query: { lang, uid } })
|
||||
)
|
||||
|
||||
metrics.success()
|
||||
|
||||
const parsedtitle = response.data.account_page.title
|
||||
.replaceAll(" ", "")
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
} from "@/lib/graphql/Query/SiteConfig.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import { contentstackBaseProcedure, router } from "@/server/trpc"
|
||||
import { langInput } from "@/server/utils"
|
||||
|
||||
@@ -43,37 +44,6 @@ import {
|
||||
validateFooterConfigSchema,
|
||||
validateFooterRefConfigSchema,
|
||||
} from "./output"
|
||||
import {
|
||||
getContactConfigCounter,
|
||||
getContactConfigFailCounter,
|
||||
getContactConfigSuccessCounter,
|
||||
getCurrentFooterCounter,
|
||||
getCurrentFooterFailCounter,
|
||||
getCurrentFooterRefCounter,
|
||||
getCurrentFooterSuccessCounter,
|
||||
getCurrentHeaderCounter,
|
||||
getCurrentHeaderFailCounter,
|
||||
getCurrentHeaderRefCounter,
|
||||
getCurrentHeaderSuccessCounter,
|
||||
getFooterCounter,
|
||||
getFooterFailCounter,
|
||||
getFooterRefCounter,
|
||||
getFooterRefFailCounter,
|
||||
getFooterRefSuccessCounter,
|
||||
getFooterSuccessCounter,
|
||||
getHeaderCounter,
|
||||
getHeaderFailCounter,
|
||||
getHeaderRefsCounter,
|
||||
getHeaderRefsFailCounter,
|
||||
getHeaderRefsSuccessCounter,
|
||||
getHeaderSuccessCounter,
|
||||
getSiteConfigCounter,
|
||||
getSiteConfigFailCounter,
|
||||
getSiteConfigRefCounter,
|
||||
getSiteConfigRefFailCounter,
|
||||
getSiteConfigRefSuccessCounter,
|
||||
getSiteConfigSuccessCounter,
|
||||
} from "./telemetry"
|
||||
import {
|
||||
getAlertPhoneContactData,
|
||||
getConnections,
|
||||
@@ -96,11 +66,14 @@ import type {
|
||||
import type { Lang } from "@/constants/languages"
|
||||
|
||||
const getContactConfig = cache(async (lang: Lang) => {
|
||||
getContactConfigCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.contactConfig start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
const getContactConfigCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"contactConfig.get"
|
||||
)
|
||||
const metricsGetContactConfig = getContactConfigCounter.init({ lang })
|
||||
|
||||
metricsGetContactConfig.start()
|
||||
|
||||
const response = await request<ContactConfigData>(
|
||||
GetContactConfig,
|
||||
{
|
||||
@@ -114,46 +87,20 @@ const getContactConfig = cache(async (lang: Lang) => {
|
||||
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
|
||||
getContactConfigFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
|
||||
console.error(
|
||||
"contentstack.config not found error",
|
||||
JSON.stringify({ query: { lang }, error: { code: notFoundError.code } })
|
||||
)
|
||||
|
||||
metricsGetContactConfig.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const validatedContactConfigConfig = validateContactConfigSchema.safeParse(
|
||||
response.data
|
||||
)
|
||||
const verifiedData = validateContactConfigSchema.safeParse(response.data)
|
||||
|
||||
if (!validatedContactConfigConfig.success) {
|
||||
getContactConfigFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedContactConfigConfig.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.contactConfig validation error",
|
||||
JSON.stringify({
|
||||
query: { lang },
|
||||
error: validatedContactConfigConfig.error,
|
||||
})
|
||||
)
|
||||
if (!verifiedData.success) {
|
||||
metricsGetContactConfig.validationError(verifiedData.error)
|
||||
return null
|
||||
}
|
||||
getContactConfigSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.contactConfig success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
return validatedContactConfigConfig.data.all_contact_config.items[0]
|
||||
|
||||
metricsGetContactConfig.success()
|
||||
|
||||
return verifiedData.data.all_contact_config.items[0]
|
||||
})
|
||||
|
||||
export const baseQueryRouter = router({
|
||||
@@ -162,11 +109,14 @@ export const baseQueryRouter = router({
|
||||
}),
|
||||
header: contentstackBaseProcedure.query(async ({ ctx }) => {
|
||||
const { lang } = ctx
|
||||
getHeaderRefsCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.header.refs start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
|
||||
const getHeaderRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"header.get.refs"
|
||||
)
|
||||
const metricsGetHeaderRefs = getHeaderRefsCounter.init({ lang })
|
||||
|
||||
metricsGetHeaderRefs.start()
|
||||
|
||||
const responseRef = await request<GetHeaderRefs>(
|
||||
GetHeaderRef,
|
||||
@@ -181,56 +131,25 @@ export const baseQueryRouter = router({
|
||||
|
||||
if (!responseRef.data) {
|
||||
const notFoundError = notFound(responseRef)
|
||||
getHeaderRefsFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.header.refs not found error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang,
|
||||
},
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetHeaderRefs.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const validatedHeaderRefs = headerRefsSchema.safeParse(responseRef.data)
|
||||
|
||||
if (!validatedHeaderRefs.success) {
|
||||
getHeaderRefsFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedHeaderRefs.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.header.refs validation error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang,
|
||||
},
|
||||
error: validatedHeaderRefs.error,
|
||||
})
|
||||
)
|
||||
metricsGetHeaderRefs.validationError(validatedHeaderRefs.error)
|
||||
return null
|
||||
}
|
||||
|
||||
getHeaderRefsSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.header.refs success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
metricsGetHeaderRefs.success()
|
||||
|
||||
const connections = getConnections(validatedHeaderRefs.data)
|
||||
|
||||
getHeaderCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.header start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
const getHeaderCounter = createCounter("trpc.contentstack", "header.get")
|
||||
const metricsGetHeader = getHeaderCounter.init({ lang })
|
||||
|
||||
metricsGetHeader.start()
|
||||
|
||||
const tags = [
|
||||
generateTagsFromSystem(lang, connections),
|
||||
@@ -245,43 +164,18 @@ export const baseQueryRouter = router({
|
||||
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
getHeaderFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.header not found error",
|
||||
JSON.stringify({
|
||||
query: { lang },
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetHeader.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const validatedHeaderConfig = headerSchema.safeParse(response.data)
|
||||
|
||||
if (!validatedHeaderConfig.success) {
|
||||
getHeaderFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedHeaderConfig.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.header validation error",
|
||||
JSON.stringify({
|
||||
query: { lang },
|
||||
error: validatedHeaderConfig.error,
|
||||
})
|
||||
)
|
||||
metricsGetHeader.validationError(validatedHeaderConfig.error)
|
||||
return null
|
||||
}
|
||||
getHeaderSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.header success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
|
||||
metricsGetHeader.success()
|
||||
|
||||
return {
|
||||
data: validatedHeaderConfig.data.header,
|
||||
@@ -290,11 +184,16 @@ export const baseQueryRouter = router({
|
||||
currentHeader: contentstackBaseProcedure
|
||||
.input(langInput)
|
||||
.query(async ({ input }) => {
|
||||
getCurrentHeaderRefCounter.add(1, { lang: input.lang })
|
||||
console.info(
|
||||
"contentstack.currentHeader.ref start",
|
||||
JSON.stringify({ query: { lang: input.lang } })
|
||||
const getCurrentHeaderRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"currentHeader.get.refs"
|
||||
)
|
||||
const metricsGetCurrentHeaderRefs = getCurrentHeaderRefsCounter.init({
|
||||
lang: input.lang,
|
||||
})
|
||||
|
||||
metricsGetCurrentHeaderRefs.start()
|
||||
|
||||
const responseRef = await request<CurrentHeaderRefDataRaw>(
|
||||
GetCurrentHeaderRef,
|
||||
{
|
||||
@@ -305,13 +204,16 @@ export const baseQueryRouter = router({
|
||||
ttl: "max",
|
||||
}
|
||||
)
|
||||
getCurrentHeaderCounter.add(1, { lang: input.lang })
|
||||
console.info(
|
||||
"contentstack.currentHeader start",
|
||||
JSON.stringify({
|
||||
query: { lang: input.lang },
|
||||
})
|
||||
|
||||
const getCurrentHeaderCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"currentHeader.get"
|
||||
)
|
||||
const metricsGetCurrentHeader = getCurrentHeaderCounter.init({
|
||||
lang: input.lang,
|
||||
})
|
||||
|
||||
metricsGetCurrentHeader.start()
|
||||
|
||||
const currentHeaderUID =
|
||||
responseRef.data.all_current_header.items[0].system.uid
|
||||
@@ -327,20 +229,7 @@ export const baseQueryRouter = router({
|
||||
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
getCurrentHeaderFailCounter.add(1, {
|
||||
lang: input.lang,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.currentHeader not found error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang: input.lang,
|
||||
},
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetCurrentHeader.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -349,40 +238,27 @@ export const baseQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!validatedHeaderConfig.success) {
|
||||
getCurrentHeaderFailCounter.add(1, {
|
||||
lang: input.lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedHeaderConfig.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.currentHeader validation error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang: input.lang,
|
||||
},
|
||||
error: validatedHeaderConfig.error,
|
||||
})
|
||||
)
|
||||
metricsGetCurrentHeader.validationError(validatedHeaderConfig.error)
|
||||
return null
|
||||
}
|
||||
getCurrentHeaderSuccessCounter.add(1, { lang: input.lang })
|
||||
console.info(
|
||||
"contentstack.currentHeader success",
|
||||
JSON.stringify({
|
||||
query: { lang: input.lang },
|
||||
})
|
||||
)
|
||||
|
||||
metricsGetCurrentHeader.success()
|
||||
|
||||
return validatedHeaderConfig.data
|
||||
}),
|
||||
currentFooter: contentstackBaseProcedure
|
||||
.input(langInput)
|
||||
.query(async ({ input }) => {
|
||||
getCurrentFooterRefCounter.add(1, { lang: input.lang })
|
||||
console.info(
|
||||
"contentstack.currentFooter.ref start",
|
||||
JSON.stringify({ query: { lang: input.lang } })
|
||||
const getCurrentFooterRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"currentFooter.get.refs"
|
||||
)
|
||||
const metricsGetCurrentFooterRefs = getCurrentFooterRefsCounter.init({
|
||||
lang: input.lang,
|
||||
})
|
||||
|
||||
metricsGetCurrentFooterRefs.start()
|
||||
|
||||
const responseRef = await request<CurrentFooterRefDataRaw>(
|
||||
GetCurrentFooterRef,
|
||||
{
|
||||
@@ -393,16 +269,17 @@ export const baseQueryRouter = router({
|
||||
ttl: "max",
|
||||
}
|
||||
)
|
||||
// There's currently no error handling/validation for the responseRef, should it be added?
|
||||
getCurrentFooterCounter.add(1, { lang: input.lang })
|
||||
console.info(
|
||||
"contentstack.currentFooter start",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang: input.lang,
|
||||
},
|
||||
})
|
||||
|
||||
const getCurrentFooterCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"currentFooter.get"
|
||||
)
|
||||
const metricsGetCurrentFooter = getCurrentFooterCounter.init({
|
||||
lang: input.lang,
|
||||
})
|
||||
|
||||
metricsGetCurrentFooter.start()
|
||||
|
||||
const currentFooterUID =
|
||||
responseRef.data.all_current_footer.items[0].system.uid
|
||||
|
||||
@@ -419,20 +296,7 @@ export const baseQueryRouter = router({
|
||||
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
getCurrentFooterFailCounter.add(1, {
|
||||
lang: input.lang,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.currentFooter not found error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang: input.lang,
|
||||
},
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetCurrentFooter.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -440,34 +304,27 @@ export const baseQueryRouter = router({
|
||||
validateCurrentFooterConfigSchema.safeParse(response.data)
|
||||
|
||||
if (!validatedCurrentFooterConfig.success) {
|
||||
getFooterFailCounter.add(1, {
|
||||
lang: input.lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedCurrentFooterConfig.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.currentFooter validation error",
|
||||
JSON.stringify({
|
||||
query: { lang: input.lang },
|
||||
error: validatedCurrentFooterConfig.error,
|
||||
})
|
||||
metricsGetCurrentFooter.validationError(
|
||||
validatedCurrentFooterConfig.error
|
||||
)
|
||||
return null
|
||||
}
|
||||
getCurrentFooterSuccessCounter.add(1, { lang: input.lang })
|
||||
console.info(
|
||||
"contentstack.currentFooter success",
|
||||
JSON.stringify({ query: { lang: input.lang } })
|
||||
)
|
||||
|
||||
metricsGetCurrentFooter.success()
|
||||
|
||||
return validatedCurrentFooterConfig.data.all_current_footer.items[0]
|
||||
}),
|
||||
footer: contentstackBaseProcedure.query(async ({ ctx }) => {
|
||||
const { lang } = ctx
|
||||
getFooterRefCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.footer.ref start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
|
||||
const getFooterRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"footer.get.refs"
|
||||
)
|
||||
const metricsGetFooterRefs = getFooterRefsCounter.init({ lang })
|
||||
|
||||
metricsGetFooterRefs.start()
|
||||
|
||||
const responseRef = await request<FooterRefDataRaw>(
|
||||
GetFooterRef,
|
||||
{
|
||||
@@ -481,20 +338,7 @@ export const baseQueryRouter = router({
|
||||
|
||||
if (!responseRef.data) {
|
||||
const notFoundError = notFound(responseRef)
|
||||
getFooterRefFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.footer.refs not found error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang,
|
||||
},
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetFooterRefs.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -503,41 +347,20 @@ export const baseQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!validatedFooterRefs.success) {
|
||||
getFooterRefFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedFooterRefs.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.footer.refs validation error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang,
|
||||
},
|
||||
error: validatedFooterRefs.error,
|
||||
})
|
||||
)
|
||||
metricsGetFooterRefs.validationError(validatedFooterRefs.error)
|
||||
return null
|
||||
}
|
||||
|
||||
getFooterRefSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.footer.refs success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
metricsGetFooterRefs.success()
|
||||
|
||||
const connections = getFooterConnections(validatedFooterRefs.data)
|
||||
const footerUID = responseRef.data.all_footer.items[0].system.uid
|
||||
|
||||
getFooterCounter.add(1, { lang: lang })
|
||||
console.info(
|
||||
"contentstack.footer start",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang,
|
||||
},
|
||||
})
|
||||
)
|
||||
const getFooterCounter = createCounter("trpc.contentstack", "footer.get")
|
||||
const metricsGetFooter = getFooterCounter.init({ lang })
|
||||
|
||||
metricsGetFooter.start()
|
||||
|
||||
const tags = [
|
||||
generateTags(lang, connections),
|
||||
generateTag(lang, footerUID),
|
||||
@@ -556,20 +379,7 @@ export const baseQueryRouter = router({
|
||||
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
getFooterFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.footer not found error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang,
|
||||
},
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetFooter.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -578,25 +388,11 @@ export const baseQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!validatedFooterConfig.success) {
|
||||
getFooterFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedFooterConfig.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.footer validation error",
|
||||
JSON.stringify({
|
||||
query: { lang: lang },
|
||||
error: validatedFooterConfig.error,
|
||||
})
|
||||
)
|
||||
metricsGetFooter.validationError(validatedFooterConfig.error)
|
||||
return null
|
||||
}
|
||||
getFooterSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.footer success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
|
||||
metricsGetFooter.success()
|
||||
|
||||
return validatedFooterConfig.data
|
||||
}),
|
||||
@@ -605,11 +401,14 @@ export const baseQueryRouter = router({
|
||||
.query(async ({ input, ctx }) => {
|
||||
const lang = input.lang ?? ctx.lang
|
||||
|
||||
getSiteConfigRefCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.siteConfig.ref start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
const getSiteConfigRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"siteConfig.get.refs"
|
||||
)
|
||||
const metricsGetSiteConfigRefs = getSiteConfigRefsCounter.init({ lang })
|
||||
|
||||
metricsGetSiteConfigRefs.start()
|
||||
|
||||
const responseRef = await request<GetSiteConfigRefData>(
|
||||
GetSiteConfigRef,
|
||||
{
|
||||
@@ -623,20 +422,7 @@ export const baseQueryRouter = router({
|
||||
|
||||
if (!responseRef.data) {
|
||||
const notFoundError = notFound(responseRef)
|
||||
getSiteConfigRefFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.siteConfig.refs not found error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang,
|
||||
},
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetSiteConfigRefs.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -645,20 +431,7 @@ export const baseQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!validatedSiteConfigRef.success) {
|
||||
getSiteConfigRefFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedSiteConfigRef.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.siteConfig.refs validation error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang,
|
||||
},
|
||||
error: validatedSiteConfigRef.error,
|
||||
})
|
||||
)
|
||||
metricsGetSiteConfigRefs.validationError(validatedSiteConfigRef.error)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -670,17 +443,16 @@ export const baseQueryRouter = router({
|
||||
generateTag(lang, siteConfigUid),
|
||||
].flat()
|
||||
|
||||
getSiteConfigRefSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.siteConfig.refs success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
metricsGetSiteConfigRefs.success()
|
||||
|
||||
getSiteConfigCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.siteConfig start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
const getSiteConfigCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"siteConfig.get"
|
||||
)
|
||||
const metricsGetSiteConfig = getSiteConfigCounter.init({ lang })
|
||||
|
||||
metricsGetSiteConfig.start()
|
||||
|
||||
const [siteConfigResponse, contactConfig] = await Promise.all([
|
||||
request<GetSiteConfigData>(
|
||||
GetSiteConfig,
|
||||
@@ -697,21 +469,7 @@ export const baseQueryRouter = router({
|
||||
|
||||
if (!siteConfigResponse.data) {
|
||||
const notFoundError = notFound(siteConfigResponse)
|
||||
|
||||
getSiteConfigFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
|
||||
console.error(
|
||||
"contentstack.siteConfig not found error",
|
||||
JSON.stringify({
|
||||
query: { lang },
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
|
||||
metricsGetSiteConfig.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -720,26 +478,11 @@ export const baseQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!validatedSiteConfig.success) {
|
||||
getSiteConfigFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedSiteConfig.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.siteConfig validation error",
|
||||
JSON.stringify({
|
||||
query: { lang },
|
||||
error: validatedSiteConfig.error,
|
||||
})
|
||||
)
|
||||
metricsGetSiteConfig.validationError(validatedSiteConfig.error)
|
||||
return null
|
||||
}
|
||||
|
||||
getSiteConfigSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.siteConfig success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
metricsGetSiteConfig.success()
|
||||
|
||||
const { sitewideAlert } = validatedSiteConfig.data
|
||||
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
const meter = metrics.getMeter("trpc.contentstack.base")
|
||||
// OpenTelemetry metrics: ContactConfig
|
||||
export const getContactConfigCounter = meter.createCounter(
|
||||
"trpc.contentstack.contactConfig.get"
|
||||
)
|
||||
export const getContactConfigSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.contactConfig.get-success"
|
||||
)
|
||||
export const getContactConfigFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.contactConfig.get-fail"
|
||||
)
|
||||
// OpenTelemetry metrics: CurrentHeader
|
||||
export const getCurrentHeaderRefCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.ref.get"
|
||||
)
|
||||
export const getCurrentHeaderRefSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.ref.get-success"
|
||||
)
|
||||
export const getCurrentHeaderRefFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.ref.get-fail"
|
||||
)
|
||||
export const getCurrentHeaderCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.get"
|
||||
)
|
||||
export const getCurrentHeaderSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.get-success"
|
||||
)
|
||||
export const getCurrentHeaderFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentHeader.get-fail"
|
||||
)
|
||||
|
||||
// OpenTelemetry metrics: Header
|
||||
export const getHeaderRefsCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.ref.get"
|
||||
)
|
||||
export const getHeaderRefsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.ref.get-success"
|
||||
)
|
||||
export const getHeaderRefsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.ref.get-fail"
|
||||
)
|
||||
export const getHeaderCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.get"
|
||||
)
|
||||
export const getHeaderSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.get-success"
|
||||
)
|
||||
export const getHeaderFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.header.get-fail"
|
||||
)
|
||||
|
||||
// OpenTelemetry metrics: CurrentFooter
|
||||
export const getCurrentFooterRefCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.ref.get"
|
||||
)
|
||||
export const getCurrentFooterRefSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.ref.get-success"
|
||||
)
|
||||
export const getCurrentFooterRefFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.ref.get-fail"
|
||||
)
|
||||
export const getCurrentFooterCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.get"
|
||||
)
|
||||
export const getCurrentFooterSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.get-success"
|
||||
)
|
||||
export const getCurrentFooterFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.currentFooter.get-fail"
|
||||
)
|
||||
|
||||
// OpenTelemetry metrics: Footer
|
||||
export const getFooterRefCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.ref.get"
|
||||
)
|
||||
export const getFooterRefSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.ref.get-success"
|
||||
)
|
||||
export const getFooterRefFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.ref.get-fail"
|
||||
)
|
||||
export const getFooterCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.get"
|
||||
)
|
||||
export const getFooterSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.get-success"
|
||||
)
|
||||
export const getFooterFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.footer.get-fail"
|
||||
)
|
||||
|
||||
// OpenTelemetry metrics: SiteConfig
|
||||
export const getSiteConfigRefCounter = meter.createCounter(
|
||||
"trpc.contentstack.SiteConfig.ref.get"
|
||||
)
|
||||
export const getSiteConfigRefSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.SiteConfig.ref.get-success"
|
||||
)
|
||||
export const getSiteConfigRefFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.SiteConfig.ref.get-fail"
|
||||
)
|
||||
export const getSiteConfigCounter = meter.createCounter(
|
||||
"trpc.contentstack.SiteConfig.get"
|
||||
)
|
||||
export const getSiteConfigSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.SiteConfig.get-success"
|
||||
)
|
||||
export const getSiteConfigFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.SiteConfig.get-fail"
|
||||
)
|
||||
@@ -1,4 +1,3 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
import { cache } from "react"
|
||||
|
||||
import {
|
||||
@@ -35,6 +34,7 @@ import {
|
||||
} from "@/lib/graphql/Query/Breadcrumbs/LoyaltyPage.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
||||
|
||||
import { generateRefsResponseTag } from "@/utils/generateTag"
|
||||
@@ -49,28 +49,6 @@ import type {
|
||||
} from "@/types/trpc/routers/contentstack/breadcrumbs"
|
||||
import type { Lang } from "@/constants/languages"
|
||||
|
||||
const meter = metrics.getMeter("trpc.breadcrumbs")
|
||||
|
||||
// OpenTelemetry metrics
|
||||
const getBreadcrumbsRefsCounter = meter.createCounter(
|
||||
"trpc.contentstack.breadcrumbs.refs.get"
|
||||
)
|
||||
const getBreadcrumbsRefsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.breadcrumbs.refs.get-success"
|
||||
)
|
||||
const getBreadcrumbsRefsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.breadcrumbs.refs.get-fail"
|
||||
)
|
||||
const getBreadcrumbsCounter = meter.createCounter(
|
||||
"trpc.contentstack.breadcrumbs.get"
|
||||
)
|
||||
const getBreadcrumbsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.breadcrumbs.get-success"
|
||||
)
|
||||
const getBreadcrumbsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.breadcrumbs.get-fail"
|
||||
)
|
||||
|
||||
interface BreadcrumbsPageData<T> {
|
||||
dataKey: keyof T
|
||||
refQuery: string
|
||||
@@ -81,11 +59,17 @@ const getBreadcrumbs = cache(async function fetchMemoizedBreadcrumbs<T>(
|
||||
{ dataKey, refQuery, query }: BreadcrumbsPageData<T>,
|
||||
{ uid, lang }: { uid: string; lang: Lang }
|
||||
) {
|
||||
getBreadcrumbsRefsCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.breadcrumbs refs get start",
|
||||
JSON.stringify({ query: { lang, uid } })
|
||||
const getBreadcrumbsRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"breadcrumbs.get.refs"
|
||||
)
|
||||
const metricsGetBreadcrumbsRefs = getBreadcrumbsRefsCounter.init({
|
||||
lang,
|
||||
uid,
|
||||
})
|
||||
|
||||
metricsGetBreadcrumbsRefs.start()
|
||||
|
||||
const refsResponse = await request<{ [K in keyof T]: BreadcrumbsRefsSchema }>(
|
||||
refQuery,
|
||||
{ locale: lang, uid },
|
||||
@@ -100,32 +84,25 @@ const getBreadcrumbs = cache(async function fetchMemoizedBreadcrumbs<T>(
|
||||
)
|
||||
|
||||
if (!validatedRefsData.success) {
|
||||
getBreadcrumbsRefsFailCounter.add(1, {
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedRefsData.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.breadcrumbs refs validation error",
|
||||
JSON.stringify({
|
||||
error: validatedRefsData.error,
|
||||
})
|
||||
)
|
||||
metricsGetBreadcrumbsRefs.validationError(validatedRefsData.error)
|
||||
return []
|
||||
}
|
||||
|
||||
getBreadcrumbsRefsSuccessCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.breadcrumbs refs get success",
|
||||
JSON.stringify({ query: { lang, uid } })
|
||||
)
|
||||
metricsGetBreadcrumbsRefs.success()
|
||||
|
||||
const tags = getTags(validatedRefsData.data, lang)
|
||||
|
||||
getBreadcrumbsCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.breadcrumbs get start",
|
||||
JSON.stringify({ query: { lang, uid } })
|
||||
const getBreadcrumbsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"breadcrumbs.get"
|
||||
)
|
||||
const metricsGetBreadcrumbs = getBreadcrumbsCounter.init({
|
||||
lang,
|
||||
uid,
|
||||
})
|
||||
|
||||
metricsGetBreadcrumbs.start()
|
||||
|
||||
const response = await request<T>(
|
||||
query,
|
||||
{ locale: lang, uid },
|
||||
@@ -137,19 +114,7 @@ const getBreadcrumbs = cache(async function fetchMemoizedBreadcrumbs<T>(
|
||||
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
getBreadcrumbsFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.breadcrumbs get not found error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetBreadcrumbs.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -158,24 +123,11 @@ const getBreadcrumbs = cache(async function fetchMemoizedBreadcrumbs<T>(
|
||||
)
|
||||
|
||||
if (!validatedBreadcrumbs.success) {
|
||||
getBreadcrumbsFailCounter.add(1, {
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedBreadcrumbs.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.breadcrumbs validation error",
|
||||
JSON.stringify({
|
||||
error: validatedBreadcrumbs.error,
|
||||
})
|
||||
)
|
||||
metricsGetBreadcrumbs.validationError(validatedBreadcrumbs.error)
|
||||
return []
|
||||
}
|
||||
|
||||
getBreadcrumbsSuccessCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.breadcrumbs get success",
|
||||
JSON.stringify({ query: { lang, uid } })
|
||||
)
|
||||
metricsGetBreadcrumbs.success()
|
||||
|
||||
return validatedBreadcrumbs.data
|
||||
})
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
cardsGridSchema,
|
||||
} from "../schemas/blocks/cardsGrid"
|
||||
import {
|
||||
dynamicContentSchema as blockDynamicContentSchema,
|
||||
dynamicContentRefsSchema,
|
||||
dynamicContentSchema as blockDynamicContentSchema,
|
||||
} from "../schemas/blocks/dynamicContent"
|
||||
import {
|
||||
shortcutsRefsSchema,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { GetCollectionPage } from "@/lib/graphql/Query/CollectionPage/CollectionPage.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
||||
|
||||
import { collectionPageSchema } from "./output"
|
||||
import {
|
||||
fetchCollectionPageRefs,
|
||||
generatePageTags,
|
||||
getCollectionPageCounter,
|
||||
validateCollectionPageRefs,
|
||||
} from "./utils"
|
||||
|
||||
@@ -32,13 +32,16 @@ export const collectionPageQueryRouter = router({
|
||||
}
|
||||
const tags = generatePageTags(collectionPageRefs, lang)
|
||||
|
||||
getCollectionPageCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.collectionPage start",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
const getCollectionPageCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"collectionPage.get"
|
||||
)
|
||||
const metricsGetCollectionPage = getCollectionPageCounter.init({
|
||||
lang,
|
||||
uid,
|
||||
})
|
||||
|
||||
metricsGetCollectionPage.start()
|
||||
|
||||
const response = await request<GetCollectionPageSchema>(
|
||||
GetCollectionPage,
|
||||
@@ -51,13 +54,12 @@ export const collectionPageQueryRouter = router({
|
||||
|
||||
const collectionPage = collectionPageSchema.safeParse(response.data)
|
||||
if (!collectionPage.success) {
|
||||
console.error(
|
||||
`Failed to validate CollectionPage Data - (lang: ${lang}, uid: ${uid})`
|
||||
)
|
||||
console.error(collectionPage.error?.format())
|
||||
metricsGetCollectionPage.validationError(collectionPage.error)
|
||||
return null
|
||||
}
|
||||
|
||||
metricsGetCollectionPage.success()
|
||||
|
||||
const tracking: TrackingSDKPageData = {
|
||||
pageId: collectionPage.data.collection_page.system.uid,
|
||||
domainLanguage: lang,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
import { GetCollectionPageRefs } from "@/lib/graphql/Query/CollectionPage/CollectionPage.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
|
||||
import { getCacheClient } from "@/services/dataCache"
|
||||
import {
|
||||
@@ -21,31 +20,17 @@ import type {
|
||||
} from "@/types/trpc/routers/contentstack/collectionPage"
|
||||
import type { Lang } from "@/constants/languages"
|
||||
|
||||
const meter = metrics.getMeter("trpc.collectionPage")
|
||||
// OpenTelemetry metrics: CollectionPage
|
||||
|
||||
export const getCollectionPageCounter = meter.createCounter(
|
||||
"trpc.contentstack.collectionPage.get"
|
||||
)
|
||||
|
||||
const getCollectionPageRefsCounter = meter.createCounter(
|
||||
"trpc.contentstack.collectionPage.get"
|
||||
)
|
||||
const getCollectionPageRefsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.collectionPage.get-fail"
|
||||
)
|
||||
const getCollectionPageRefsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.collectionPage.get-success"
|
||||
)
|
||||
|
||||
export async function fetchCollectionPageRefs(lang: Lang, uid: string) {
|
||||
getCollectionPageRefsCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.collectionPage.refs start",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
const getCollectionPageRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"collectionPage.get.refs"
|
||||
)
|
||||
const metricsGetCollectionPageRefs = getCollectionPageRefsCounter.init({
|
||||
lang,
|
||||
uid,
|
||||
})
|
||||
|
||||
metricsGetCollectionPageRefs.start()
|
||||
|
||||
const cacheClient = await getCacheClient()
|
||||
const cacheKey = generateRefsResponseTag(lang, uid)
|
||||
@@ -61,24 +46,7 @@ export async function fetchCollectionPageRefs(lang: Lang, uid: string) {
|
||||
|
||||
if (!refsResponse.data) {
|
||||
const notFoundError = notFound(refsResponse)
|
||||
getCollectionPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
code: notFoundError.code,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.collectionPage.refs not found error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang,
|
||||
uid,
|
||||
},
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetCollectionPageRefs.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -90,30 +58,22 @@ export function validateCollectionPageRefs(
|
||||
lang: Lang,
|
||||
uid: string
|
||||
) {
|
||||
const getCollectionPageRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"collectionPage.get.refs"
|
||||
)
|
||||
const metricsGetCollectionPageRefs = getCollectionPageRefsCounter.init({
|
||||
lang,
|
||||
uid,
|
||||
})
|
||||
|
||||
const validatedData = collectionPageRefsSchema.safeParse(data)
|
||||
if (!validatedData.success) {
|
||||
getCollectionPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedData.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.collectionPage.refs validation error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: validatedData.error,
|
||||
})
|
||||
)
|
||||
metricsGetCollectionPageRefs.validationError(validatedData.error)
|
||||
return null
|
||||
}
|
||||
getCollectionPageRefsSuccessCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.collectionPage.refs success",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
)
|
||||
|
||||
metricsGetCollectionPageRefs.success()
|
||||
|
||||
return validatedData.data
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
GetContentPageBlocksBatch1,
|
||||
GetContentPageBlocksBatch2,
|
||||
} from "@/lib/graphql/Query/ContentPage/ContentPage.graphql"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
||||
|
||||
import { contentPageSchema } from "./output"
|
||||
@@ -12,7 +13,6 @@ import {
|
||||
createPageType,
|
||||
fetchContentPageRefs,
|
||||
generatePageTags,
|
||||
getContentPageCounter,
|
||||
} from "./utils"
|
||||
|
||||
import type { TrackingSDKPageData } from "@/types/components/tracking"
|
||||
@@ -30,13 +30,16 @@ export const contentPageQueryRouter = router({
|
||||
|
||||
const tags = generatePageTags(contentPageRefs, lang)
|
||||
|
||||
getContentPageCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.contentPage start",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
const getContentPageCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"contentPage.get"
|
||||
)
|
||||
const metricsGetContentPage = getContentPageCounter.init({
|
||||
lang,
|
||||
uid,
|
||||
})
|
||||
|
||||
metricsGetContentPage.start()
|
||||
|
||||
const contentPageRequest = await batchRequest<GetContentPageSchema>([
|
||||
{
|
||||
@@ -69,13 +72,12 @@ export const contentPageQueryRouter = router({
|
||||
|
||||
const contentPage = contentPageSchema.safeParse(contentPageRequest.data)
|
||||
if (!contentPage.success) {
|
||||
console.error(
|
||||
`Failed to validate Contentpage Data - (lang: ${lang}, uid: ${uid})`
|
||||
)
|
||||
console.error(contentPage.error?.format())
|
||||
metricsGetContentPage.validationError(contentPage.error)
|
||||
return null
|
||||
}
|
||||
|
||||
metricsGetContentPage.success()
|
||||
|
||||
const tracking: TrackingSDKPageData = {
|
||||
pageId: contentPage.data.content_page.system.uid,
|
||||
domainLanguage: lang,
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
import { batchRequest } from "@/lib/graphql/batchRequest"
|
||||
import {
|
||||
GetContentPageBlocksRefs,
|
||||
GetContentPageRefs,
|
||||
} from "@/lib/graphql/Query/ContentPage/ContentPage.graphql"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
|
||||
import {
|
||||
generateRefsResponseTag,
|
||||
@@ -18,37 +17,24 @@ import { contentPageRefsSchema } from "./output"
|
||||
import { TrackingChannelEnum } from "@/types/components/tracking"
|
||||
import { ContentPageEnum } from "@/types/enums/contentPage"
|
||||
import type { System } from "@/types/requests/system"
|
||||
import type {
|
||||
ContentPageRefs,
|
||||
GetContentPageRefsSchema,
|
||||
import {
|
||||
type ContentPageRefs,
|
||||
type GetContentPageRefsSchema,
|
||||
} from "@/types/trpc/routers/contentstack/contentPage"
|
||||
import type { Lang } from "@/constants/languages"
|
||||
|
||||
const meter = metrics.getMeter("trpc.contentPage")
|
||||
// OpenTelemetry metrics: ContentPage
|
||||
|
||||
export const getContentPageCounter = meter.createCounter(
|
||||
"trpc.contentstack.contentPage.get"
|
||||
)
|
||||
|
||||
const getContentPageRefsCounter = meter.createCounter(
|
||||
"trpc.contentstack.contentPage.get"
|
||||
)
|
||||
const getContentPageRefsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.contentPage.get-fail"
|
||||
)
|
||||
const getContentPageRefsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.contentPage.get-success"
|
||||
)
|
||||
|
||||
export async function fetchContentPageRefs(lang: Lang, uid: string) {
|
||||
getContentPageRefsCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.contentPage.refs start",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
const getContentPageRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"contentPage.get.refs"
|
||||
)
|
||||
const metricsGetContentPageRefs = getContentPageRefsCounter.init({
|
||||
lang,
|
||||
uid,
|
||||
})
|
||||
|
||||
metricsGetContentPageRefs.start()
|
||||
|
||||
const res = await batchRequest<GetContentPageRefsSchema>([
|
||||
{
|
||||
document: GetContentPageRefs,
|
||||
@@ -69,50 +55,17 @@ export async function fetchContentPageRefs(lang: Lang, uid: string) {
|
||||
])
|
||||
if (!res.data) {
|
||||
const notFoundError = notFound(res)
|
||||
getContentPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
code: notFoundError.code,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.contentPage.refs not found error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang,
|
||||
uid,
|
||||
},
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetContentPageRefs.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const validatedData = contentPageRefsSchema.safeParse(res.data)
|
||||
if (!validatedData.success) {
|
||||
getContentPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedData.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.contentPage.refs validation error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: validatedData.error,
|
||||
})
|
||||
)
|
||||
metricsGetContentPageRefs.validationError(validatedData.error)
|
||||
return null
|
||||
}
|
||||
getContentPageRefsSuccessCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.contentPage.refs success",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
)
|
||||
|
||||
metricsGetContentPageRefs.success()
|
||||
|
||||
return validatedData.data
|
||||
}
|
||||
@@ -142,8 +95,7 @@ export function getConnections({ content_page }: ContentPageRefs) {
|
||||
case ContentPageEnum.ContentStack.blocks.Content:
|
||||
{
|
||||
if (block.content.length) {
|
||||
// TS has trouble infering the filtered types
|
||||
// @ts-ignore
|
||||
// @ts-expect-error: TS has trouble infering the filtered types
|
||||
connections.push(...block.content)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
} from "@/lib/graphql/Query/DestinationCityPage/DestinationCityPage.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import { contentStackUidWithServiceProcedure, router } from "@/server/trpc"
|
||||
|
||||
import { generateRefsResponseTag } from "@/utils/generateTag"
|
||||
@@ -13,14 +14,6 @@ import {
|
||||
destinationCityPageRefsSchema,
|
||||
destinationCityPageSchema,
|
||||
} from "./output"
|
||||
import {
|
||||
getDestinationCityPageCounter,
|
||||
getDestinationCityPageFailCounter,
|
||||
getDestinationCityPageRefsCounter,
|
||||
getDestinationCityPageRefsFailCounter,
|
||||
getDestinationCityPageRefsSuccessCounter,
|
||||
getDestinationCityPageSuccessCounter,
|
||||
} from "./telemetry"
|
||||
import { generatePageTags } from "./utils"
|
||||
|
||||
import {
|
||||
@@ -36,11 +29,14 @@ export const destinationCityPageQueryRouter = router({
|
||||
get: contentStackUidWithServiceProcedure.query(async ({ ctx }) => {
|
||||
const { lang, uid, serviceToken } = ctx
|
||||
|
||||
getDestinationCityPageRefsCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.destinationCityPage.refs start",
|
||||
JSON.stringify({ query: { lang, uid } })
|
||||
const getDestinationCityPageRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"destinationCityPage.get.refs"
|
||||
)
|
||||
const metricsGetDestinationCityPageRefs =
|
||||
getDestinationCityPageRefsCounter.init({ lang, uid })
|
||||
|
||||
metricsGetDestinationCityPageRefs.start()
|
||||
|
||||
const refsResponse = await request<GetDestinationCityPageRefsSchema>(
|
||||
GetDestinationCityPageRefs,
|
||||
@@ -53,19 +49,7 @@ export const destinationCityPageQueryRouter = router({
|
||||
|
||||
if (!refsResponse.data) {
|
||||
const notFoundError = notFound(refsResponse)
|
||||
getDestinationCityPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.destinationCityPage.refs not found error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetDestinationCityPageRefs.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -73,33 +57,25 @@ export const destinationCityPageQueryRouter = router({
|
||||
refsResponse.data
|
||||
)
|
||||
if (!validatedRefsData.success) {
|
||||
getDestinationCityPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedRefsData.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.destinationCityPage.refs validation error",
|
||||
JSON.stringify({ query: { lang, uid }, error: validatedRefsData.error })
|
||||
)
|
||||
metricsGetDestinationCityPageRefs.validationError(validatedRefsData.error)
|
||||
return null
|
||||
}
|
||||
getDestinationCityPageRefsSuccessCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.destinationCityPage.refs success",
|
||||
JSON.stringify({ query: { lang, uid } })
|
||||
)
|
||||
|
||||
metricsGetDestinationCityPageRefs.success()
|
||||
|
||||
const tags = generatePageTags(validatedRefsData.data, lang)
|
||||
|
||||
getDestinationCityPageCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.destinationCityPage start",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
const getDestinationCityPageCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"destinationCityPage.get"
|
||||
)
|
||||
const metricsGetDestinationCityPage = getDestinationCityPageCounter.init({
|
||||
lang,
|
||||
uid,
|
||||
})
|
||||
|
||||
metricsGetDestinationCityPage.start()
|
||||
|
||||
const response = await request<GetDestinationCityPageData>(
|
||||
GetDestinationCityPage,
|
||||
{
|
||||
@@ -113,40 +89,17 @@ export const destinationCityPageQueryRouter = router({
|
||||
)
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
getDestinationCityPageFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.destinationCityPage not found error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetDestinationCityPage.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const validatedResponse = destinationCityPageSchema.safeParse(response.data)
|
||||
|
||||
if (!validatedResponse.success) {
|
||||
getDestinationCityPageFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedResponse.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.destinationCityPage validation error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: validatedResponse.error,
|
||||
})
|
||||
)
|
||||
metricsGetDestinationCityPage.validationError(validatedResponse.error)
|
||||
return null
|
||||
}
|
||||
|
||||
const destinationCityPage = validatedResponse.data.destination_city_page
|
||||
const cityIdentifier = destinationCityPage.destination_settings.city
|
||||
if (!cityIdentifier) {
|
||||
@@ -160,30 +113,16 @@ export const destinationCityPageQueryRouter = router({
|
||||
})
|
||||
|
||||
if (!city) {
|
||||
getDestinationCityPageFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "not_found",
|
||||
error: `Couldn't find city with cityIdentifier: ${cityIdentifier}`,
|
||||
})
|
||||
|
||||
console.error(
|
||||
"contentstack.destinationCityPage not found error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: `Couldn't find city with cityIdentifier: ${cityIdentifier}`,
|
||||
})
|
||||
metricsGetDestinationCityPage.dataError(
|
||||
`Failed to get city data for ${cityIdentifier}`,
|
||||
{
|
||||
cityIdentifier,
|
||||
}
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
getDestinationCityPageSuccessCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.destinationCityPage success",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
)
|
||||
metricsGetDestinationCityPage.success()
|
||||
|
||||
const system = destinationCityPage.system
|
||||
const pageName = `destinations|${city.country}|${city.name}`
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
const meter = metrics.getMeter("trpc.contentstack.destinationCityPage")
|
||||
|
||||
export const getDestinationCityPageRefsCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationCityPage.get"
|
||||
)
|
||||
export const getDestinationCityPageRefsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationCityPage.get-fail"
|
||||
)
|
||||
export const getDestinationCityPageRefsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationCityPage.get-success"
|
||||
)
|
||||
|
||||
export const getDestinationCityPageCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationCityPage.get"
|
||||
)
|
||||
export const getDestinationCityPageSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationCityPage.get-success"
|
||||
)
|
||||
export const getDestinationCityPageFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationCityPage.get-fail"
|
||||
)
|
||||
|
||||
export const getCityPageCountCounter = meter.createCounter(
|
||||
"trpc.contentstack.cityPageCount.get"
|
||||
)
|
||||
export const getCityPageCountSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.cityPageCount.get-success"
|
||||
)
|
||||
export const getCityPageCountFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.cityPageCount.get-fail"
|
||||
)
|
||||
|
||||
export const getCityPageUrlsCounter = meter.createCounter(
|
||||
"trpc.contentstack.cityPageUrls.get"
|
||||
)
|
||||
export const getCityPageUrlsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.cityPageUrls.get-success"
|
||||
)
|
||||
export const getCityPageUrlsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.cityPageUrls.get-fail"
|
||||
)
|
||||
@@ -1,18 +1,11 @@
|
||||
import { GetCityPageCount } from "@/lib/graphql/Query/DestinationCityPage/DestinationCityPageCount.graphql"
|
||||
import { GetCityPageUrls } from "@/lib/graphql/Query/DestinationCityPage/DestinationCityPageUrl.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
|
||||
import { generateTag, generateTagsFromSystem } from "@/utils/generateTag"
|
||||
|
||||
import { batchedCityPageUrlsSchema, cityPageCountSchema } from "./output"
|
||||
import {
|
||||
getCityPageCountCounter,
|
||||
getCityPageCountFailCounter,
|
||||
getCityPageCountSuccessCounter,
|
||||
getCityPageUrlsCounter,
|
||||
getCityPageUrlsFailCounter,
|
||||
getCityPageUrlsSuccessCounter,
|
||||
} from "./telemetry"
|
||||
|
||||
import { DestinationCityPageEnum } from "@/types/enums/destinationCityPage"
|
||||
import type { System } from "@/types/requests/system"
|
||||
@@ -76,11 +69,13 @@ export function getConnections({
|
||||
}
|
||||
|
||||
export async function getCityPageCount(lang: Lang) {
|
||||
getCityPageCountCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.cityPageCount start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
const getCityPageCountCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"cityPageCount.get"
|
||||
)
|
||||
const metricsGetCityPageCount = getCityPageCountCounter.init({ lang })
|
||||
|
||||
metricsGetCityPageCount.start()
|
||||
|
||||
const response = await request<GetCityPageCountData>(
|
||||
GetCityPageCount,
|
||||
@@ -92,15 +87,10 @@ export async function getCityPageCount(lang: Lang) {
|
||||
ttl: "max",
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.data) {
|
||||
getCityPageCountFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "not_found",
|
||||
error: `City pages count not found for lang: ${lang}`,
|
||||
})
|
||||
console.error(
|
||||
"contentstack.cityPageCount not found error",
|
||||
JSON.stringify({ query: { lang } })
|
||||
metricsGetCityPageCount.dataError(
|
||||
`Failed to get city pages count for ${lang}`
|
||||
)
|
||||
return 0
|
||||
}
|
||||
@@ -108,35 +98,24 @@ export async function getCityPageCount(lang: Lang) {
|
||||
const validatedResponse = cityPageCountSchema.safeParse(response.data)
|
||||
|
||||
if (!validatedResponse.success) {
|
||||
getCityPageCountFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedResponse.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.hotelPageCount validation error",
|
||||
JSON.stringify({
|
||||
query: { lang },
|
||||
error: validatedResponse.error,
|
||||
})
|
||||
)
|
||||
metricsGetCityPageCount.validationError(validatedResponse.error)
|
||||
return 0
|
||||
}
|
||||
getCityPageCountSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.cityPageCount success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
|
||||
metricsGetCityPageCount.success()
|
||||
|
||||
return validatedResponse.data
|
||||
}
|
||||
|
||||
export async function getCityPageUrls(lang: Lang) {
|
||||
getCityPageUrlsCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.cityPageUrls start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
const getCityPageUrlsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"cityPageUrls.get"
|
||||
)
|
||||
const metricsGetCityPageUrls = getCityPageUrlsCounter.init({ lang })
|
||||
|
||||
metricsGetCityPageUrls.start()
|
||||
|
||||
const count = await getCityPageCount(lang)
|
||||
|
||||
if (count === 0) {
|
||||
@@ -162,25 +141,11 @@ export async function getCityPageUrls(lang: Lang) {
|
||||
const validatedResponse = batchedCityPageUrlsSchema.safeParse(batchedResponse)
|
||||
|
||||
if (!validatedResponse.success) {
|
||||
getCityPageUrlsFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedResponse.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.cityPageUrls validation error",
|
||||
JSON.stringify({
|
||||
query: { lang },
|
||||
error: validatedResponse.error,
|
||||
})
|
||||
)
|
||||
metricsGetCityPageUrls.validationError(validatedResponse.error)
|
||||
return []
|
||||
}
|
||||
getCityPageUrlsSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.cityPageUrls success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
|
||||
metricsGetCityPageUrls.success()
|
||||
|
||||
return validatedResponse.data
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
} from "@/lib/graphql/Query/DestinationCountryPage/DestinationCountryPage.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import {
|
||||
contentStackBaseWithServiceProcedure,
|
||||
contentstackExtendedProcedureUID,
|
||||
@@ -17,14 +18,6 @@ import {
|
||||
destinationCountryPageRefsSchema,
|
||||
destinationCountryPageSchema,
|
||||
} from "./output"
|
||||
import {
|
||||
getDestinationCountryPageCounter,
|
||||
getDestinationCountryPageFailCounter,
|
||||
getDestinationCountryPageRefsCounter,
|
||||
getDestinationCountryPageRefsFailCounter,
|
||||
getDestinationCountryPageRefsSuccessCounter,
|
||||
getDestinationCountryPageSuccessCounter,
|
||||
} from "./telemetry"
|
||||
import { generatePageTags, getCityPages } from "./utils"
|
||||
|
||||
import {
|
||||
@@ -41,11 +34,14 @@ export const destinationCountryPageQueryRouter = router({
|
||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
||||
const { lang, uid } = ctx
|
||||
|
||||
getDestinationCountryPageRefsCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.destinationCountryPage.refs start",
|
||||
JSON.stringify({ query: { lang, uid } })
|
||||
const getDestinationCountryPageRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"destinationCountryPage.get.refs"
|
||||
)
|
||||
const metricsGetDestinationCountryPageRefs =
|
||||
getDestinationCountryPageRefsCounter.init({ lang, uid })
|
||||
|
||||
metricsGetDestinationCountryPageRefs.start()
|
||||
|
||||
const refsResponse = await request<GetDestinationCountryPageRefsSchema>(
|
||||
GetDestinationCountryPageRefs,
|
||||
@@ -58,19 +54,7 @@ export const destinationCountryPageQueryRouter = router({
|
||||
|
||||
if (!refsResponse.data) {
|
||||
const notFoundError = notFound(refsResponse)
|
||||
getDestinationCountryPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.destinationCountryPage.refs not found error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetDestinationCountryPageRefs.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -78,33 +62,25 @@ export const destinationCountryPageQueryRouter = router({
|
||||
refsResponse.data
|
||||
)
|
||||
if (!validatedRefsData.success) {
|
||||
getDestinationCountryPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedRefsData.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.destinationCountryPage.refs validation error",
|
||||
JSON.stringify({ query: { lang, uid }, error: validatedRefsData.error })
|
||||
metricsGetDestinationCountryPageRefs.validationError(
|
||||
validatedRefsData.error
|
||||
)
|
||||
return null
|
||||
}
|
||||
getDestinationCountryPageRefsSuccessCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.destinationCountryPage.refs success",
|
||||
JSON.stringify({ query: { lang, uid } })
|
||||
)
|
||||
|
||||
metricsGetDestinationCountryPageRefs.success()
|
||||
|
||||
const tags = generatePageTags(validatedRefsData.data, lang)
|
||||
|
||||
getDestinationCountryPageCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.destinationCountryPage start",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
const getDestinationCountryPageCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"destinationCountryPage.get"
|
||||
)
|
||||
const metricsGetDestinationCountryPage =
|
||||
getDestinationCountryPageCounter.init({ lang, uid })
|
||||
|
||||
metricsGetDestinationCountryPage.start()
|
||||
|
||||
const response = await request<GetDestinationCountryPageData>(
|
||||
GetDestinationCountryPage,
|
||||
{
|
||||
@@ -118,19 +94,7 @@ export const destinationCountryPageQueryRouter = router({
|
||||
)
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
getDestinationCountryPageFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.destinationCountryPage not found error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetDestinationCountryPage.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -139,32 +103,15 @@ export const destinationCountryPageQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!validatedResponse.success) {
|
||||
getDestinationCountryPageFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedResponse.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.destinationCountryPage validation error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: validatedResponse.error,
|
||||
})
|
||||
)
|
||||
metricsGetDestinationCountryPage.validationError(validatedResponse.error)
|
||||
return null
|
||||
}
|
||||
|
||||
const destinationCountryPage =
|
||||
validatedResponse.data.destination_country_page
|
||||
const country = destinationCountryPage.destination_settings.country
|
||||
|
||||
getDestinationCountryPageSuccessCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.destinationCountryPage success",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
)
|
||||
metricsGetDestinationCountryPage.success()
|
||||
|
||||
const system = destinationCountryPage.system
|
||||
const pageName = `destinations|${country}`
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
const meter = metrics.getMeter("trpc.contentstack.destinationCountryPage")
|
||||
|
||||
export const getDestinationCountryPageRefsCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationCountryPage.get"
|
||||
)
|
||||
export const getDestinationCountryPageRefsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationCountryPage.get-fail"
|
||||
)
|
||||
export const getDestinationCountryPageRefsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationCountryPage.get-success"
|
||||
)
|
||||
|
||||
export const getDestinationCountryPageCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationCountryPage.get"
|
||||
)
|
||||
export const getDestinationCountryPageSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationCountryPage.get-success"
|
||||
)
|
||||
export const getDestinationCountryPageFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationCountryPage.get-fail"
|
||||
)
|
||||
|
||||
export const getCityListDataCounter = meter.createCounter(
|
||||
"trpc.contentstack.cityListData.get"
|
||||
)
|
||||
export const getCityListDataSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.cityListData.get-success"
|
||||
)
|
||||
export const getCityListDataFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.cityListData.get-fail"
|
||||
)
|
||||
|
||||
export const getCountryPageUrlsCounter = meter.createCounter(
|
||||
"trpc.contentstack.getCountryPageUrls"
|
||||
)
|
||||
|
||||
export const getCountryPageUrlsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.getCountryPageUrls-success"
|
||||
)
|
||||
|
||||
export const getCountryPageUrlsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.getCountryPageUrls-fail"
|
||||
)
|
||||
@@ -1,20 +1,13 @@
|
||||
import { GetDestinationCityListData } from "@/lib/graphql/Query/DestinationCityPage/DestinationCityListData.graphql"
|
||||
import { GetCountryPageUrls } from "@/lib/graphql/Query/DestinationCountryPage/DestinationCountryPageUrl.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
|
||||
import { generateTag, generateTagsFromSystem } from "@/utils/generateTag"
|
||||
|
||||
import { getCitiesByCountry } from "../../hotels/utils"
|
||||
import { destinationCityListDataSchema } from "../destinationCityPage/output"
|
||||
import { countryPageUrlsSchema } from "./output"
|
||||
import {
|
||||
getCityListDataCounter,
|
||||
getCityListDataFailCounter,
|
||||
getCityListDataSuccessCounter,
|
||||
getCountryPageUrlsCounter,
|
||||
getCountryPageUrlsFailCounter,
|
||||
getCountryPageUrlsSuccessCounter,
|
||||
} from "./telemetry"
|
||||
|
||||
import { ApiCountry, type Country } from "@/types/enums/country"
|
||||
import { DestinationCountryPageEnum } from "@/types/enums/destinationCountryPage"
|
||||
@@ -77,11 +70,16 @@ export async function getCityListDataByCityIdentifier(
|
||||
lang: Lang,
|
||||
cityIdentifier: string
|
||||
) {
|
||||
getCityListDataCounter.add(1, { lang, cityIdentifier })
|
||||
console.info(
|
||||
"contentstack.cityListData start",
|
||||
JSON.stringify({ query: { lang, cityIdentifier } })
|
||||
const getCityListDataCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"cityListData.get"
|
||||
)
|
||||
const metricsGetCityListData = getCityListDataCounter.init({
|
||||
lang,
|
||||
cityIdentifier,
|
||||
})
|
||||
|
||||
metricsGetCityListData.start()
|
||||
|
||||
const response = await request<GetDestinationCityListDataResponse>(
|
||||
GetDestinationCityListData,
|
||||
@@ -96,15 +94,8 @@ export async function getCityListDataByCityIdentifier(
|
||||
)
|
||||
|
||||
if (!response.data) {
|
||||
getCityListDataFailCounter.add(1, {
|
||||
lang,
|
||||
cityIdentifier,
|
||||
error_type: "not_found",
|
||||
error: `Destination city page not found for cityIdentifier: ${cityIdentifier}`,
|
||||
})
|
||||
console.error(
|
||||
"contentstack.cityListData not found error",
|
||||
JSON.stringify({ query: { lang, cityIdentifier } })
|
||||
metricsGetCityListData.dataError(
|
||||
`Failed to get destination city page for cityIdentifier: ${cityIdentifier}`
|
||||
)
|
||||
return null
|
||||
}
|
||||
@@ -114,26 +105,11 @@ export async function getCityListDataByCityIdentifier(
|
||||
)
|
||||
|
||||
if (!validatedResponse.success) {
|
||||
getCityListDataFailCounter.add(1, {
|
||||
lang,
|
||||
cityIdentifier,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedResponse.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.cityListData validation error",
|
||||
JSON.stringify({
|
||||
query: { lang, cityIdentifier },
|
||||
error: validatedResponse.error,
|
||||
})
|
||||
)
|
||||
metricsGetCityListData.validationError(validatedResponse.error)
|
||||
return null
|
||||
}
|
||||
getCityListDataSuccessCounter.add(1, { lang, cityIdentifier })
|
||||
console.info(
|
||||
"contentstack.cityListData success",
|
||||
JSON.stringify({ query: { lang, cityIdentifier } })
|
||||
)
|
||||
|
||||
metricsGetCityListData.success()
|
||||
|
||||
return validatedResponse.data
|
||||
}
|
||||
@@ -172,11 +148,13 @@ export async function getCityPages(
|
||||
}
|
||||
|
||||
export async function getCountryPageUrls(lang: Lang) {
|
||||
getCountryPageUrlsCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.countryPageUrls start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
const getCountryPageUrlsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"getCountryPageUrls"
|
||||
)
|
||||
const metricsGetCountryPageUrls = getCountryPageUrlsCounter.init({ lang })
|
||||
|
||||
metricsGetCountryPageUrls.start()
|
||||
|
||||
const tag = `${lang}:country_page_urls`
|
||||
const response = await request<GetCountryPageUrlsData>(
|
||||
@@ -191,14 +169,8 @@ export async function getCountryPageUrls(lang: Lang) {
|
||||
)
|
||||
|
||||
if (!response.data) {
|
||||
getCountryPageUrlsFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "not_found",
|
||||
error: `Country pages not found for lang: ${lang}`,
|
||||
})
|
||||
console.error(
|
||||
"contentstack.countryPageUrls not found error",
|
||||
JSON.stringify({ query: { lang } })
|
||||
metricsGetCountryPageUrls.dataError(
|
||||
`Failed to get country pages for lang: ${lang}`
|
||||
)
|
||||
return []
|
||||
}
|
||||
@@ -208,26 +180,11 @@ export async function getCountryPageUrls(lang: Lang) {
|
||||
)
|
||||
|
||||
if (!validatedCountryPageUrls.success) {
|
||||
getCountryPageUrlsFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedCountryPageUrls.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.countryPageUrls validation error",
|
||||
JSON.stringify({
|
||||
query: { lang },
|
||||
error: validatedCountryPageUrls.error,
|
||||
})
|
||||
)
|
||||
metricsGetCountryPageUrls.validationError(validatedCountryPageUrls.error)
|
||||
return []
|
||||
}
|
||||
|
||||
getCountryPageUrlsSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.countryPageUrls success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
metricsGetCountryPageUrls.success()
|
||||
|
||||
return validatedCountryPageUrls.data
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from "@/lib/graphql/Query/DestinationOverviewPage/DestinationOverviewPage.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import {
|
||||
contentstackExtendedProcedureUID,
|
||||
router,
|
||||
@@ -31,14 +32,6 @@ import {
|
||||
destinationOverviewPageRefsSchema,
|
||||
destinationOverviewPageSchema,
|
||||
} from "./output"
|
||||
import {
|
||||
getDestinationOverviewPageCounter,
|
||||
getDestinationOverviewPageFailCounter,
|
||||
getDestinationOverviewPageRefsCounter,
|
||||
getDestinationOverviewPageRefsFailCounter,
|
||||
getDestinationOverviewPageRefsSuccessCounter,
|
||||
getDestinationOverviewPageSuccessCounter,
|
||||
} from "./telemetry"
|
||||
|
||||
import type {
|
||||
Cities,
|
||||
@@ -57,13 +50,15 @@ export const destinationOverviewPageQueryRouter = router({
|
||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
||||
const { lang, uid } = ctx
|
||||
|
||||
getDestinationOverviewPageRefsCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.destinationOverviewPage.refs start",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
const getDestinationOverviewPageRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"destinationOverviewPage.get.refs"
|
||||
)
|
||||
const metricsGetDestinationOverviewPageRefs =
|
||||
getDestinationOverviewPageRefsCounter.init({ lang, uid })
|
||||
|
||||
metricsGetDestinationOverviewPageRefs.start()
|
||||
|
||||
const refsResponse = await request<GetDestinationOverviewPageRefsSchema>(
|
||||
GetDestinationOverviewPageRefs,
|
||||
{
|
||||
@@ -77,19 +72,7 @@ export const destinationOverviewPageQueryRouter = router({
|
||||
)
|
||||
if (!refsResponse.data) {
|
||||
const notFoundError = notFound(refsResponse)
|
||||
getDestinationOverviewPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.destinationOverviewPage.refs not found error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetDestinationOverviewPageRefs.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -98,37 +81,23 @@ export const destinationOverviewPageQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!validatedRefsData.success) {
|
||||
getDestinationOverviewPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedRefsData.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.destinationOverviewPage.refs validation error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: validatedRefsData.error,
|
||||
})
|
||||
metricsGetDestinationOverviewPageRefs.validationError(
|
||||
validatedRefsData.error
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
getDestinationOverviewPageRefsSuccessCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.destinationOverviewPage.refs success",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
)
|
||||
metricsGetDestinationOverviewPageRefs.success()
|
||||
|
||||
getDestinationOverviewPageCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.destinationOverviewPage start",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
const getDestinationOverviewPageCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"destinationOverviewPage.get"
|
||||
)
|
||||
const metricsGetDestinationOverviewPage =
|
||||
getDestinationOverviewPageCounter.init({ lang, uid })
|
||||
|
||||
metricsGetDestinationOverviewPage.start()
|
||||
|
||||
const response = await request<GetDestinationOverviewPageData>(
|
||||
GetDestinationOverviewPage,
|
||||
{
|
||||
@@ -142,19 +111,7 @@ export const destinationOverviewPageQueryRouter = router({
|
||||
)
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
getDestinationOverviewPageFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.destinationOverviewPage not found error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetDestinationOverviewPage.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -163,29 +120,13 @@ export const destinationOverviewPageQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!destinationOverviewPage.success) {
|
||||
getDestinationOverviewPageFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(destinationOverviewPage.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.destinationOverviewPage validation error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: destinationOverviewPage.error,
|
||||
})
|
||||
metricsGetDestinationOverviewPage.validationError(
|
||||
destinationOverviewPage.error
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
getDestinationOverviewPageSuccessCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.destinationOverviewPage success",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
)
|
||||
metricsGetDestinationOverviewPage.success()
|
||||
|
||||
const system = destinationOverviewPage.data.destination_overview_page.system
|
||||
const tracking: TrackingSDKPageData = {
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
const meter = metrics.getMeter("trpc.contentstack.destinationOverviewPage")
|
||||
|
||||
export const getDestinationOverviewPageRefsCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationOverviewPage.get"
|
||||
)
|
||||
export const getDestinationOverviewPageRefsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationOverviewPage.get-fail"
|
||||
)
|
||||
export const getDestinationOverviewPageRefsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationOverviewPage.get-success"
|
||||
)
|
||||
|
||||
export const getDestinationOverviewPageCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationOverviewPage.get"
|
||||
)
|
||||
export const getDestinationOverviewPageSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationOverviewPage.get-success"
|
||||
)
|
||||
export const getDestinationOverviewPageFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.destinationOverviewPage.get-fail"
|
||||
)
|
||||
@@ -1,29 +1,27 @@
|
||||
import { GetHotelPage } from "@/lib/graphql/Query/HotelPage/HotelPage.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
||||
|
||||
import { generateTag } from "@/utils/generateTag"
|
||||
|
||||
import { hotelPageSchema } from "./output"
|
||||
import {
|
||||
getHotelPageCounter,
|
||||
getHotelPageFailCounter,
|
||||
getHotelPageSuccessCounter,
|
||||
} from "./telemetry"
|
||||
|
||||
import type { GetHotelPageData } from "@/types/trpc/routers/contentstack/hotelPage"
|
||||
|
||||
export const hotelPageQueryRouter = router({
|
||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
||||
const { lang, uid } = ctx
|
||||
getHotelPageCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.hotelPage start",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
|
||||
const getHotelPageCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"hotelPage.get"
|
||||
)
|
||||
const metricsGetHotelPage = getHotelPageCounter.init({ lang, uid })
|
||||
|
||||
metricsGetHotelPage.start()
|
||||
|
||||
const response = await request<GetHotelPageData>(
|
||||
GetHotelPage,
|
||||
{
|
||||
@@ -37,48 +35,19 @@ export const hotelPageQueryRouter = router({
|
||||
)
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
getHotelPageFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.hotelPage not found error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetHotelPage.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const validatedHotelPage = hotelPageSchema.safeParse(response.data)
|
||||
|
||||
if (!validatedHotelPage.success) {
|
||||
getHotelPageFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedHotelPage.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.hotelPage validation error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: validatedHotelPage.error,
|
||||
})
|
||||
)
|
||||
metricsGetHotelPage.validationError(validatedHotelPage.error)
|
||||
return null
|
||||
}
|
||||
|
||||
getHotelPageSuccessCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.hotelPage success",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
)
|
||||
metricsGetHotelPage.success()
|
||||
|
||||
return validatedHotelPage.data.hotel_page
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
const meter = metrics.getMeter("trpc.contentstack.hotelPage")
|
||||
|
||||
export const getHotelPageRefsCounter = meter.createCounter(
|
||||
"trpc.contentstack.hotelPage.get"
|
||||
)
|
||||
export const getHotelPageRefsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.hotelPage.get-fail"
|
||||
)
|
||||
export const getHotelPageRefsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.hotelPage.get-success"
|
||||
)
|
||||
|
||||
export const getHotelPageCounter = meter.createCounter(
|
||||
"trpc.contentstack.hotelPage.get"
|
||||
)
|
||||
export const getHotelPageSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.hotelPage.get-success"
|
||||
)
|
||||
export const getHotelPageFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.hotelPage.get-fail"
|
||||
)
|
||||
|
||||
export const getHotelPageUrlsCounter = meter.createCounter(
|
||||
"trpc.contentstack.hotelPageUrls.get"
|
||||
)
|
||||
export const getHotelPageUrlsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.hotelPageUrls.get-success"
|
||||
)
|
||||
export const getHotelPageUrlsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.hotelPageUrls.get-fail"
|
||||
)
|
||||
|
||||
export const getHotelPageCountCounter = meter.createCounter(
|
||||
"trpc.contentstack.hotelPageCount.get"
|
||||
)
|
||||
export const getHotelPageCountSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.hotelPageCount.get-success"
|
||||
)
|
||||
export const getHotelPageCountFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.hotelPageCount.get-fail"
|
||||
)
|
||||
@@ -3,6 +3,7 @@ import { GetHotelPageCount } from "@/lib/graphql/Query/HotelPage/HotelPageCount.
|
||||
import { GetHotelPageUrls } from "@/lib/graphql/Query/HotelPage/HotelPageUrl.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
|
||||
import {
|
||||
generateRefsResponseTag,
|
||||
@@ -15,17 +16,6 @@ import {
|
||||
hotelPageCountSchema,
|
||||
hotelPageRefsSchema,
|
||||
} from "./output"
|
||||
import {
|
||||
getHotelPageCountCounter,
|
||||
getHotelPageCountFailCounter,
|
||||
getHotelPageCountSuccessCounter,
|
||||
getHotelPageRefsCounter,
|
||||
getHotelPageRefsFailCounter,
|
||||
getHotelPageRefsSuccessCounter,
|
||||
getHotelPageUrlsCounter,
|
||||
getHotelPageUrlsFailCounter,
|
||||
getHotelPageUrlsSuccessCounter,
|
||||
} from "./telemetry"
|
||||
|
||||
import { HotelPageEnum } from "@/types/enums/hotelPage"
|
||||
import type { System } from "@/types/requests/system"
|
||||
@@ -38,13 +28,13 @@ import type {
|
||||
import type { Lang } from "@/constants/languages"
|
||||
|
||||
export async function fetchHotelPageRefs(lang: Lang, uid: string) {
|
||||
getHotelPageRefsCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.hotelPage.refs start",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
const getHotelPageRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"hotelPage.get.refs"
|
||||
)
|
||||
const metricsGetHotelPageRefs = getHotelPageRefsCounter.init({ lang, uid })
|
||||
|
||||
metricsGetHotelPageRefs.start()
|
||||
|
||||
const refsResponse = await request<GetHotelPageRefsSchema>(
|
||||
GetHotelPageRefs,
|
||||
@@ -56,26 +46,10 @@ export async function fetchHotelPageRefs(lang: Lang, uid: string) {
|
||||
)
|
||||
if (!refsResponse.data) {
|
||||
const notFoundError = notFound(refsResponse)
|
||||
getHotelPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
code: notFoundError.code,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.hotelPage.refs not found error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang,
|
||||
uid,
|
||||
},
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetHotelPageRefs.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
return refsResponse.data
|
||||
}
|
||||
|
||||
@@ -84,30 +58,20 @@ export function validateHotelPageRefs(
|
||||
lang: Lang,
|
||||
uid: string
|
||||
) {
|
||||
const getHotelPageRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"hotelPage.get.refs"
|
||||
)
|
||||
const metricsGetHotelPageRefs = getHotelPageRefsCounter.init({ lang, uid })
|
||||
|
||||
const validatedData = hotelPageRefsSchema.safeParse(data)
|
||||
|
||||
if (!validatedData.success) {
|
||||
getHotelPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedData.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.hotelPage.refs validation error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: validatedData.error,
|
||||
})
|
||||
)
|
||||
metricsGetHotelPageRefs.validationError(validatedData.error)
|
||||
return null
|
||||
}
|
||||
getHotelPageRefsSuccessCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.hotelPage.refs success",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
)
|
||||
|
||||
metricsGetHotelPageRefs.success()
|
||||
|
||||
return validatedData.data
|
||||
}
|
||||
@@ -144,11 +108,14 @@ export function getConnections({ hotel_page }: HotelPageRefs) {
|
||||
}
|
||||
|
||||
export async function getHotelPageCount(lang: Lang) {
|
||||
getHotelPageCountCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.hotelPageCount start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
const getHotelPageCountCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"hotelPageCount.get"
|
||||
)
|
||||
const metricsGetHotelPageCount = getHotelPageCountCounter.init({ lang })
|
||||
|
||||
metricsGetHotelPageCount.start()
|
||||
|
||||
const response = await request<GetHotelPageCountData>(
|
||||
GetHotelPageCount,
|
||||
{
|
||||
@@ -161,50 +128,31 @@ export async function getHotelPageCount(lang: Lang) {
|
||||
)
|
||||
|
||||
if (!response.data) {
|
||||
getHotelPageCountFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "not_found",
|
||||
error: `Hotel pages count not found for lang: ${lang}`,
|
||||
})
|
||||
console.error(
|
||||
"contentstack.hotelPageCount not found error",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
metricsGetHotelPageCount.noDataError()
|
||||
return 0
|
||||
}
|
||||
|
||||
const validatedResponse = hotelPageCountSchema.safeParse(response.data)
|
||||
|
||||
if (!validatedResponse.success) {
|
||||
getHotelPageCountFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedResponse.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.hotelPageCount validation error",
|
||||
JSON.stringify({
|
||||
query: { lang },
|
||||
error: validatedResponse.error,
|
||||
})
|
||||
)
|
||||
metricsGetHotelPageCount.validationError(validatedResponse.error)
|
||||
return 0
|
||||
}
|
||||
getHotelPageCountSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.hotelPageCount success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
|
||||
metricsGetHotelPageCount.success()
|
||||
|
||||
return validatedResponse.data
|
||||
}
|
||||
|
||||
export async function getHotelPageUrls(lang: Lang) {
|
||||
getHotelPageUrlsCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.hotelPageUrls start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
const getHotelPageUrlsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"hotelPageUrls.get"
|
||||
)
|
||||
const metricsGetHotelPageUrls = getHotelPageUrlsCounter.init({ lang })
|
||||
|
||||
metricsGetHotelPageUrls.start()
|
||||
|
||||
const count = await getHotelPageCount(lang)
|
||||
|
||||
if (count === 0) {
|
||||
@@ -236,25 +184,11 @@ export async function getHotelPageUrls(lang: Lang) {
|
||||
batchedHotelPageUrlsSchema.safeParse(batchedResponse)
|
||||
|
||||
if (!validatedResponse.success) {
|
||||
getHotelPageUrlsFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedResponse.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.hotelPageUrls validation error",
|
||||
JSON.stringify({
|
||||
query: { lang },
|
||||
error: validatedResponse.error,
|
||||
})
|
||||
)
|
||||
metricsGetHotelPageUrls.validationError(validatedResponse.error)
|
||||
return []
|
||||
}
|
||||
getHotelPageUrlsSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.hotelPageUrl success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
|
||||
metricsGetHotelPageUrls.success()
|
||||
|
||||
return validatedResponse.data
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
const meter = metrics.getMeter("trpc.contentstack.languageSwitcher")
|
||||
export const getLanguageSwitcherCounter = meter.createCounter(
|
||||
"trpc.contentstack.languageSwitcher.get"
|
||||
)
|
||||
export const getLanguageSwitcherSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.languageSwitcher.get-success"
|
||||
)
|
||||
export const getLanguageSwitcherFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.languageSwitcher.get-fail"
|
||||
)
|
||||
@@ -41,16 +41,12 @@ import {
|
||||
GetFiNoSvUrlsStartPage,
|
||||
} from "@/lib/graphql/Query/StartPage/StartPage.graphql"
|
||||
import { internalServerError } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
|
||||
import { generateTag } from "@/utils/generateTag"
|
||||
import { removeTrailingSlash } from "@/utils/url"
|
||||
|
||||
import { validateLanguageSwitcherData } from "./output"
|
||||
import {
|
||||
getLanguageSwitcherCounter,
|
||||
getLanguageSwitcherFailCounter,
|
||||
getLanguageSwitcherSuccessCounter,
|
||||
} from "./telemetry"
|
||||
|
||||
import { PageContentTypeEnum } from "@/types/requests/contentType"
|
||||
import type {
|
||||
@@ -65,21 +61,17 @@ export async function getUrlsOfAllLanguages(
|
||||
uid: string,
|
||||
contentType: string
|
||||
) {
|
||||
getLanguageSwitcherCounter.add(1, {
|
||||
uid,
|
||||
const getLanguageSwitcherCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"languageSwitcher.get"
|
||||
)
|
||||
const metricsGetLanguageSwitcher = getLanguageSwitcherCounter.init({
|
||||
lang,
|
||||
uid,
|
||||
contentType,
|
||||
})
|
||||
console.info(
|
||||
"contentstack.languageSwitcher start",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
uid,
|
||||
lang,
|
||||
contentType,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
metricsGetLanguageSwitcher.start()
|
||||
|
||||
const variables = { uid }
|
||||
const tagsDaDeEn = [
|
||||
@@ -184,42 +176,13 @@ export async function getUrlsOfAllLanguages(
|
||||
validateLanguageSwitcherData.safeParse(urls)
|
||||
|
||||
if (!validatedLanguageSwitcherData.success) {
|
||||
getLanguageSwitcherFailCounter.add(1, {
|
||||
uid,
|
||||
lang,
|
||||
contentType,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedLanguageSwitcherData.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.languageSwitcher validation error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
uid,
|
||||
lang,
|
||||
contentType,
|
||||
},
|
||||
error: validatedLanguageSwitcherData.error,
|
||||
})
|
||||
metricsGetLanguageSwitcher.validationError(
|
||||
validatedLanguageSwitcherData.error
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
getLanguageSwitcherSuccessCounter.add(1, {
|
||||
uid,
|
||||
lang,
|
||||
contentType,
|
||||
})
|
||||
console.info(
|
||||
"contentstack.languageSwitcher success",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
uid,
|
||||
lang,
|
||||
contentType,
|
||||
},
|
||||
})
|
||||
)
|
||||
metricsGetLanguageSwitcher.success()
|
||||
|
||||
return urls
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
import { cache } from "react"
|
||||
|
||||
import {
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
} from "@/lib/graphql/Query/LoyaltyLevels.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import { contentstackBaseProcedure, router } from "@/server/trpc"
|
||||
|
||||
import { generateLoyaltyConfigTag } from "@/utils/generateTag"
|
||||
@@ -24,32 +24,14 @@ import {
|
||||
|
||||
import type { Context } from "@/server/context"
|
||||
|
||||
const meter = metrics.getMeter("trpc.loyaltyLevel")
|
||||
// OpenTelemetry metrics: Loyalty Level
|
||||
const getAllLoyaltyLevelCounter = meter.createCounter(
|
||||
"trpc.contentstack.loyaltyLevel.all"
|
||||
)
|
||||
|
||||
const getAllLoyaltyLevelSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.loyaltyLevel.all-success"
|
||||
)
|
||||
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 const getAllLoyaltyLevels = cache(async (ctx: Context) => {
|
||||
getAllLoyaltyLevelCounter.add(1)
|
||||
const getLoyaltyLevelAllCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"loyaltyLevel.all"
|
||||
)
|
||||
const metricsGetLoyaltyLevelAll = getLoyaltyLevelAllCounter.init()
|
||||
|
||||
metricsGetLoyaltyLevelAll.start()
|
||||
|
||||
// Ideally we should fetch all available tiers from API, but since they
|
||||
// are static, we can just use the enum values. We want to know which
|
||||
@@ -67,17 +49,8 @@ export const getAllLoyaltyLevels = cache(async (ctx: Context) => {
|
||||
)
|
||||
|
||||
if (!loyaltyLevelsConfigResponse.data) {
|
||||
getAllLoyaltyLevelFailCounter.add(1)
|
||||
const notFoundError = notFound(loyaltyLevelsConfigResponse)
|
||||
console.error(
|
||||
"contentstack.loyaltyLevels not found error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang: ctx.lang,
|
||||
},
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetLoyaltyLevelAll.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -85,30 +58,28 @@ export const getAllLoyaltyLevels = cache(async (ctx: Context) => {
|
||||
loyaltyLevelsConfigResponse.data
|
||||
)
|
||||
if (!validatedLoyaltyLevels.success) {
|
||||
getAllLoyaltyLevelFailCounter.add(1)
|
||||
console.error(validatedLoyaltyLevels.error)
|
||||
console.error(
|
||||
"contentstack.rewards validation error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang: ctx.lang,
|
||||
},
|
||||
error: validatedLoyaltyLevels.error,
|
||||
})
|
||||
)
|
||||
metricsGetLoyaltyLevelAll.validationError(validatedLoyaltyLevels.error)
|
||||
return []
|
||||
}
|
||||
|
||||
getAllLoyaltyLevelSuccessCounter.add(1)
|
||||
metricsGetLoyaltyLevelAll.success()
|
||||
|
||||
return validatedLoyaltyLevels.data
|
||||
})
|
||||
|
||||
export const getLoyaltyLevel = cache(
|
||||
async (ctx: Context, level_id: MembershipLevel) => {
|
||||
getByLevelLoyaltyLevelCounter.add(1, {
|
||||
query: JSON.stringify({ lang: ctx.lang, level_id }),
|
||||
const getLoyaltyLevelCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"loyaltyLevel.get"
|
||||
)
|
||||
const metricsGetLoyaltyLevel = getLoyaltyLevelCounter.init({
|
||||
lang: ctx.lang,
|
||||
level_id,
|
||||
})
|
||||
|
||||
metricsGetLoyaltyLevel.start()
|
||||
|
||||
const loyaltyLevelsConfigResponse = await request<LoyaltyLevelsResponse>(
|
||||
GetLoyaltyLevel,
|
||||
{ lang: ctx.lang, level_id },
|
||||
@@ -121,15 +92,8 @@ export const getLoyaltyLevel = cache(
|
||||
!loyaltyLevelsConfigResponse.data ||
|
||||
!loyaltyLevelsConfigResponse.data.all_loyalty_level.items.length
|
||||
) {
|
||||
getByLevelLoyaltyLevelFailCounter.add(1)
|
||||
const notFoundError = notFound(loyaltyLevelsConfigResponse)
|
||||
console.error(
|
||||
"contentstack.loyaltyLevel not found error",
|
||||
JSON.stringify({
|
||||
query: { lang: ctx.lang, level_id },
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetLoyaltyLevel.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -137,19 +101,12 @@ export const getLoyaltyLevel = cache(
|
||||
loyaltyLevelsConfigResponse.data
|
||||
)
|
||||
if (!validatedLoyaltyLevels.success) {
|
||||
getByLevelLoyaltyLevelFailCounter.add(1)
|
||||
console.error(validatedLoyaltyLevels.error)
|
||||
console.error(
|
||||
"contentstack.loyaltyLevel validation error",
|
||||
JSON.stringify({
|
||||
query: { lang: ctx.lang, level_id },
|
||||
error: validatedLoyaltyLevels.error,
|
||||
})
|
||||
)
|
||||
metricsGetLoyaltyLevel.validationError(validatedLoyaltyLevels.error)
|
||||
return null
|
||||
}
|
||||
|
||||
getByLevelLoyaltyLevelSuccessCounter.add(1)
|
||||
metricsGetLoyaltyLevel.success()
|
||||
|
||||
const result: LoyaltyLevel = validatedLoyaltyLevels.data[0]
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
import {
|
||||
GetLoyaltyPage,
|
||||
GetLoyaltyPageRefs,
|
||||
} from "@/lib/graphql/Query/LoyaltyPage/LoyaltyPage.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
||||
|
||||
import {
|
||||
@@ -26,39 +25,22 @@ import type {
|
||||
GetLoyaltyPageSchema,
|
||||
} from "@/types/trpc/routers/contentstack/loyaltyPage"
|
||||
|
||||
const meter = metrics.getMeter("trpc.loyaltyPage")
|
||||
// OpenTelemetry metrics: LoyaltyPage
|
||||
const getLoyaltyPageRefsCounter = meter.createCounter(
|
||||
"trpc.contentstack.loyaltyPage.get"
|
||||
)
|
||||
const getLoyaltyPageRefsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.loyaltyPage.get-success"
|
||||
)
|
||||
const getLoyaltyPageRefsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.loyaltyPage.get-fail"
|
||||
)
|
||||
const getLoyaltyPageCounter = meter.createCounter(
|
||||
"trpc.contentstack.loyaltyPage.get"
|
||||
)
|
||||
const getLoyaltyPageSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.loyaltyPage.get-success"
|
||||
)
|
||||
const getLoyaltyPageFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.loyaltyPage.get-fail"
|
||||
)
|
||||
|
||||
export const loyaltyPageQueryRouter = router({
|
||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
||||
const { lang, uid } = ctx
|
||||
const metricsVariables = { lang, uid }
|
||||
const variables = { locale: lang, uid }
|
||||
getLoyaltyPageRefsCounter.add(1, metricsVariables)
|
||||
console.info(
|
||||
"contentstack.loyaltyPage.refs start",
|
||||
JSON.stringify({
|
||||
query: metricsVariables,
|
||||
})
|
||||
|
||||
const getLoyaltyPageRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"loyaltyPage.get.refs"
|
||||
)
|
||||
const metricsGetLoyaltyPageRefs = getLoyaltyPageRefsCounter.init({
|
||||
lang,
|
||||
uid,
|
||||
})
|
||||
|
||||
metricsGetLoyaltyPageRefs.start()
|
||||
|
||||
const variables = { locale: lang, uid }
|
||||
const refsResponse = await request<GetLoyaltyPageRefsSchema>(
|
||||
GetLoyaltyPageRefs,
|
||||
variables,
|
||||
@@ -70,20 +52,7 @@ export const loyaltyPageQueryRouter = router({
|
||||
|
||||
if (!refsResponse.data) {
|
||||
const notFoundError = notFound(refsResponse)
|
||||
getLoyaltyPageRefsFailCounter.add(1, {
|
||||
...metricsVariables,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
code: notFoundError.code,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.loyaltyPage.refs not found error",
|
||||
JSON.stringify({
|
||||
query: metricsVariables,
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetLoyaltyPageRefs.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -91,27 +60,11 @@ export const loyaltyPageQueryRouter = router({
|
||||
refsResponse.data
|
||||
)
|
||||
if (!validatedLoyaltyPageRefs.success) {
|
||||
getLoyaltyPageRefsFailCounter.add(1, {
|
||||
...metricsVariables,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedLoyaltyPageRefs.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.loyaltyPage.refs validation error",
|
||||
JSON.stringify({
|
||||
query: metricsVariables,
|
||||
error: validatedLoyaltyPageRefs.error,
|
||||
})
|
||||
)
|
||||
metricsGetLoyaltyPageRefs.validationError(validatedLoyaltyPageRefs.error)
|
||||
return null
|
||||
}
|
||||
getLoyaltyPageRefsSuccessCounter.add(1, metricsVariables)
|
||||
console.info(
|
||||
"contentstack.loyaltyPage.refs success",
|
||||
JSON.stringify({
|
||||
query: metricsVariables,
|
||||
})
|
||||
)
|
||||
|
||||
metricsGetLoyaltyPageRefs.success()
|
||||
|
||||
const connections = getConnections(validatedLoyaltyPageRefs.data)
|
||||
|
||||
@@ -119,13 +72,15 @@ export const loyaltyPageQueryRouter = router({
|
||||
generateTagsFromSystem(lang, connections),
|
||||
generateTag(lang, validatedLoyaltyPageRefs.data.loyalty_page.system.uid),
|
||||
].flat()
|
||||
getLoyaltyPageCounter.add(1, metricsVariables)
|
||||
console.info(
|
||||
"contentstack.loyaltyPage start",
|
||||
JSON.stringify({
|
||||
query: metricsVariables,
|
||||
})
|
||||
|
||||
const getLoyaltyPageCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"loyaltyPage.get"
|
||||
)
|
||||
const metricsGetLoyaltyPage = getLoyaltyPageCounter.init({ lang, uid })
|
||||
|
||||
metricsGetLoyaltyPage.start()
|
||||
|
||||
const response = await request<GetLoyaltyPageSchema>(
|
||||
GetLoyaltyPage,
|
||||
variables,
|
||||
@@ -137,35 +92,13 @@ export const loyaltyPageQueryRouter = router({
|
||||
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
getLoyaltyPageFailCounter.add(1, {
|
||||
...metricsVariables,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.loyaltyPage not found error",
|
||||
JSON.stringify({
|
||||
query: metricsVariables,
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
throw notFound(response)
|
||||
metricsGetLoyaltyPage.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const validatedLoyaltyPage = loyaltyPageSchema.safeParse(response.data)
|
||||
if (!validatedLoyaltyPage.success) {
|
||||
getLoyaltyPageFailCounter.add(1, {
|
||||
...metricsVariables,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedLoyaltyPage.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.loyaltyPage validation error",
|
||||
JSON.stringify({
|
||||
query: metricsVariables,
|
||||
error: validatedLoyaltyPage.error,
|
||||
})
|
||||
)
|
||||
metricsGetLoyaltyPage.validationError(validatedLoyaltyPage.error)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -182,11 +115,8 @@ export const loyaltyPageQueryRouter = router({
|
||||
siteSections: validatedLoyaltyPage.data.trackingProps.url,
|
||||
siteVersion: "new-web",
|
||||
}
|
||||
getLoyaltyPageSuccessCounter.add(1, metricsVariables)
|
||||
console.info(
|
||||
"contentstack.loyaltyPage success",
|
||||
JSON.stringify({ query: metricsVariables })
|
||||
)
|
||||
|
||||
metricsGetLoyaltyPage.success()
|
||||
|
||||
// Assert LoyaltyPage type to get correct typings for RTE fields
|
||||
return {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
import { cache } from "react"
|
||||
|
||||
import { GetAccountPageMetadata } from "@/lib/graphql/Query/AccountPage/Metadata.graphql"
|
||||
@@ -12,6 +11,7 @@ import { GetLoyaltyPageMetadata } from "@/lib/graphql/Query/LoyaltyPage/Metadata
|
||||
import { GetStartPageMetadata } from "@/lib/graphql/Query/StartPage/Metadata.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import { contentStackUidWithServiceProcedure, router } from "@/server/trpc"
|
||||
|
||||
import { generateTag } from "@/utils/generateTag"
|
||||
@@ -29,37 +29,15 @@ import type { LanguageSwitcherData } from "@/types/requests/languageSwitcher"
|
||||
import type { RawMetadataSchema } from "@/types/trpc/routers/contentstack/metadata"
|
||||
import type { Lang } from "@/constants/languages"
|
||||
|
||||
const meter = metrics.getMeter("trpc.metadata")
|
||||
|
||||
// OpenTelemetry metrics
|
||||
const fetchMetadataCounter = meter.createCounter(
|
||||
"trpc.contentstack.metadata.get"
|
||||
)
|
||||
const fetchMetadataSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.metadata.get-success"
|
||||
)
|
||||
const fetchMetadataFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.metadata.get-fail"
|
||||
)
|
||||
const transformMetadataCounter = meter.createCounter(
|
||||
"trpc.contentstack.metadata.transform"
|
||||
)
|
||||
const transformMetadataSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.metadata.transform-success"
|
||||
)
|
||||
const transformMetadataFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.metadata.transform-fail"
|
||||
)
|
||||
|
||||
const fetchMetadata = cache(async function fetchMemoizedMetadata<T>(
|
||||
query: string,
|
||||
{ uid, lang }: { uid: string; lang: Lang }
|
||||
) {
|
||||
fetchMetadataCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.metadata fetch start",
|
||||
JSON.stringify({ query: { lang, uid } })
|
||||
)
|
||||
const getMetadataCounter = createCounter("trpc.contentstack", "metadata.get")
|
||||
const metricsGetMetadata = getMetadataCounter.init({ lang, uid })
|
||||
|
||||
metricsGetMetadata.start()
|
||||
|
||||
const response = await request<T>(
|
||||
query,
|
||||
{ locale: lang, uid },
|
||||
@@ -68,54 +46,35 @@ const fetchMetadata = cache(async function fetchMemoizedMetadata<T>(
|
||||
ttl: "max",
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
fetchMetadataFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.metadata fetch not found error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetMetadata.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
fetchMetadataSuccessCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
"contentstack.metadata fetch success",
|
||||
JSON.stringify({ query: { lang, uid } })
|
||||
)
|
||||
metricsGetMetadata.success()
|
||||
|
||||
return response.data
|
||||
})
|
||||
|
||||
async function getTransformedMetadata(data: unknown) {
|
||||
transformMetadataCounter.add(1)
|
||||
console.info("contentstack.metadata transform start")
|
||||
const transformMetadataCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"metadata.transform"
|
||||
)
|
||||
const metricsTransformMetadata = transformMetadataCounter.init()
|
||||
|
||||
metricsTransformMetadata.start()
|
||||
|
||||
const validatedMetadata = await metadataSchema.safeParseAsync(data)
|
||||
|
||||
if (!validatedMetadata.success) {
|
||||
transformMetadataFailCounter.add(1, {
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedMetadata.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.metadata validation error",
|
||||
JSON.stringify({
|
||||
error: validatedMetadata.error,
|
||||
})
|
||||
)
|
||||
metricsTransformMetadata.validationError(validatedMetadata.error)
|
||||
return null
|
||||
}
|
||||
|
||||
transformMetadataSuccessCounter.add(1)
|
||||
console.info("contentstack.metadata transform success")
|
||||
metricsTransformMetadata.success()
|
||||
|
||||
return validatedMetadata.data
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
import { cache } from "react"
|
||||
|
||||
import { GetAllSasTierComparison } from "@/lib/graphql/Query/SASTierComparison.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import { contentstackBaseProcedure, router } from "@/server/trpc"
|
||||
|
||||
import { validateSasTierComparisonSchema } from "./output"
|
||||
@@ -11,20 +11,16 @@ import { validateSasTierComparisonSchema } from "./output"
|
||||
import type { SasTierComparisonResponse } from "@/types/trpc/routers/contentstack/partner"
|
||||
import type { Context } from "@/server/context"
|
||||
|
||||
const meter = metrics.getMeter("trpc.partner")
|
||||
const getSasTierComparisonCounter = meter.createCounter(
|
||||
"trpc.contentstack.partner.getSasTierComparison"
|
||||
)
|
||||
|
||||
const getSasTierComparisonSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.partner.getSasTierComparison-success"
|
||||
)
|
||||
const getSasTierComparisonFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.partner.getSasTierComparison-fail"
|
||||
)
|
||||
|
||||
export const getSasTierComparison = cache(async (ctx: Context) => {
|
||||
getSasTierComparisonCounter.add(1)
|
||||
const getSasTierComparisonCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"partner.getSasTierComparison"
|
||||
)
|
||||
const metricsGetSasTierComparison = getSasTierComparisonCounter.init({
|
||||
lang: ctx.lang,
|
||||
})
|
||||
|
||||
metricsGetSasTierComparison.start()
|
||||
|
||||
const tag = `${ctx.lang}:sas_tier_comparison`
|
||||
const sasTierComparisonConfigResponse =
|
||||
@@ -38,17 +34,8 @@ export const getSasTierComparison = cache(async (ctx: Context) => {
|
||||
)
|
||||
|
||||
if (!sasTierComparisonConfigResponse.data) {
|
||||
getSasTierComparisonFailCounter.add(1)
|
||||
const notFoundError = notFound(sasTierComparisonConfigResponse)
|
||||
console.error(
|
||||
"contentstack.sas not found error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang: ctx.lang,
|
||||
},
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetSasTierComparison.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -57,21 +44,14 @@ export const getSasTierComparison = cache(async (ctx: Context) => {
|
||||
)
|
||||
|
||||
if (!validatedSasTierComparison.success) {
|
||||
getSasTierComparisonFailCounter.add(1)
|
||||
console.error(validatedSasTierComparison.error)
|
||||
console.error(
|
||||
"contentstack.sas validation error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang: ctx.lang,
|
||||
},
|
||||
error: validatedSasTierComparison.error,
|
||||
})
|
||||
metricsGetSasTierComparison.validationError(
|
||||
validatedSasTierComparison.error
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
getSasTierComparisonSuccessCounter.add(1)
|
||||
metricsGetSasTierComparison.success()
|
||||
|
||||
return validatedSasTierComparison.data
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as api from "@/lib/api"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import {
|
||||
contentStackBaseWithProtectedProcedure,
|
||||
contentStackBaseWithServiceProcedure,
|
||||
@@ -22,24 +23,9 @@ import {
|
||||
} from "./input"
|
||||
import { validateCategorizedRewardsSchema } from "./output"
|
||||
import {
|
||||
getAllRewardCounter,
|
||||
getAllRewardFailCounter,
|
||||
getAllRewardSuccessCounter,
|
||||
getByLevelRewardCounter,
|
||||
getByLevelRewardFailCounter,
|
||||
getByLevelRewardSuccessCounter,
|
||||
getCachedAllTierRewards,
|
||||
getCmsRewards,
|
||||
getCurrentRewardCounter,
|
||||
getCurrentRewardFailCounter,
|
||||
getCurrentRewardSuccessCounter,
|
||||
getRedeemCounter,
|
||||
getRedeemFailCounter,
|
||||
getRedeemSuccessCounter,
|
||||
getUniqueRewardIds,
|
||||
getUnwrapSurpriseCounter,
|
||||
getUnwrapSurpriseFailCounter,
|
||||
getUnwrapSurpriseSuccessCounter,
|
||||
} from "./utils"
|
||||
|
||||
import type { BaseReward, Surprise } from "@/types/components/myPages/rewards"
|
||||
@@ -50,7 +36,14 @@ export const rewardQueryRouter = router({
|
||||
all: contentStackBaseWithServiceProcedure
|
||||
.input(rewardsAllInput)
|
||||
.query(async function ({ input, ctx }) {
|
||||
getAllRewardCounter.add(1)
|
||||
const getContentstackRewardAllCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"reward.all"
|
||||
)
|
||||
const metricsGetContentstackRewardAll =
|
||||
getContentstackRewardAllCounter.init()
|
||||
|
||||
metricsGetContentstackRewardAll.start()
|
||||
|
||||
const allApiRewards = await getCachedAllTierRewards(ctx.serviceToken)
|
||||
|
||||
@@ -75,16 +68,23 @@ export const rewardQueryRouter = router({
|
||||
const levelsWithRewards = Object.entries(allApiRewards).map(
|
||||
([level, rewards]) => {
|
||||
const combinedRewards = rewards
|
||||
.filter((r) => (input.unique ? r?.rewardTierLevel === level : true))
|
||||
.filter((reward) =>
|
||||
input.unique ? reward.rewardTierLevel === level : true
|
||||
)
|
||||
.map((reward) => {
|
||||
const contentStackReward = contentStackRewards.find((r) => {
|
||||
return r.reward_id === reward?.rewardId
|
||||
return r.reward_id === reward.rewardId
|
||||
})
|
||||
|
||||
if (contentStackReward) {
|
||||
return contentStackReward
|
||||
} else {
|
||||
console.error("No contentStackReward found", reward?.rewardId)
|
||||
metricsGetContentstackRewardAll.dataError(
|
||||
`Failed to find reward in CMS for reward ${reward.rewardId} `,
|
||||
{
|
||||
rewardId: reward.rewardId,
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
.filter((reward): reward is CMSReward => Boolean(reward))
|
||||
@@ -94,9 +94,9 @@ export const rewardQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!levelConfig) {
|
||||
getAllRewardFailCounter.add(1)
|
||||
|
||||
console.error("contentstack.loyaltyLevels level not found")
|
||||
metricsGetContentstackRewardAll.dataError(
|
||||
`Failed to matched loyalty level between API and CMS for level ${level}`
|
||||
)
|
||||
throw notFound()
|
||||
}
|
||||
const result: LevelWithRewards = {
|
||||
@@ -107,21 +107,31 @@ export const rewardQueryRouter = router({
|
||||
}
|
||||
)
|
||||
|
||||
getAllRewardSuccessCounter.add(1)
|
||||
metricsGetContentstackRewardAll.success()
|
||||
|
||||
return levelsWithRewards
|
||||
}),
|
||||
byLevel: contentStackBaseWithServiceProcedure
|
||||
.input(rewardsByLevelInput)
|
||||
.query(async function ({ input, ctx }) {
|
||||
getByLevelRewardCounter.add(1)
|
||||
const { level_id } = input
|
||||
|
||||
const getRewardByLevelCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"reward.byLevel"
|
||||
)
|
||||
const metricsGetRewardByLevel = getRewardByLevelCounter.init({
|
||||
level_id,
|
||||
})
|
||||
|
||||
metricsGetRewardByLevel.start()
|
||||
|
||||
const allUpcomingApiRewards = await getCachedAllTierRewards(
|
||||
ctx.serviceToken
|
||||
)
|
||||
|
||||
if (!allUpcomingApiRewards || !allUpcomingApiRewards[level_id]) {
|
||||
getByLevelRewardFailCounter.add(1)
|
||||
metricsGetRewardByLevel.noDataError()
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -150,24 +160,36 @@ export const rewardQueryRouter = router({
|
||||
const levelsWithRewards = apiRewards
|
||||
.map((reward) => {
|
||||
const contentStackReward = contentStackRewards.find((r) => {
|
||||
return r.reward_id === reward?.rewardId
|
||||
return r.reward_id === reward.rewardId
|
||||
})
|
||||
|
||||
if (contentStackReward) {
|
||||
return contentStackReward
|
||||
} else {
|
||||
console.info("No contentStackReward found", reward?.rewardId)
|
||||
metricsGetRewardByLevel.dataError(
|
||||
`Failed to find reward in Contentstack with rewardId: ${reward.rewardId}`,
|
||||
{
|
||||
rewardId: reward.rewardId,
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
.filter((reward): reward is CMSReward => Boolean(reward))
|
||||
|
||||
getByLevelRewardSuccessCounter.add(1)
|
||||
metricsGetRewardByLevel.success()
|
||||
|
||||
return { level: loyaltyLevelsConfig, rewards: levelsWithRewards }
|
||||
}),
|
||||
current: contentStackBaseWithProtectedProcedure
|
||||
.input(langInput.optional()) // lang is required for client, but not for server
|
||||
.query(async function ({ ctx }) {
|
||||
getCurrentRewardCounter.add(1)
|
||||
const getCurrentRewardCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"reward.current"
|
||||
)
|
||||
const metricsGetCurrentReward = getCurrentRewardCounter.init()
|
||||
|
||||
metricsGetCurrentReward.start()
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Profile.Reward.reward,
|
||||
@@ -179,25 +201,7 @@ export const rewardQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
getCurrentRewardFailCounter.add(1, {
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.reward error ",
|
||||
JSON.stringify({
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
await metricsGetCurrentReward.httpError(apiResponse)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -207,19 +211,7 @@ export const rewardQueryRouter = router({
|
||||
validateCategorizedRewardsSchema.safeParse(data)
|
||||
|
||||
if (!validatedApiRewards.success) {
|
||||
getCurrentRewardFailCounter.add(1, {
|
||||
locale: ctx.lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedApiRewards.error),
|
||||
})
|
||||
console.error(validatedApiRewards.error)
|
||||
console.error(
|
||||
"contentstack.rewards validation error",
|
||||
JSON.stringify({
|
||||
query: { locale: ctx.lang },
|
||||
error: validatedApiRewards.error,
|
||||
})
|
||||
)
|
||||
metricsGetCurrentReward.validationError(validatedApiRewards.error)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -243,14 +235,20 @@ export const rewardQueryRouter = router({
|
||||
}
|
||||
})
|
||||
|
||||
getCurrentRewardSuccessCounter.add(1)
|
||||
metricsGetCurrentReward.success()
|
||||
|
||||
return { rewards }
|
||||
}),
|
||||
surprises: contentStackBaseWithProtectedProcedure
|
||||
.input(langInput.optional()) // lang is required for client, but not for server
|
||||
.query(async ({ ctx }) => {
|
||||
getCurrentRewardCounter.add(1)
|
||||
const getSurprisesCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"surprises"
|
||||
)
|
||||
const metricsGetSurprises = getSurprisesCounter.init()
|
||||
|
||||
metricsGetSurprises.start()
|
||||
|
||||
const endpoint = api.endpoints.v1.Profile.Reward.reward
|
||||
|
||||
@@ -262,25 +260,7 @@ export const rewardQueryRouter = router({
|
||||
})
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
getCurrentRewardFailCounter.add(1, {
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.reward error ",
|
||||
JSON.stringify({
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
await metricsGetSurprises.httpError(apiResponse)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -289,19 +269,7 @@ export const rewardQueryRouter = router({
|
||||
validateCategorizedRewardsSchema.safeParse(data)
|
||||
|
||||
if (!validatedApiRewards.success) {
|
||||
getCurrentRewardFailCounter.add(1, {
|
||||
locale: ctx.lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedApiRewards.error),
|
||||
})
|
||||
console.error(validatedApiRewards.error)
|
||||
console.error(
|
||||
"contentstack.surprises validation error",
|
||||
JSON.stringify({
|
||||
query: { locale: ctx.lang },
|
||||
error: validatedApiRewards.error,
|
||||
})
|
||||
)
|
||||
metricsGetSurprises.validationError(validatedApiRewards.error)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -316,8 +284,6 @@ export const rewardQueryRouter = router({
|
||||
return null
|
||||
}
|
||||
|
||||
getCurrentRewardSuccessCounter.add(1)
|
||||
|
||||
const surprises: Surprise[] = cmsRewards
|
||||
.map((cmsReward) => {
|
||||
// Non-null assertion is used here because we know our reward exist
|
||||
@@ -336,73 +302,80 @@ export const rewardQueryRouter = router({
|
||||
})
|
||||
.flatMap((surprises) => (surprises ? [surprises] : []))
|
||||
|
||||
metricsGetSurprises.success()
|
||||
|
||||
return surprises
|
||||
}),
|
||||
unwrap: protectedProcedure
|
||||
.input(rewardsUpdateInput)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
getUnwrapSurpriseCounter.add(1)
|
||||
const results = await Promise.allSettled(
|
||||
// Execute each unwrap individually
|
||||
input.map(({ rewardId, couponCode }) => {
|
||||
async function handleUnwrap() {
|
||||
const getUnwrapSurpriseCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"reward.unwrap"
|
||||
)
|
||||
|
||||
const promises = input.map(({ rewardId, couponCode }) => {
|
||||
return api.post(api.endpoints.v1.Profile.Reward.unwrap, {
|
||||
body: {
|
||||
rewardId,
|
||||
couponCode,
|
||||
},
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
const responses = await Promise.all(promises)
|
||||
|
||||
const errors = await Promise.all(
|
||||
responses.map(async (apiResponse) => {
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
|
||||
getUnwrapSurpriseFailCounter.add(1, {
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
const metricsGetUnwrapSurprise = getUnwrapSurpriseCounter.init({
|
||||
rewardId,
|
||||
couponCode,
|
||||
})
|
||||
|
||||
console.error(
|
||||
"contentstack.unwrap API error",
|
||||
JSON.stringify({
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
metricsGetUnwrapSurprise.start()
|
||||
|
||||
const apiResponse = await api.post(
|
||||
api.endpoints.v1.Profile.Reward.unwrap,
|
||||
{
|
||||
body: {
|
||||
rewardId,
|
||||
couponCode,
|
||||
},
|
||||
query: {},
|
||||
})
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
return false
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
metricsGetUnwrapSurprise.httpError(apiResponse)
|
||||
return false
|
||||
}
|
||||
|
||||
metricsGetUnwrapSurprise.success()
|
||||
|
||||
return true
|
||||
}
|
||||
return true
|
||||
|
||||
return handleUnwrap()
|
||||
})
|
||||
)
|
||||
|
||||
if (errors.filter((ok) => !ok).length > 0) {
|
||||
if (
|
||||
results.some(
|
||||
(result) => result.status === "rejected" || result.value === false
|
||||
)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
getUnwrapSurpriseSuccessCounter.add(1)
|
||||
|
||||
return true
|
||||
}),
|
||||
redeem: protectedProcedure
|
||||
.input(rewardsRedeemInput)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
getRedeemCounter.add(1)
|
||||
|
||||
const { rewardId, couponCode } = input
|
||||
|
||||
const getRedeemCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"reward.redeem"
|
||||
)
|
||||
|
||||
const metricGetRedeem = getRedeemCounter.init({ rewardId, couponCode })
|
||||
|
||||
metricGetRedeem.start()
|
||||
|
||||
const apiResponse = await api.post(
|
||||
api.endpoints.v1.Profile.Reward.redeem,
|
||||
{
|
||||
@@ -417,29 +390,11 @@ export const rewardQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
getRedeemFailCounter.add(1, {
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.redeem error ",
|
||||
JSON.stringify({
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
metricGetRedeem.httpError(apiResponse)
|
||||
return null
|
||||
}
|
||||
|
||||
getRedeemSuccessCounter.add(1)
|
||||
metricGetRedeem.success()
|
||||
|
||||
return true
|
||||
}),
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
import * as api from "@/lib/api"
|
||||
import {
|
||||
GetRewards as GetRewards,
|
||||
@@ -7,6 +5,7 @@ import {
|
||||
} from "@/lib/graphql/Query/RewardsWithRedeem.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
|
||||
import { getCacheClient } from "@/services/dataCache"
|
||||
import {
|
||||
@@ -26,63 +25,6 @@ import type {
|
||||
} 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)
|
||||
@@ -97,6 +39,14 @@ export async function getCachedAllTierRewards(token: string) {
|
||||
return await cacheClient.cacheOrGet(
|
||||
"getAllTierRewards",
|
||||
async () => {
|
||||
const getApiRewardAllTiersCounter = createCounter(
|
||||
"trpc.api",
|
||||
"reward.allTiers"
|
||||
)
|
||||
const metricsGetApiRewardAllTiers = getApiRewardAllTiersCounter.init()
|
||||
|
||||
metricsGetApiRewardAllTiers.start()
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Profile.Reward.allTiers,
|
||||
{
|
||||
@@ -107,26 +57,7 @@ export async function getCachedAllTierRewards(token: string) {
|
||||
)
|
||||
|
||||
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,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
metricsGetApiRewardAllTiers.httpError(apiResponse)
|
||||
throw apiResponse
|
||||
}
|
||||
|
||||
@@ -135,20 +66,14 @@ export async function getCachedAllTierRewards(token: string) {
|
||||
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,
|
||||
})
|
||||
metricsGetApiRewardAllTiers.validationError(
|
||||
validatedApiAllTierRewards.error
|
||||
)
|
||||
throw validatedApiAllTierRewards.error
|
||||
}
|
||||
|
||||
metricsGetApiRewardAllTiers.success()
|
||||
|
||||
return validatedApiAllTierRewards.data
|
||||
},
|
||||
"1h"
|
||||
@@ -164,13 +89,15 @@ export async function getCmsRewards(lang: Lang, rewardIds: string[]) {
|
||||
generateLoyaltyConfigTag(lang, "reward", id)
|
||||
)
|
||||
|
||||
getAllCMSRewardRefsCounter.add(1, { lang, rewardIds })
|
||||
console.info(
|
||||
"contentstack.reward.refs start",
|
||||
JSON.stringify({
|
||||
query: { lang, rewardIds },
|
||||
})
|
||||
const getContentstackRewardAllRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"reward.all.refs"
|
||||
)
|
||||
const metricsGetContentstackRewardAllRefs =
|
||||
getContentstackRewardAllRefsCounter.init({ lang, rewardIds })
|
||||
|
||||
metricsGetContentstackRewardAllRefs.start()
|
||||
|
||||
const refsResponse = await request<GetRewardRefsSchema>(
|
||||
GetRewardsRef,
|
||||
{
|
||||
@@ -182,50 +109,30 @@ export async function getCmsRewards(lang: Lang, rewardIds: string[]) {
|
||||
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 },
|
||||
})
|
||||
)
|
||||
metricsGetContentstackRewardAllRefs.noDataError()
|
||||
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,
|
||||
})
|
||||
)
|
||||
metricsGetContentstackRewardAllRefs.validationError(validatedRefsData.error)
|
||||
return null
|
||||
}
|
||||
|
||||
getAllCMSRewardRefsSuccessCounter.add(1, { lang, rewardIds })
|
||||
console.info(
|
||||
"contentstack.startPage.refs success",
|
||||
JSON.stringify({
|
||||
query: { lang, rewardIds },
|
||||
})
|
||||
metricsGetContentstackRewardAllRefs.success()
|
||||
|
||||
const getContentstackRewardAllCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"reward.all"
|
||||
)
|
||||
const metricsGetContentstackRewardAll = getContentstackRewardAllCounter.init({
|
||||
lang,
|
||||
rewardIds,
|
||||
})
|
||||
|
||||
const cmsRewardsResponse = await request<CMSRewardsResponse>(
|
||||
GetRewards,
|
||||
@@ -240,22 +147,8 @@ export async function getCmsRewards(lang: Lang, rewardIds: string[]) {
|
||||
)
|
||||
|
||||
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 },
|
||||
})
|
||||
)
|
||||
metricsGetContentstackRewardAll.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
@@ -263,22 +156,11 @@ export async function getCmsRewards(lang: Lang, rewardIds: string[]) {
|
||||
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,
|
||||
})
|
||||
)
|
||||
metricsGetContentstackRewardAll.validationError(validatedCmsRewards.error)
|
||||
return null
|
||||
}
|
||||
|
||||
metricsGetContentstackRewardAll.success()
|
||||
|
||||
return validatedCmsRewards.data
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
} from "@/lib/graphql/Query/StartPage/StartPage.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
||||
|
||||
import {
|
||||
@@ -13,14 +14,6 @@ import {
|
||||
} from "@/utils/generateTag"
|
||||
|
||||
import { startPageRefsSchema, startPageSchema } from "./output"
|
||||
import {
|
||||
getStartPageCounter,
|
||||
getStartPageFailCounter,
|
||||
getStartPageRefsCounter,
|
||||
getStartPageRefsFailCounter,
|
||||
getStartPageRefsSuccessCounter,
|
||||
getStartPageSuccessCounter,
|
||||
} from "./telemetry"
|
||||
import { getConnections } from "./utils"
|
||||
|
||||
import {
|
||||
@@ -36,13 +29,14 @@ export const startPageQueryRouter = router({
|
||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
||||
const { lang, uid } = ctx
|
||||
|
||||
getStartPageRefsCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.startPage.refs start",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
const getStartPageRefsCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"startPage.get.refs"
|
||||
)
|
||||
const metricsGetStartPageRefs = getStartPageRefsCounter.init({ lang, uid })
|
||||
|
||||
metricsGetStartPageRefs.start()
|
||||
|
||||
const refsResponse = await request<GetStartPageRefsSchema>(
|
||||
GetStartPageRefs,
|
||||
{
|
||||
@@ -56,56 +50,26 @@ export const startPageQueryRouter = router({
|
||||
)
|
||||
if (!refsResponse.data) {
|
||||
const notFoundError = notFound(refsResponse)
|
||||
getStartPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.startPage.refs not found error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetStartPageRefs.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const validatedRefsData = startPageRefsSchema.safeParse(refsResponse.data)
|
||||
|
||||
if (!validatedRefsData.success) {
|
||||
getStartPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
uid,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedRefsData.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.startPage.refs validation error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: validatedRefsData.error,
|
||||
})
|
||||
)
|
||||
metricsGetStartPageRefs.validationError(validatedRefsData.error)
|
||||
return null
|
||||
}
|
||||
|
||||
getStartPageRefsSuccessCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.startPage.refs success",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
)
|
||||
metricsGetStartPageRefs.success()
|
||||
|
||||
getStartPageCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.startPage start",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
const getStartPageCounter = createCounter(
|
||||
"trpc.contentstack",
|
||||
"startPage.get"
|
||||
)
|
||||
const metricsGetStartPage = getStartPageCounter.init({ lang, uid })
|
||||
|
||||
metricsGetStartPage.start()
|
||||
|
||||
const connections = getConnections(validatedRefsData.data)
|
||||
|
||||
@@ -113,6 +77,7 @@ export const startPageQueryRouter = router({
|
||||
generateTagsFromSystem(lang, connections),
|
||||
generateTag(lang, validatedRefsData.data.start_page.system.uid),
|
||||
].flat()
|
||||
|
||||
const response = await request<GetStartPageData>(
|
||||
GetStartPage,
|
||||
{
|
||||
@@ -127,48 +92,18 @@ export const startPageQueryRouter = router({
|
||||
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
getStartPageFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.startPage not found error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
metricsGetStartPage.noDataError()
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const startPage = startPageSchema.safeParse(response.data)
|
||||
|
||||
if (!startPage.success) {
|
||||
getStartPageFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(startPage.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.startPage validation error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: startPage.error,
|
||||
})
|
||||
)
|
||||
metricsGetStartPage.validationError(startPage.error)
|
||||
return null
|
||||
}
|
||||
|
||||
getStartPageSuccessCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.startPage success",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
})
|
||||
)
|
||||
metricsGetStartPage.success()
|
||||
|
||||
const system = startPage.data.start_page.system
|
||||
const tracking: TrackingSDKPageData = {
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
const meter = metrics.getMeter("trpc.contentstack.startPage")
|
||||
|
||||
export const getStartPageRefsCounter = meter.createCounter(
|
||||
"trpc.contentstack.startPage.get"
|
||||
)
|
||||
export const getStartPageRefsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.startPage.get-fail"
|
||||
)
|
||||
export const getStartPageRefsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.startPage.get-success"
|
||||
)
|
||||
|
||||
export const getStartPageCounter = meter.createCounter(
|
||||
"trpc.contentstack.startPage.get"
|
||||
)
|
||||
export const getStartPageSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.startPage.get-success"
|
||||
)
|
||||
export const getStartPageFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.startPage.get-fail"
|
||||
)
|
||||
@@ -1,88 +0,0 @@
|
||||
import { metrics as opentelemetryMetrics } from "@opentelemetry/api"
|
||||
|
||||
const meter = opentelemetryMetrics.getMeter("trpc.hotels")
|
||||
export const metrics = {
|
||||
additionalData: {
|
||||
counter: meter.createCounter("trpc.hotels.additionalData"),
|
||||
fail: meter.createCounter("trpc.hotels.additionalData-fail"),
|
||||
success: meter.createCounter("trpc.hotels.additionalData-success"),
|
||||
},
|
||||
breakfastPackage: {
|
||||
counter: meter.createCounter("trpc.package.breakfast"),
|
||||
fail: meter.createCounter("trpc.package.breakfast-fail"),
|
||||
success: meter.createCounter("trpc.package.breakfast-success"),
|
||||
},
|
||||
ancillaryPackage: {
|
||||
counter: meter.createCounter("trpc.package.ancillary"),
|
||||
fail: meter.createCounter("trpc.package.ancillary-fail"),
|
||||
success: meter.createCounter("trpc.package.ancillary-success"),
|
||||
},
|
||||
hotel: {
|
||||
counter: meter.createCounter("trpc.hotel.get"),
|
||||
fail: meter.createCounter("trpc.hotel.get-fail"),
|
||||
success: meter.createCounter("trpc.hotel.get-success"),
|
||||
},
|
||||
hotels: {
|
||||
counter: meter.createCounter("trpc.hotel.hotels.get"),
|
||||
fail: meter.createCounter("trpc.hotel.hotels.get-fail"),
|
||||
success: meter.createCounter("trpc.hotel.hotels.get-success"),
|
||||
},
|
||||
hotelIds: {
|
||||
counter: meter.createCounter("trpc.hotel.hotel-ids.get"),
|
||||
fail: meter.createCounter("trpc.hotel.hotel-ids.get-fail"),
|
||||
success: meter.createCounter("trpc.hotel.hotel-ids.get-success"),
|
||||
},
|
||||
hotelsAvailability: {
|
||||
counter: meter.createCounter("trpc.hotel.availability.hotels"),
|
||||
fail: meter.createCounter("trpc.hotel.availability.hotels-fail"),
|
||||
success: meter.createCounter("trpc.hotel.availability.hotels-success"),
|
||||
},
|
||||
hotelsAvailabilityBookingCode: {
|
||||
counter: meter.createCounter("trpc.hotel.availability.hotels-booking-code"),
|
||||
fail: meter.createCounter(
|
||||
"trpc.hotel.availability.hotels-booking-code-fail"
|
||||
),
|
||||
success: meter.createCounter(
|
||||
"trpc.hotel.availability.hotels-booking-code-success"
|
||||
),
|
||||
},
|
||||
hotelsByHotelIdAvailability: {
|
||||
counter: meter.createCounter("trpc.hotel.availability.hotels-by-hotel-id"),
|
||||
fail: meter.createCounter(
|
||||
"trpc.hotel.availability.hotels-by-hotel-id-fail"
|
||||
),
|
||||
success: meter.createCounter(
|
||||
"trpc.hotel.availability.hotels-by-hotel-id-success"
|
||||
),
|
||||
},
|
||||
meetingRooms: {
|
||||
counter: meter.createCounter("trpc.hotels.meetingRooms"),
|
||||
fail: meter.createCounter("trpc.hotels.meetingRooms-fail"),
|
||||
success: meter.createCounter("trpc.hotels.meetingRooms-success"),
|
||||
},
|
||||
nearbyHotelIds: {
|
||||
counter: meter.createCounter("trpc.hotel.nearby-hotel-ids.get"),
|
||||
fail: meter.createCounter("trpc.hotel.nearby-hotel-ids.get-fail"),
|
||||
success: meter.createCounter("trpc.hotel.nearby-hotel-ids.get-success"),
|
||||
},
|
||||
packages: {
|
||||
counter: meter.createCounter("trpc.hotel.packages.get"),
|
||||
fail: meter.createCounter("trpc.hotel.packages.get-fail"),
|
||||
success: meter.createCounter("trpc.hotel.packages.get-success"),
|
||||
},
|
||||
roomsAvailability: {
|
||||
counter: meter.createCounter("trpc.hotel.roomsAvailability.rooms"),
|
||||
fail: meter.createCounter("trpc.hotel.roomsAvailability.rooms-fail"),
|
||||
success: meter.createCounter("trpc.hotel.roomsAvailability.rooms-success"),
|
||||
},
|
||||
selectedRoomAvailability: {
|
||||
counter: meter.createCounter("trpc.hotel.availability.room"),
|
||||
fail: meter.createCounter("trpc.hotel.availability.room-fail"),
|
||||
success: meter.createCounter("trpc.hotel.availability.room-success"),
|
||||
},
|
||||
roomFeatures: {
|
||||
counter: meter.createCounter("trpc.availability.roomfeature"),
|
||||
fail: meter.createCounter("trpc.availability.roomfeature-fail"),
|
||||
success: meter.createCounter("trpc.availability.roomfeature-success"),
|
||||
},
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import * as api from "@/lib/api"
|
||||
import { dt } from "@/lib/dt"
|
||||
import { badRequestError, unauthorizedError } from "@/server/errors/trpc"
|
||||
import { getCityPageUrls } from "@/server/routers/contentstack/destinationCityPage/utils"
|
||||
import { getVerifiedUser } from "@/server/routers/user/utils"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import {
|
||||
contentStackBaseWithServiceProcedure,
|
||||
publicProcedure,
|
||||
@@ -17,7 +19,6 @@ import { toApiLang } from "@/server/utils"
|
||||
import { getCacheClient } from "@/services/dataCache"
|
||||
|
||||
import { getHotelPageUrls } from "../contentstack/hotelPage/utils"
|
||||
import { getVerifiedUser } from "../user/query"
|
||||
import { additionalDataSchema } from "./schemas/hotel/include/additionalData"
|
||||
import { meetingRoomsSchema } from "./schemas/meetingRoom"
|
||||
import {
|
||||
@@ -42,17 +43,11 @@ import {
|
||||
selectRateRoomAvailabilityInputSchema,
|
||||
selectRateRoomsAvailabilityInputSchema,
|
||||
} from "./input"
|
||||
import { metrics } from "./metrics"
|
||||
import {
|
||||
ancillaryPackagesSchema,
|
||||
breakfastPackagesSchema,
|
||||
getNearbyHotelIdsSchema,
|
||||
} from "./output"
|
||||
import {
|
||||
locationsUrlsCounter,
|
||||
locationsUrlsFailCounter,
|
||||
locationsUrlsSuccessCounter,
|
||||
} from "./telemetry"
|
||||
import {
|
||||
getBedTypes,
|
||||
getCitiesByCountry,
|
||||
@@ -431,36 +426,24 @@ export const hotelQueryRouter = router({
|
||||
const { lang } = ctx
|
||||
const apiLang = toApiLang(lang)
|
||||
|
||||
metrics.hotelsAvailabilityBookingCode.counter.add(1, {
|
||||
...input,
|
||||
})
|
||||
const bookingCodeAvailabilityResponse =
|
||||
await getHotelsAvailabilityByCity(input, apiLang, ctx.serviceToken)
|
||||
|
||||
// If API or network failed with no response
|
||||
if (!bookingCodeAvailabilityResponse) {
|
||||
metrics.hotelsAvailabilityBookingCode.fail.add(1, {
|
||||
...input,
|
||||
error_type: "unknown",
|
||||
})
|
||||
return null
|
||||
}
|
||||
|
||||
// Get regular availability of hotels which don't have availability with booking code.
|
||||
const unavailableHotelIds =
|
||||
bookingCodeAvailabilityResponse?.availability
|
||||
.filter((hotel) => {
|
||||
return hotel.status === "NotAvailable"
|
||||
})
|
||||
.flatMap((hotel) => {
|
||||
return hotel.hotelId
|
||||
})
|
||||
const unavailableHotelIds = bookingCodeAvailabilityResponse.availability
|
||||
.filter((hotel) => {
|
||||
return hotel.status === "NotAvailable"
|
||||
})
|
||||
.flatMap((hotel) => {
|
||||
return hotel.hotelId
|
||||
})
|
||||
|
||||
// All hotels have availability with booking code no need to fetch regular prices.
|
||||
// return response as is without any filtering as below.
|
||||
if (!unavailableHotelIds || !unavailableHotelIds.length) {
|
||||
return bookingCodeAvailabilityResponse
|
||||
}
|
||||
|
||||
const unavailableHotelsInput = {
|
||||
...input,
|
||||
bookingCode: "",
|
||||
@@ -472,11 +455,6 @@ export const hotelQueryRouter = router({
|
||||
ctx.serviceToken
|
||||
)
|
||||
|
||||
metrics.hotelsAvailabilityBookingCode.success.add(1, {
|
||||
...input,
|
||||
})
|
||||
console.info("api.hotels.hotelsAvailabilityBookingCode success")
|
||||
|
||||
// No regular rates available due to network or API failure (no need to filter & merge).
|
||||
if (!unavailableHotels) {
|
||||
return bookingCodeAvailabilityResponse
|
||||
@@ -537,19 +515,16 @@ export const hotelQueryRouter = router({
|
||||
const language = ctx.lang
|
||||
let hotelsToFetch: string[] = []
|
||||
|
||||
metrics.hotels.counter.add(1, {
|
||||
input: JSON.stringify(input),
|
||||
const getHotelsByCSFilterCounter = createCounter(
|
||||
"trpc.hotel.hotels",
|
||||
"byCSFilter"
|
||||
)
|
||||
const metricsGetHotelsByCSFilter = getHotelsByCSFilterCounter.init({
|
||||
input,
|
||||
language,
|
||||
})
|
||||
console.info(
|
||||
"api.hotel.hotels start",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
...input,
|
||||
language,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
metricsGetHotelsByCSFilter.start()
|
||||
|
||||
if (hotelsToInclude.length) {
|
||||
hotelsToFetch = hotelsToInclude
|
||||
@@ -571,20 +546,13 @@ export const hotelQueryRouter = router({
|
||||
.find((loc) => loc.cityIdentifier === locationFilter.city)?.id
|
||||
|
||||
if (!cityId) {
|
||||
metrics.hotels.fail.add(1, {
|
||||
input: JSON.stringify(input),
|
||||
language,
|
||||
error_type: "not_found",
|
||||
error: `CityId not found for cityIdentifier: ${locationFilter.city}`,
|
||||
})
|
||||
|
||||
console.error(
|
||||
"api.hotel.hotels not found error",
|
||||
JSON.stringify({
|
||||
query: { ...input, language },
|
||||
error: `CityId not found for cityIdentifier: ${locationFilter.city}`,
|
||||
})
|
||||
metricsGetHotelsByCSFilter.dataError(
|
||||
`CityId not found for cityIdentifier: ${locationFilter.city}`,
|
||||
{
|
||||
cityIdentifier: locationFilter.city,
|
||||
}
|
||||
)
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
@@ -594,20 +562,13 @@ export const hotelQueryRouter = router({
|
||||
})
|
||||
|
||||
if (!hotelIds?.length) {
|
||||
metrics.hotels.fail.add(1, {
|
||||
cityId,
|
||||
language,
|
||||
error_type: "not_found",
|
||||
error: `No hotelIds found for cityId: ${cityId}`,
|
||||
})
|
||||
|
||||
console.error(
|
||||
"api.hotel.hotels not found error",
|
||||
JSON.stringify({
|
||||
query: { cityId, language },
|
||||
error: `No hotelIds found for cityId: ${cityId}`,
|
||||
})
|
||||
metricsGetHotelsByCSFilter.dataError(
|
||||
`No hotelIds found for cityId: ${cityId}`,
|
||||
{
|
||||
cityId,
|
||||
}
|
||||
)
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
@@ -623,20 +584,13 @@ export const hotelQueryRouter = router({
|
||||
})
|
||||
|
||||
if (!hotelIds?.length) {
|
||||
metrics.hotels.fail.add(1, {
|
||||
country: locationFilter.country,
|
||||
language,
|
||||
error_type: "not_found",
|
||||
error: `No hotelIds found for country: ${locationFilter.country}`,
|
||||
})
|
||||
|
||||
console.error(
|
||||
"api.hotel.hotels not found error",
|
||||
JSON.stringify({
|
||||
query: { country: locationFilter.country, language },
|
||||
error: `No hotelIds found for cityId: ${locationFilter.country}`,
|
||||
})
|
||||
metricsGetHotelsByCSFilter.dataError(
|
||||
`No hotelIds found for country: ${locationFilter.country}`,
|
||||
{
|
||||
country: locationFilter.country,
|
||||
}
|
||||
)
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
@@ -648,20 +602,11 @@ export const hotelQueryRouter = router({
|
||||
}
|
||||
|
||||
if (!hotelsToFetch.length) {
|
||||
metrics.hotels.fail.add(1, {
|
||||
input: JSON.stringify(input),
|
||||
language,
|
||||
error_type: "not_found",
|
||||
error: `Couldn't find any hotels for given input: ${JSON.stringify(input)}`,
|
||||
})
|
||||
|
||||
console.error(
|
||||
"api.hotel.hotels not found error",
|
||||
JSON.stringify({
|
||||
query: JSON.stringify(input),
|
||||
error: `Couldn't find any hotels for given input: ${JSON.stringify(input)}`,
|
||||
})
|
||||
metricsGetHotelsByCSFilter.dataError(
|
||||
`Couldn't find any hotels for given input: ${JSON.stringify(input)}`,
|
||||
input
|
||||
)
|
||||
|
||||
return []
|
||||
}
|
||||
const hotelPages = await getHotelPageUrls(language)
|
||||
@@ -684,20 +629,7 @@ export const hotelQueryRouter = router({
|
||||
})
|
||||
)
|
||||
|
||||
metrics.hotels.success.add(1, {
|
||||
input: JSON.stringify(input),
|
||||
language,
|
||||
})
|
||||
|
||||
console.info(
|
||||
"api.hotels success",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
input: JSON.stringify(input),
|
||||
language,
|
||||
},
|
||||
})
|
||||
)
|
||||
metricsGetHotelsByCSFilter.success()
|
||||
|
||||
return hotels.filter((hotel): hotel is HotelDataWithUrl => !!hotel)
|
||||
}),
|
||||
@@ -769,13 +701,17 @@ export const hotelQueryRouter = router({
|
||||
return cacheClient.cacheOrGet(
|
||||
`${apiLang}:nearbyHotels:${hotelId}`,
|
||||
async () => {
|
||||
metrics.nearbyHotelIds.counter.add(1, {
|
||||
const nearbyHotelsCounter = createCounter(
|
||||
"trpc.hotel",
|
||||
"nearbyHotelIds"
|
||||
)
|
||||
const metricsNearbyHotels = nearbyHotelsCounter.init({
|
||||
params,
|
||||
hotelId,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.nearbyHotelIds start",
|
||||
JSON.stringify({ query: { hotelId, params } })
|
||||
)
|
||||
|
||||
metricsNearbyHotels.start()
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Hotel.Hotels.nearbyHotels(hotelId),
|
||||
{
|
||||
@@ -786,55 +722,18 @@ export const hotelQueryRouter = router({
|
||||
params
|
||||
)
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
metrics.nearbyHotelIds.fail.add(1, {
|
||||
hotelId,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.nearbyHotelIds error",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
await metricsNearbyHotels.httpError(apiResponse)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const validateHotelData = getNearbyHotelIdsSchema.safeParse(apiJson)
|
||||
if (!validateHotelData.success) {
|
||||
metrics.nearbyHotelIds.fail.add(1, {
|
||||
hotelId,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validateHotelData.error),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.nearbyHotelIds validation error",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params },
|
||||
error: validateHotelData.error,
|
||||
})
|
||||
)
|
||||
metricsNearbyHotels.validationError(validateHotelData.error)
|
||||
throw badRequestError()
|
||||
}
|
||||
metrics.nearbyHotelIds.success.add(1, {
|
||||
hotelId,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.nearbyHotelIds success",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params },
|
||||
})
|
||||
)
|
||||
|
||||
metricsNearbyHotels.success()
|
||||
|
||||
return validateHotelData.data.map((id: string) => parseInt(id, 10))
|
||||
},
|
||||
@@ -884,16 +783,17 @@ export const hotelQueryRouter = router({
|
||||
urls: publicProcedure
|
||||
.input(getLocationsUrlsInput)
|
||||
.query(async ({ input }) => {
|
||||
const procedureName = "hotels.locations.urls"
|
||||
|
||||
const { lang } = input
|
||||
|
||||
locationsUrlsCounter.add(1, { lang })
|
||||
|
||||
console.info(
|
||||
`${procedureName}: start`,
|
||||
JSON.stringify({ query: { lang } })
|
||||
const locationsUrlsCounter = createCounter(
|
||||
"trpc.hotel.locations",
|
||||
"urls"
|
||||
)
|
||||
const metricsLocationsUrls = locationsUrlsCounter.init({
|
||||
lang,
|
||||
})
|
||||
|
||||
metricsLocationsUrls.start()
|
||||
|
||||
const [hotelPageUrlsResult, cityPageUrlsResult] =
|
||||
await Promise.allSettled([
|
||||
@@ -905,32 +805,15 @@ export const hotelQueryRouter = router({
|
||||
hotelPageUrlsResult.status === "rejected" ||
|
||||
cityPageUrlsResult.status === "rejected"
|
||||
) {
|
||||
locationsUrlsFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "no_data",
|
||||
response: JSON.stringify({
|
||||
hotelPageUrlsResult,
|
||||
cityPageUrlsResult,
|
||||
}),
|
||||
})
|
||||
|
||||
console.error(`${procedureName}: no data`, {
|
||||
variables: { lang },
|
||||
error_type: "no_data",
|
||||
response: {
|
||||
hotelPageUrlsResult,
|
||||
cityPageUrlsResult,
|
||||
},
|
||||
metricsLocationsUrls.dataError(`Failed to get data for page URLs`, {
|
||||
hotelPageUrlsResult,
|
||||
cityPageUrlsResult,
|
||||
})
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
locationsUrlsSuccessCounter.add(1, { lang })
|
||||
|
||||
console.info(`${procedureName}: success`, {
|
||||
variables: { lang },
|
||||
})
|
||||
metricsLocationsUrls.success()
|
||||
|
||||
return {
|
||||
hotels: hotelPageUrlsResult.value,
|
||||
@@ -992,12 +875,12 @@ export const hotelQueryRouter = router({
|
||||
hotelId,
|
||||
language,
|
||||
}
|
||||
const metricsData = { ...params, hotelId: input.hotelId }
|
||||
metrics.meetingRooms.counter.add(1, metricsData)
|
||||
console.info(
|
||||
"api.hotels.meetingRooms start",
|
||||
JSON.stringify({ query: { hotelId, params } })
|
||||
)
|
||||
const meetingRoomsCounter = createCounter("trpc.hotel", "meetingRooms")
|
||||
const metricsMeetingRooms = meetingRoomsCounter.init({
|
||||
params,
|
||||
})
|
||||
|
||||
metricsMeetingRooms.start()
|
||||
|
||||
const cacheClient = await getCacheClient()
|
||||
return cacheClient.cacheOrGet(
|
||||
@@ -1014,28 +897,7 @@ export const hotelQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
metrics.meetingRooms.fail.add(1, {
|
||||
...metricsData,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.meetingRooms error",
|
||||
JSON.stringify({
|
||||
query: { params },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
await metricsMeetingRooms.httpError(apiResponse)
|
||||
throw new Error("Failed to fetch meeting rooms")
|
||||
}
|
||||
|
||||
@@ -1043,22 +905,10 @@ export const hotelQueryRouter = router({
|
||||
const validatedMeetingRooms = meetingRoomsSchema.safeParse(apiJson)
|
||||
|
||||
if (!validatedMeetingRooms.success) {
|
||||
console.error(
|
||||
"api.hotels.meetingRooms validation error",
|
||||
JSON.stringify({
|
||||
query: { params },
|
||||
error: validatedMeetingRooms.error,
|
||||
})
|
||||
)
|
||||
metricsMeetingRooms.validationError(validatedMeetingRooms.error)
|
||||
throw badRequestError()
|
||||
}
|
||||
metrics.meetingRooms.success.add(1, {
|
||||
hotelId,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.meetingRooms success",
|
||||
JSON.stringify({ query: { params } })
|
||||
)
|
||||
metricsMeetingRooms.success()
|
||||
|
||||
return validatedMeetingRooms.data.data
|
||||
},
|
||||
@@ -1074,12 +924,16 @@ export const hotelQueryRouter = router({
|
||||
hotelId,
|
||||
language,
|
||||
}
|
||||
const metricsData = { ...params, hotelId: input.hotelId }
|
||||
metrics.additionalData.counter.add(1, metricsData)
|
||||
console.info(
|
||||
"api.hotels.additionalData start",
|
||||
JSON.stringify({ query: { hotelId, params } })
|
||||
|
||||
const additionalDataCounter = createCounter(
|
||||
"trpc.hotel",
|
||||
"additionalData"
|
||||
)
|
||||
const metricsAdditionalData = additionalDataCounter.init({
|
||||
params,
|
||||
})
|
||||
|
||||
metricsAdditionalData.start()
|
||||
|
||||
const cacheClient = await getCacheClient()
|
||||
return cacheClient.cacheOrGet(
|
||||
@@ -1096,28 +950,7 @@ export const hotelQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
metrics.additionalData.fail.add(1, {
|
||||
...metricsData,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.additionalData error",
|
||||
JSON.stringify({
|
||||
query: { params },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
await metricsAdditionalData.httpError(apiResponse)
|
||||
throw new Error("Unable to fetch additional data for hotel")
|
||||
}
|
||||
|
||||
@@ -1126,22 +959,11 @@ export const hotelQueryRouter = router({
|
||||
additionalDataSchema.safeParse(apiJson)
|
||||
|
||||
if (!validatedAdditionalData.success) {
|
||||
console.error(
|
||||
"api.hotels.additionalData validation error",
|
||||
JSON.stringify({
|
||||
query: { params },
|
||||
error: validatedAdditionalData.error,
|
||||
})
|
||||
)
|
||||
metricsAdditionalData.validationError(validatedAdditionalData.error)
|
||||
throw badRequestError()
|
||||
}
|
||||
metrics.additionalData.success.add(1, {
|
||||
hotelId,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.additionalData success",
|
||||
JSON.stringify({ query: { params } })
|
||||
)
|
||||
|
||||
metricsAdditionalData.success()
|
||||
|
||||
return validatedAdditionalData.data
|
||||
},
|
||||
@@ -1167,12 +989,16 @@ export const hotelQueryRouter = router({
|
||||
language: apiLang,
|
||||
}
|
||||
|
||||
const metricsData = { ...params, hotelId: input.hotelId }
|
||||
metrics.breakfastPackage.counter.add(1, metricsData)
|
||||
console.info(
|
||||
"api.package.breakfast start",
|
||||
JSON.stringify({ query: metricsData })
|
||||
const breakfastCounter = createCounter(
|
||||
"trpc.hotel.packages",
|
||||
"breakfast"
|
||||
)
|
||||
const metricsBreakfast = breakfastCounter.init({
|
||||
params,
|
||||
hotelId: input.hotelId,
|
||||
})
|
||||
|
||||
metricsBreakfast.start()
|
||||
|
||||
const cacheClient = await getCacheClient()
|
||||
const breakfastPackages = await cacheClient.cacheOrGet(
|
||||
@@ -1189,57 +1015,17 @@ export const hotelQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
metrics.breakfastPackage.fail.add(1, {
|
||||
...metricsData,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.package.breakfast error",
|
||||
JSON.stringify({
|
||||
query: metricsData,
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
await metricsBreakfast.httpError(apiResponse)
|
||||
throw new Error("Unable to fetch breakfast packages")
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const breakfastPackages = breakfastPackagesSchema.safeParse(apiJson)
|
||||
if (!breakfastPackages.success) {
|
||||
metrics.breakfastPackage.fail.add(1, {
|
||||
...metricsData,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(breakfastPackages.error),
|
||||
})
|
||||
console.error(
|
||||
"api.package.breakfast validation error",
|
||||
JSON.stringify({
|
||||
query: metricsData,
|
||||
error: breakfastPackages.error,
|
||||
})
|
||||
)
|
||||
|
||||
metricsBreakfast.validationError(breakfastPackages.error)
|
||||
throw new Error("Unable to parse breakfast packages")
|
||||
}
|
||||
|
||||
metrics.breakfastPackage.success.add(1, metricsData)
|
||||
console.info(
|
||||
"api.package.breakfast success",
|
||||
JSON.stringify({
|
||||
query: metricsData,
|
||||
})
|
||||
)
|
||||
|
||||
return breakfastPackages.data
|
||||
},
|
||||
"1h"
|
||||
@@ -1265,6 +1051,8 @@ export const hotelQueryRouter = router({
|
||||
// }
|
||||
// }
|
||||
|
||||
metricsBreakfast.success()
|
||||
|
||||
return breakfastPackages.filter(
|
||||
(pkg) => pkg.code !== BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
|
||||
)
|
||||
@@ -1281,16 +1069,21 @@ export const hotelQueryRouter = router({
|
||||
language: apiLang,
|
||||
}
|
||||
|
||||
const ancillaryCounter = createCounter(
|
||||
"trpc.hotel.packages",
|
||||
"ancillary"
|
||||
)
|
||||
const metricsAncillary = ancillaryCounter.init({
|
||||
params,
|
||||
hotelId: input.hotelId,
|
||||
})
|
||||
|
||||
metricsAncillary.start()
|
||||
|
||||
const cacheClient = await getCacheClient()
|
||||
return await cacheClient.cacheOrGet(
|
||||
const result = await cacheClient.cacheOrGet(
|
||||
`${apiLang}:hotel:${input.hotelId}:ancillaries:startDate:${params.StartDate}:endDate:${params.EndDate}`,
|
||||
async () => {
|
||||
const metricsData = { ...params, hotelId: input.hotelId }
|
||||
metrics.ancillaryPackage.counter.add(1, metricsData)
|
||||
console.info(
|
||||
"api.package.ancillary start",
|
||||
JSON.stringify({ query: metricsData })
|
||||
)
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Package.Ancillary.hotel(input.hotelId),
|
||||
{
|
||||
@@ -1302,59 +1095,25 @@ export const hotelQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
metrics.ancillaryPackage.fail.add(1, {
|
||||
...metricsData,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.package.ancillary start error",
|
||||
JSON.stringify({
|
||||
query: metricsData,
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
await metricsAncillary.httpError(apiResponse)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const ancillaryPackages = ancillaryPackagesSchema.safeParse(apiJson)
|
||||
if (!ancillaryPackages.success) {
|
||||
metrics.ancillaryPackage.fail.add(1, {
|
||||
...metricsData,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(ancillaryPackages.error),
|
||||
})
|
||||
console.error(
|
||||
"api.package.ancillary validation error",
|
||||
JSON.stringify({
|
||||
query: metricsData,
|
||||
error: ancillaryPackages.error,
|
||||
})
|
||||
)
|
||||
metricsAncillary.validationError(ancillaryPackages.error)
|
||||
return null
|
||||
}
|
||||
|
||||
metrics.ancillaryPackage.success.add(1, metricsData)
|
||||
console.info(
|
||||
"api.package.ancillary success",
|
||||
JSON.stringify({
|
||||
query: metricsData,
|
||||
})
|
||||
)
|
||||
return ancillaryPackages.data
|
||||
},
|
||||
"1h"
|
||||
)
|
||||
|
||||
metricsAncillary.success()
|
||||
|
||||
return result
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
const meter = metrics.getMeter("trpc.hotels")
|
||||
export const getHotelCounter = meter.createCounter("trpc.hotel.get")
|
||||
export const getHotelSuccessCounter = meter.createCounter(
|
||||
"trpc.hotel.get-success"
|
||||
)
|
||||
export const getHotelFailCounter = meter.createCounter("trpc.hotel.get-fail")
|
||||
|
||||
export const getPackagesCounter = meter.createCounter("trpc.hotel.packages.get")
|
||||
export const getPackagesSuccessCounter = meter.createCounter(
|
||||
"trpc.hotel.packages.get-success"
|
||||
)
|
||||
export const getPackagesFailCounter = meter.createCounter(
|
||||
"trpc.hotel.packages.get-fail"
|
||||
)
|
||||
|
||||
export const hotelsAvailabilityCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.hotels"
|
||||
)
|
||||
export const hotelsAvailabilitySuccessCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.hotels-success"
|
||||
)
|
||||
export const hotelsAvailabilityFailCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.hotels-fail"
|
||||
)
|
||||
|
||||
export const hotelsByHotelIdAvailabilityCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.hotels-by-hotel-id"
|
||||
)
|
||||
export const hotelsByHotelIdAvailabilitySuccessCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.hotels-by-hotel-id-success"
|
||||
)
|
||||
export const hotelsByHotelIdAvailabilityFailCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.hotels-by-hotel-id-fail"
|
||||
)
|
||||
|
||||
export const selectedRoomAvailabilityCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.room"
|
||||
)
|
||||
export const selectedRoomAvailabilitySuccessCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.room-success"
|
||||
)
|
||||
export const selectedRoomAvailabilityFailCounter = meter.createCounter(
|
||||
"trpc.hotel.availability.room-fail"
|
||||
)
|
||||
|
||||
export const breakfastPackagesCounter = meter.createCounter(
|
||||
"trpc.package.breakfast"
|
||||
)
|
||||
export const breakfastPackagesSuccessCounter = meter.createCounter(
|
||||
"trpc.package.breakfast-success"
|
||||
)
|
||||
export const breakfastPackagesFailCounter = meter.createCounter(
|
||||
"trpc.package.breakfast-fail"
|
||||
)
|
||||
|
||||
export const getHotelsCounter = meter.createCounter("trpc.hotel.hotels.get")
|
||||
export const getHotelsSuccessCounter = meter.createCounter(
|
||||
"trpc.hotel.hotels.get-success"
|
||||
)
|
||||
export const getHotelsFailCounter = meter.createCounter(
|
||||
"trpc.hotel.hotels.get-fail"
|
||||
)
|
||||
|
||||
export const getHotelIdsCounter = meter.createCounter(
|
||||
"trpc.hotel.hotel-ids.get"
|
||||
)
|
||||
export const getHotelIdsSuccessCounter = meter.createCounter(
|
||||
"trpc.hotel.hotel-ids.get-success"
|
||||
)
|
||||
export const getHotelIdsFailCounter = meter.createCounter(
|
||||
"trpc.hotel.hotel-ids.get-fail"
|
||||
)
|
||||
|
||||
export const nearbyHotelIdsCounter = meter.createCounter(
|
||||
"trpc.hotel.nearby-hotel-ids.get"
|
||||
)
|
||||
export const nearbyHotelIdsSuccessCounter = meter.createCounter(
|
||||
"trpc.hotel.nearby-hotel-ids.get-success"
|
||||
)
|
||||
export const nearbyHotelIdsFailCounter = meter.createCounter(
|
||||
"trpc.hotel.nearby-hotel-ids.get-fail"
|
||||
)
|
||||
|
||||
export const meetingRoomsCounter = meter.createCounter(
|
||||
"trpc.hotels.meetingRooms"
|
||||
)
|
||||
export const meetingRoomsSuccessCounter = meter.createCounter(
|
||||
"trpc.hotels.meetingRooms-success"
|
||||
)
|
||||
export const meetingRoomsFailCounter = meter.createCounter(
|
||||
"trpc.hotels.meetingRooms-fail"
|
||||
)
|
||||
|
||||
export const additionalDataCounter = meter.createCounter(
|
||||
"trpc.hotels.additionalData"
|
||||
)
|
||||
export const additionalDataSuccessCounter = meter.createCounter(
|
||||
"trpc.hotels.additionalData-success"
|
||||
)
|
||||
export const additionalDataFailCounter = meter.createCounter(
|
||||
"trpc.hotels.additionalData-fail"
|
||||
)
|
||||
|
||||
export const locationsUrlsCounter = meter.createCounter(
|
||||
"trpc.hotels.locations.urls"
|
||||
)
|
||||
export const locationsUrlsSuccessCounter = meter.createCounter(
|
||||
"trpc.hotels.locations.urls-success"
|
||||
)
|
||||
export const locationsUrlsFailCounter = meter.createCounter(
|
||||
"trpc.hotels.locations.urls-fail"
|
||||
)
|
||||
@@ -6,6 +6,7 @@ import { Lang } from "@/constants/languages"
|
||||
import { env } from "@/env/server"
|
||||
import * as api from "@/lib/api"
|
||||
import { badRequestError } from "@/server/errors/trpc"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import { toApiLang } from "@/server/utils"
|
||||
|
||||
import { generateChildrenString } from "@/components/HotelReservation/utils"
|
||||
@@ -13,8 +14,7 @@ import { getCacheClient } from "@/services/dataCache"
|
||||
import { cache } from "@/utils/cache"
|
||||
|
||||
import { getHotelPageUrls } from "../contentstack/hotelPage/utils"
|
||||
import { type RoomFeaturesInput, roomPackagesInputSchema } from "./input"
|
||||
import { metrics } from "./metrics"
|
||||
import { type RoomFeaturesInput } from "./input"
|
||||
import {
|
||||
type Cities,
|
||||
citiesByCountrySchema,
|
||||
@@ -38,12 +38,12 @@ import type {
|
||||
DestinationPagesHotelData,
|
||||
Room as RoomCategory,
|
||||
} from "@/types/hotel"
|
||||
import type { PackagesInput } from "@/types/requests/packages"
|
||||
import type { PackagesOutput } from "@/types/requests/packages"
|
||||
import type {
|
||||
HotelsAvailabilityInputSchema,
|
||||
HotelsByHotelIdsAvailabilityInputSchema,
|
||||
RoomsAvailabilityInputRoom,
|
||||
RoomsAvailabilityInputSchema,
|
||||
RoomsAvailabilityOutputSchema,
|
||||
} from "@/types/trpc/routers/hotel/availability"
|
||||
import type { HotelInput } from "@/types/trpc/routers/hotel/hotel"
|
||||
import type {
|
||||
@@ -332,18 +332,23 @@ export async function getHotelIdsByCityId({
|
||||
cityId: string
|
||||
serviceToken: string
|
||||
}) {
|
||||
const getHotelIdsByCityIdCounter = createCounter(
|
||||
"hotel",
|
||||
"getHotelIdsByCityId"
|
||||
)
|
||||
const metricsGetHotelIdsByCityId = getHotelIdsByCityIdCounter.init({
|
||||
cityId,
|
||||
})
|
||||
|
||||
metricsGetHotelIdsByCityId.start()
|
||||
|
||||
const cacheClient = await getCacheClient()
|
||||
return await cacheClient.cacheOrGet(
|
||||
const result = await cacheClient.cacheOrGet(
|
||||
`${cityId}:hotelsByCityId`,
|
||||
async () => {
|
||||
const searchParams = new URLSearchParams({
|
||||
city: cityId,
|
||||
})
|
||||
metrics.hotelIds.counter.add(1, { params: searchParams.toString() })
|
||||
console.info(
|
||||
"api.hotel.hotel-ids start",
|
||||
JSON.stringify({ params: searchParams.toString() })
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Hotel.hotels,
|
||||
@@ -356,59 +361,25 @@ export async function getHotelIdsByCityId({
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const responseMessage = await apiResponse.text()
|
||||
metrics.hotelIds.fail.add(1, {
|
||||
params: searchParams.toString(),
|
||||
error_type: "http_error",
|
||||
error: responseMessage,
|
||||
})
|
||||
console.error(
|
||||
"api.hotel.hotel-ids fetch error",
|
||||
JSON.stringify({
|
||||
params: searchParams.toString(),
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text: responseMessage,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
await metricsGetHotelIdsByCityId.httpError(apiResponse)
|
||||
throw new Error("Unable to fetch hotelIds by cityId")
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const validatedHotelIds = getHotelIdsSchema.safeParse(apiJson)
|
||||
if (!validatedHotelIds.success) {
|
||||
metrics.hotelIds.fail.add(1, {
|
||||
params: searchParams.toString(),
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedHotelIds.error),
|
||||
})
|
||||
console.error(
|
||||
"api.hotel.hotel-ids validation error",
|
||||
JSON.stringify({
|
||||
params: searchParams.toString(),
|
||||
error: validatedHotelIds.error,
|
||||
})
|
||||
)
|
||||
|
||||
metricsGetHotelIdsByCityId.validationError(validatedHotelIds.error)
|
||||
throw new Error("Unable to parse data for hotelIds by cityId")
|
||||
}
|
||||
|
||||
metrics.hotelIds.success.add(1, { cityId })
|
||||
console.info(
|
||||
"api.hotel.hotel-ids success",
|
||||
JSON.stringify({
|
||||
params: searchParams.toString(),
|
||||
response: validatedHotelIds.data,
|
||||
})
|
||||
)
|
||||
|
||||
return validatedHotelIds.data
|
||||
},
|
||||
env.CACHE_TIME_HOTELS
|
||||
)
|
||||
|
||||
metricsGetHotelIdsByCityId.success()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export async function getHotelIdsByCountry({
|
||||
@@ -418,17 +389,22 @@ export async function getHotelIdsByCountry({
|
||||
country: string
|
||||
serviceToken: string
|
||||
}) {
|
||||
const getHotelIdsByCountryCounter = createCounter(
|
||||
"hotel",
|
||||
"getHotelIdsByCountry"
|
||||
)
|
||||
|
||||
const metricsGetHotelIdsByCountry = getHotelIdsByCountryCounter.init({
|
||||
country,
|
||||
})
|
||||
|
||||
metricsGetHotelIdsByCountry.start()
|
||||
|
||||
const cacheClient = await getCacheClient()
|
||||
|
||||
return await cacheClient.cacheOrGet(
|
||||
const result = await cacheClient.cacheOrGet(
|
||||
`${country}:hotelsByCountry`,
|
||||
async () => {
|
||||
metrics.hotelIds.counter.add(1, { country })
|
||||
console.info(
|
||||
"api.hotel.hotel-ids start",
|
||||
JSON.stringify({ query: { country } })
|
||||
)
|
||||
|
||||
const hotelIdsParams = new URLSearchParams({
|
||||
country,
|
||||
})
|
||||
@@ -444,55 +420,25 @@ export async function getHotelIdsByCountry({
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const responseMessage = await apiResponse.text()
|
||||
metrics.hotelIds.fail.add(1, {
|
||||
country,
|
||||
error_type: "http_error",
|
||||
error: responseMessage,
|
||||
})
|
||||
console.error(
|
||||
"api.hotel.hotel-ids fetch error",
|
||||
JSON.stringify({
|
||||
query: { country },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text: responseMessage,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
await metricsGetHotelIdsByCountry.httpError(apiResponse)
|
||||
throw new Error("Unable to fetch hotelIds by country")
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const validatedHotelIds = getHotelIdsSchema.safeParse(apiJson)
|
||||
if (!validatedHotelIds.success) {
|
||||
metrics.hotelIds.fail.add(1, {
|
||||
country,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedHotelIds.error),
|
||||
})
|
||||
console.error(
|
||||
"api.hotel.hotel-ids validation error",
|
||||
JSON.stringify({
|
||||
query: { country },
|
||||
error: validatedHotelIds.error,
|
||||
})
|
||||
)
|
||||
metricsGetHotelIdsByCountry.validationError(validatedHotelIds.error)
|
||||
throw new Error("Unable to parse hotelIds by country")
|
||||
}
|
||||
|
||||
metrics.hotelIds.success.add(1, { country })
|
||||
console.info(
|
||||
"api.hotel.hotel-ids success",
|
||||
JSON.stringify({ query: { country } })
|
||||
)
|
||||
|
||||
return validatedHotelIds.data
|
||||
},
|
||||
env.CACHE_TIME_HOTELS
|
||||
)
|
||||
|
||||
metricsGetHotelIdsByCountry.success()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export async function getHotelIdsByCityIdentifier(
|
||||
@@ -631,128 +577,84 @@ function findProduct(product: Products, rateDefinition: RateDefinition) {
|
||||
|
||||
export const getHotel = cache(
|
||||
async (input: HotelInput, serviceToken: string) => {
|
||||
const callable = async function (
|
||||
hotelId: HotelInput["hotelId"],
|
||||
language: HotelInput["language"],
|
||||
isCardOnlyPayment?: HotelInput["isCardOnlyPayment"]
|
||||
) {
|
||||
/**
|
||||
* Since API expects the params appended and not just
|
||||
* a comma separated string we need to initialize the
|
||||
* SearchParams with a sequence of pairs
|
||||
* (include=City&include=NearbyHotels&include=Restaurants etc.)
|
||||
**/
|
||||
const params = new URLSearchParams([
|
||||
["include", "AdditionalData"],
|
||||
["include", "City"],
|
||||
["include", "NearbyHotels"],
|
||||
["include", "Restaurants"],
|
||||
["include", "RoomCategories"],
|
||||
["language", toApiLang(language)],
|
||||
])
|
||||
metrics.hotel.counter.add(1, {
|
||||
hotelId,
|
||||
language,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.hotelData start",
|
||||
JSON.stringify({ query: { hotelId, params: params.toString() } })
|
||||
)
|
||||
const { hotelId, language, isCardOnlyPayment } = input
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Hotel.Hotels.hotel(hotelId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
const getHotelCounter = createCounter("hotel", "getHotel")
|
||||
const metricsGetHotel = getHotelCounter.init({
|
||||
hotelId,
|
||||
language,
|
||||
isCardOnlyPayment,
|
||||
})
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
metrics.hotel.fail.add(1, {
|
||||
hotelId,
|
||||
language,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.hotelData error",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params: params.toString() },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const validateHotelData = hotelSchema.safeParse(apiJson)
|
||||
|
||||
if (!validateHotelData.success) {
|
||||
metrics.hotel.fail.add(1, {
|
||||
hotelId,
|
||||
language,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validateHotelData.error),
|
||||
})
|
||||
|
||||
console.error(
|
||||
"api.hotels.hotelData validation error",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params: params.toString() },
|
||||
error: validateHotelData.error,
|
||||
})
|
||||
)
|
||||
throw badRequestError()
|
||||
}
|
||||
|
||||
metrics.hotel.success.add(1, {
|
||||
hotelId,
|
||||
language,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.hotelData success",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params: params.toString() },
|
||||
})
|
||||
)
|
||||
const hotelData = validateHotelData.data
|
||||
|
||||
if (isCardOnlyPayment) {
|
||||
hotelData.hotel.merchantInformationData.alternatePaymentOptions = []
|
||||
}
|
||||
|
||||
const gallery = hotelData.additionalData?.gallery
|
||||
if (gallery) {
|
||||
const smallerImages = gallery.smallerImages
|
||||
const hotelGalleryImages =
|
||||
hotelData.hotel.hotelType === HotelTypeEnum.Signature
|
||||
? smallerImages.slice(0, 10)
|
||||
: smallerImages.slice(0, 6)
|
||||
hotelData.hotel.galleryImages = hotelGalleryImages
|
||||
}
|
||||
|
||||
return hotelData
|
||||
}
|
||||
metricsGetHotel.start()
|
||||
|
||||
const cacheClient = await getCacheClient()
|
||||
return await cacheClient.cacheOrGet(
|
||||
|
||||
const result = await cacheClient.cacheOrGet(
|
||||
`${input.language}:hotel:${input.hotelId}:${!!input.isCardOnlyPayment}`,
|
||||
async () => {
|
||||
return callable(input.hotelId, input.language, input.isCardOnlyPayment)
|
||||
/**
|
||||
* Since API expects the params appended and not just
|
||||
* a comma separated string we need to initialize the
|
||||
* SearchParams with a sequence of pairs
|
||||
* (include=City&include=NearbyHotels&include=Restaurants etc.)
|
||||
**/
|
||||
const params = new URLSearchParams([
|
||||
["include", "AdditionalData"],
|
||||
["include", "City"],
|
||||
["include", "NearbyHotels"],
|
||||
["include", "Restaurants"],
|
||||
["include", "RoomCategories"],
|
||||
["language", toApiLang(language)],
|
||||
])
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Hotel.Hotels.hotel(hotelId),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${serviceToken}`,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
await metricsGetHotel.httpError(apiResponse)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const validateHotelData = hotelSchema.safeParse(apiJson)
|
||||
|
||||
if (!validateHotelData.success) {
|
||||
metricsGetHotel.validationError(validateHotelData.error)
|
||||
throw badRequestError()
|
||||
}
|
||||
|
||||
const hotelData = validateHotelData.data
|
||||
|
||||
if (isCardOnlyPayment) {
|
||||
hotelData.hotel.merchantInformationData.alternatePaymentOptions = []
|
||||
}
|
||||
|
||||
const gallery = hotelData.additionalData?.gallery
|
||||
if (gallery) {
|
||||
const smallerImages = gallery.smallerImages
|
||||
const hotelGalleryImages =
|
||||
hotelData.hotel.hotelType === HotelTypeEnum.Signature
|
||||
? smallerImages.slice(0, 10)
|
||||
: smallerImages.slice(0, 6)
|
||||
hotelData.hotel.galleryImages = hotelGalleryImages
|
||||
}
|
||||
|
||||
return hotelData
|
||||
},
|
||||
env.CACHE_TIME_HOTELS
|
||||
)
|
||||
|
||||
metricsGetHotel.success()
|
||||
|
||||
return result
|
||||
}
|
||||
)
|
||||
|
||||
@@ -781,19 +683,25 @@ export async function getHotelsAvailabilityByCity(
|
||||
...(redemption ? { isRedemption: "true" } : {}),
|
||||
language: apiLang,
|
||||
}
|
||||
metrics.hotelsAvailability.counter.add(1, {
|
||||
cityId,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
bookingCode,
|
||||
redemption,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.hotelsAvailability start",
|
||||
JSON.stringify({ query: { cityId, params } })
|
||||
|
||||
const getHotelsAvailabilityByCityCounter = createCounter(
|
||||
"hotel",
|
||||
"getHotelsAvailabilityByCity"
|
||||
)
|
||||
const metricsGetHotelsAvailabilityByCity =
|
||||
getHotelsAvailabilityByCityCounter.init({
|
||||
apiLang,
|
||||
cityId,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
bookingCode,
|
||||
redemption,
|
||||
})
|
||||
|
||||
metricsGetHotelsAvailabilityByCity.start()
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Availability.city(cityId),
|
||||
{
|
||||
@@ -804,74 +712,19 @@ export async function getHotelsAvailabilityByCity(
|
||||
params
|
||||
)
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
metrics.hotelsAvailability.fail.add(1, {
|
||||
cityId,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
bookingCode,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.hotelsAvailability error",
|
||||
JSON.stringify({
|
||||
query: { cityId, params },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
await metricsGetHotelsAvailabilityByCity.httpError(apiResponse)
|
||||
throw new Error("Failed to fetch hotels availability by city")
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const validateAvailabilityData = hotelsAvailabilitySchema.safeParse(apiJson)
|
||||
if (!validateAvailabilityData.success) {
|
||||
metrics.hotelsAvailability.fail.add(1, {
|
||||
cityId,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
bookingCode,
|
||||
redemption,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validateAvailabilityData.error),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.hotelsAvailability validation error",
|
||||
JSON.stringify({
|
||||
query: { cityId, params },
|
||||
error: validateAvailabilityData.error,
|
||||
})
|
||||
metricsGetHotelsAvailabilityByCity.validationError(
|
||||
validateAvailabilityData.error
|
||||
)
|
||||
throw badRequestError()
|
||||
}
|
||||
metrics.hotelsAvailability.success.add(1, {
|
||||
cityId,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
bookingCode,
|
||||
redemption,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.hotelsAvailability success",
|
||||
JSON.stringify({
|
||||
query: { cityId, params: params },
|
||||
})
|
||||
)
|
||||
|
||||
if (redemption) {
|
||||
validateAvailabilityData.data.data.forEach((data) => {
|
||||
data.attributes.productType?.redemptions?.forEach((r) => {
|
||||
@@ -880,11 +733,15 @@ export async function getHotelsAvailabilityByCity(
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
const result = {
|
||||
availability: validateAvailabilityData.data.data.flatMap(
|
||||
(hotels) => hotels.attributes
|
||||
),
|
||||
}
|
||||
|
||||
metricsGetHotelsAvailabilityByCity.success()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export async function getHotelsAvailabilityByHotelIds(
|
||||
@@ -910,8 +767,26 @@ export async function getHotelsAvailabilityByHotelIds(
|
||||
["language", apiLang],
|
||||
])
|
||||
|
||||
const getHotelsAvailabilityByHotelIdsCounter = createCounter(
|
||||
"hotel",
|
||||
"getHotelsAvailabilityByHotelIds"
|
||||
)
|
||||
const metricsGetHotelsAvailabilityByHotelIds =
|
||||
getHotelsAvailabilityByHotelIdsCounter.init({
|
||||
apiLang,
|
||||
hotelIds,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
bookingCode,
|
||||
})
|
||||
|
||||
metricsGetHotelsAvailabilityByHotelIds.start()
|
||||
|
||||
const cacheClient = await getCacheClient()
|
||||
return cacheClient.cacheOrGet(
|
||||
|
||||
const result = cacheClient.cacheOrGet(
|
||||
`${apiLang}:hotels:availability:${hotelIds.join(",")}:${roomStayStartDate}:${roomStayEndDate}:${adults}:${children}:${bookingCode}`,
|
||||
async () => {
|
||||
/**
|
||||
@@ -924,18 +799,7 @@ export async function getHotelsAvailabilityByHotelIds(
|
||||
hotelIds.forEach((hotelId) =>
|
||||
params.append("hotelIds", hotelId.toString())
|
||||
)
|
||||
metrics.hotelsByHotelIdAvailability.counter.add(1, {
|
||||
hotelIds,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
bookingCode,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.hotelsByHotelIdAvailability start",
|
||||
JSON.stringify({ query: { params } })
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Availability.hotels(),
|
||||
{
|
||||
@@ -946,72 +810,20 @@ export async function getHotelsAvailabilityByHotelIds(
|
||||
params
|
||||
)
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
metrics.hotelsByHotelIdAvailability.fail.add(1, {
|
||||
hotelIds,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
bookingCode,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.hotelsByHotelIdAvailability error",
|
||||
JSON.stringify({
|
||||
query: { params },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
await metricsGetHotelsAvailabilityByHotelIds.httpError(apiResponse)
|
||||
throw new Error("Failed to fetch hotels availability by hotelIds")
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const validateAvailabilityData =
|
||||
hotelsAvailabilitySchema.safeParse(apiJson)
|
||||
if (!validateAvailabilityData.success) {
|
||||
metrics.hotelsByHotelIdAvailability.fail.add(1, {
|
||||
hotelIds,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
bookingCode,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validateAvailabilityData.error),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.hotelsByHotelIdAvailability validation error",
|
||||
JSON.stringify({
|
||||
query: { params },
|
||||
error: validateAvailabilityData.error,
|
||||
})
|
||||
metricsGetHotelsAvailabilityByHotelIds.validationError(
|
||||
validateAvailabilityData.error
|
||||
)
|
||||
throw badRequestError()
|
||||
}
|
||||
metrics.hotelsByHotelIdAvailability.success.add(1, {
|
||||
hotelIds,
|
||||
roomStayStartDate,
|
||||
roomStayEndDate,
|
||||
adults,
|
||||
children,
|
||||
bookingCode,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.hotelsByHotelIdAvailability success",
|
||||
JSON.stringify({
|
||||
query: { params },
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
availability: validateAvailabilityData.data.data.flatMap(
|
||||
(hotels) => hotels.attributes
|
||||
@@ -1020,6 +832,10 @@ export async function getHotelsAvailabilityByHotelIds(
|
||||
},
|
||||
env.CACHE_TIME_CITY_SEARCH
|
||||
)
|
||||
|
||||
metricsGetHotelsAvailabilityByHotelIds.success()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
async function getRoomFeaturesInventory(
|
||||
@@ -1034,23 +850,32 @@ async function getRoomFeaturesInventory(
|
||||
roomFeatureCodes,
|
||||
startDate,
|
||||
} = input
|
||||
|
||||
const params = {
|
||||
adults,
|
||||
hotelId,
|
||||
roomFeatureCode: roomFeatureCodes,
|
||||
roomStayEndDate: endDate,
|
||||
roomStayStartDate: startDate,
|
||||
...(childrenInRoom?.length && {
|
||||
children: generateChildrenString(childrenInRoom),
|
||||
}),
|
||||
}
|
||||
|
||||
const getRoomFeaturesInventoryCounter = createCounter(
|
||||
"hotel",
|
||||
"getRoomFeaturesInventory"
|
||||
)
|
||||
const metricsGetRoomFeaturesInventory =
|
||||
getRoomFeaturesInventoryCounter.init(params)
|
||||
|
||||
metricsGetRoomFeaturesInventory.start()
|
||||
|
||||
const cacheClient = await getCacheClient()
|
||||
return cacheClient.cacheOrGet(
|
||||
|
||||
const result = cacheClient.cacheOrGet(
|
||||
stringify(input),
|
||||
async function () {
|
||||
const params = {
|
||||
adults,
|
||||
hotelId,
|
||||
roomFeatureCode: roomFeatureCodes,
|
||||
roomStayEndDate: endDate,
|
||||
roomStayStartDate: startDate,
|
||||
...(childrenInRoom?.length && {
|
||||
children: generateChildrenString(childrenInRoom),
|
||||
}),
|
||||
}
|
||||
|
||||
metrics.roomFeatures.counter.add(1, params)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Availability.roomFeatures(hotelId),
|
||||
{
|
||||
@@ -1062,67 +887,45 @@ async function getRoomFeaturesInventory(
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = apiResponse.text()
|
||||
console.error(
|
||||
"api.availability.roomfeature error",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
metrics.roomFeatures.fail.add(1, params)
|
||||
await metricsGetRoomFeaturesInventory.httpError(apiResponse)
|
||||
return null
|
||||
}
|
||||
|
||||
const data = await apiResponse.json()
|
||||
const validatedRoomFeaturesData = roomFeaturesSchema.safeParse(data)
|
||||
if (!validatedRoomFeaturesData.success) {
|
||||
console.error(
|
||||
"api.availability.roomfeature error",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params },
|
||||
error: validatedRoomFeaturesData.error,
|
||||
})
|
||||
metricsGetRoomFeaturesInventory.validationError(
|
||||
validatedRoomFeaturesData.error
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
metrics.roomFeatures.success.add(1, params)
|
||||
|
||||
return validatedRoomFeaturesData.data
|
||||
},
|
||||
"5m"
|
||||
)
|
||||
|
||||
metricsGetRoomFeaturesInventory.success()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export async function getPackages(
|
||||
rawInput: PackagesInput,
|
||||
serviceToken: string
|
||||
) {
|
||||
const parsedInput = roomPackagesInputSchema.safeParse(rawInput)
|
||||
if (!parsedInput.success) {
|
||||
console.info(`Failed to parse input for Get Packages`)
|
||||
console.error(parsedInput.error)
|
||||
return null
|
||||
}
|
||||
const input = parsedInput.data
|
||||
export async function getPackages(input: PackagesOutput, serviceToken: string) {
|
||||
const { adults, children, endDate, hotelId, lang, packageCodes, startDate } =
|
||||
input
|
||||
|
||||
const getPackagesCounter = createCounter("hotel", "getPackages")
|
||||
const metricsGetPackages = getPackagesCounter.init({
|
||||
input,
|
||||
})
|
||||
|
||||
metricsGetPackages.start()
|
||||
|
||||
const cacheClient = await getCacheClient()
|
||||
return cacheClient.cacheOrGet(
|
||||
|
||||
const result = cacheClient.cacheOrGet(
|
||||
stringify(input),
|
||||
async function () {
|
||||
const {
|
||||
adults,
|
||||
children,
|
||||
endDate,
|
||||
hotelId,
|
||||
lang,
|
||||
packageCodes,
|
||||
startDate,
|
||||
} = input
|
||||
const apiLang = toApiLang(lang)
|
||||
|
||||
const searchParams = new URLSearchParams({
|
||||
@@ -1137,16 +940,6 @@ export async function getPackages(
|
||||
searchParams.append("packageCodes", code)
|
||||
})
|
||||
|
||||
const params = searchParams.toString()
|
||||
|
||||
metrics.packages.counter.add(1, {
|
||||
hotelId,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.packages start",
|
||||
JSON.stringify({ query: { hotelId, params } })
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Package.Packages.hotel(hotelId),
|
||||
{
|
||||
@@ -1158,56 +951,29 @@ export async function getPackages(
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
metrics.packages.fail.add(1, {
|
||||
hotelId,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.hotels.packages error",
|
||||
JSON.stringify({ query: { hotelId, params } })
|
||||
)
|
||||
await metricsGetPackages.httpError(apiResponse)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const validatedPackagesData = packagesSchema.safeParse(apiJson)
|
||||
if (!validatedPackagesData.success) {
|
||||
metrics.packages.fail.add(1, {
|
||||
hotelId,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedPackagesData.error),
|
||||
})
|
||||
|
||||
console.error(
|
||||
"api.hotels.packages validation error",
|
||||
JSON.stringify({
|
||||
query: { hotelId, params },
|
||||
error: validatedPackagesData.error,
|
||||
})
|
||||
)
|
||||
metricsGetPackages.validationError(validatedPackagesData.error)
|
||||
return null
|
||||
}
|
||||
|
||||
metrics.packages.success.add(1, {
|
||||
hotelId,
|
||||
})
|
||||
console.info(
|
||||
"api.hotels.packages success",
|
||||
JSON.stringify({ query: { hotelId, params: params } })
|
||||
)
|
||||
|
||||
return validatedPackagesData.data
|
||||
},
|
||||
"3h"
|
||||
)
|
||||
|
||||
metricsGetPackages.success()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export async function getRoomsAvailability(
|
||||
input: RoomsAvailabilityInputSchema,
|
||||
input: RoomsAvailabilityOutputSchema,
|
||||
token: string,
|
||||
serviceToken: string,
|
||||
userPoints: number | undefined
|
||||
@@ -1219,27 +985,18 @@ export async function getRoomsAvailability(
|
||||
|
||||
const redemption = searchType === REDEMPTION
|
||||
|
||||
const apiLang = toApiLang(lang)
|
||||
|
||||
const kids = rooms
|
||||
.map((r) => r.childrenInRoom)
|
||||
.filter(Boolean)
|
||||
.map((kid) => JSON.stringify(kid))
|
||||
const metricsData = {
|
||||
adultsCount: rooms.map((r) => r.adults),
|
||||
bookingCode,
|
||||
childArray: kids.length ? kids : undefined,
|
||||
hotelId,
|
||||
roomStayEndDate: toDate,
|
||||
roomStayStartDate: fromDate,
|
||||
}
|
||||
|
||||
metrics.roomsAvailability.counter.add(1, metricsData)
|
||||
|
||||
console.info(
|
||||
"api.hotels.roomsAvailability start",
|
||||
JSON.stringify({ query: { hotelId, params: metricsData } })
|
||||
const getRoomsAvailabilityCounter = createCounter(
|
||||
"hotel",
|
||||
"getRoomsAvailability"
|
||||
)
|
||||
const metricsGetRoomsAvailability = getRoomsAvailabilityCounter.init({
|
||||
input,
|
||||
redemption,
|
||||
})
|
||||
|
||||
metricsGetRoomsAvailability.start()
|
||||
|
||||
const apiLang = toApiLang(lang)
|
||||
|
||||
const baseCacheKey = {
|
||||
bookingCode,
|
||||
@@ -1257,7 +1014,7 @@ export async function getRoomsAvailability(
|
||||
...baseCacheKey,
|
||||
room,
|
||||
}
|
||||
return cacheClient.cacheOrGet(
|
||||
const result = cacheClient.cacheOrGet(
|
||||
stringify(cacheKey),
|
||||
async function () {
|
||||
{
|
||||
@@ -1287,9 +1044,8 @@ export async function getRoomsAvailability(
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
await metricsGetRoomsAvailability.httpError(apiResponse)
|
||||
const text = await apiResponse.text()
|
||||
metrics.roomsAvailability.fail.add(1, metricsData)
|
||||
console.error("Failed API call", { params, text })
|
||||
return { error: "http_error", details: text }
|
||||
}
|
||||
|
||||
@@ -1297,11 +1053,10 @@ export async function getRoomsAvailability(
|
||||
const validateAvailabilityData =
|
||||
roomsAvailabilitySchema.safeParse(apiJson)
|
||||
if (!validateAvailabilityData.success) {
|
||||
console.error("Validation error", {
|
||||
params,
|
||||
error: validateAvailabilityData.error,
|
||||
})
|
||||
metrics.roomsAvailability.fail.add(1, metricsData)
|
||||
metricsGetRoomsAvailability.validationError(
|
||||
validateAvailabilityData.error
|
||||
)
|
||||
|
||||
return {
|
||||
error: "validation_error",
|
||||
details: validateAvailabilityData.error,
|
||||
@@ -1323,7 +1078,7 @@ export async function getRoomsAvailability(
|
||||
const roomFeatures = await getPackages(
|
||||
{
|
||||
adults: room.adults,
|
||||
children: room.childrenInRoom?.length,
|
||||
children: room.childrenInRoom?.length || 0,
|
||||
endDate: input.booking.toDate,
|
||||
hotelId: input.booking.hotelId,
|
||||
lang,
|
||||
@@ -1387,9 +1142,10 @@ export async function getRoomsAvailability(
|
||||
},
|
||||
"1m"
|
||||
)
|
||||
|
||||
return result
|
||||
})
|
||||
)
|
||||
metrics.roomsAvailability.success.add(1, metricsData)
|
||||
|
||||
const data = availabilityResponses.map((availability) => {
|
||||
if (availability.status === "fulfilled") {
|
||||
@@ -1401,6 +1157,8 @@ export async function getRoomsAvailability(
|
||||
}
|
||||
})
|
||||
|
||||
metricsGetRoomsAvailability.success()
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@ import { TRPCError } from "@trpc/server"
|
||||
import { z } from "zod"
|
||||
|
||||
import { Lang } from "@/constants/languages"
|
||||
import { getVerifiedUser } from "@/server/routers/user/utils"
|
||||
import { safeProtectedProcedure } from "@/server/trpc"
|
||||
|
||||
import { isValidSession } from "@/utils/session"
|
||||
|
||||
import { getVerifiedUser } from "../../user/query"
|
||||
import { getPrimaryLinks } from "./getPrimaryLinks"
|
||||
import { getSecondaryLinks } from "./getSecondaryLinks"
|
||||
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import { publicProcedure, router } from "@/server/trpc"
|
||||
|
||||
import { getCacheClient } from "@/services/dataCache"
|
||||
|
||||
import { jobylonFeedSchema } from "./output"
|
||||
import {
|
||||
getJobylonFeedCounter,
|
||||
getJobylonFeedFailCounter,
|
||||
getJobylonFeedSuccessCounter,
|
||||
} from "./telemetry"
|
||||
|
||||
export const TWENTYFOUR_HOURS = 60 * 60 * 24
|
||||
|
||||
@@ -19,20 +15,19 @@ const feedUrl =
|
||||
export const jobylonQueryRouter = router({
|
||||
feed: router({
|
||||
get: publicProcedure.query(async function () {
|
||||
const jobylonFeedGetCounter = createCounter("trpc.jobylon.feed", "get")
|
||||
const metricsJobylonFeedGet = jobylonFeedGetCounter.init()
|
||||
|
||||
metricsJobylonFeedGet.start()
|
||||
|
||||
const url = new URL(feedUrl)
|
||||
url.search = new URLSearchParams({
|
||||
format: "json",
|
||||
}).toString()
|
||||
const urlString = url.toString()
|
||||
|
||||
getJobylonFeedCounter.add(1, { url: urlString })
|
||||
console.info(
|
||||
"jobylon.feed start",
|
||||
JSON.stringify({ query: { url: urlString } })
|
||||
)
|
||||
|
||||
const cacheClient = await getCacheClient()
|
||||
return await cacheClient.cacheOrGet(
|
||||
|
||||
const result = cacheClient.cacheOrGet(
|
||||
"jobylon:feed",
|
||||
async () => {
|
||||
const response = await fetch(url, {
|
||||
@@ -40,27 +35,14 @@ export const jobylonQueryRouter = router({
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
await metricsJobylonFeedGet.httpError(response)
|
||||
const text = await response.text()
|
||||
const error = {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
text,
|
||||
}
|
||||
getJobylonFeedFailCounter.add(1, {
|
||||
url: urlString,
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify(error),
|
||||
})
|
||||
console.error(
|
||||
"jobylon.feed error",
|
||||
JSON.stringify({
|
||||
query: { url: urlString },
|
||||
error,
|
||||
})
|
||||
)
|
||||
|
||||
throw new Error(
|
||||
`Failed to fetch Jobylon feed: ${JSON.stringify(error)}`
|
||||
`Failed to fetch Jobylon feed: ${JSON.stringify({
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
text,
|
||||
})}`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -68,37 +50,20 @@ export const jobylonQueryRouter = router({
|
||||
const validatedResponse = jobylonFeedSchema.safeParse(responseJson)
|
||||
|
||||
if (!validatedResponse.success) {
|
||||
getJobylonFeedFailCounter.add(1, {
|
||||
urlString,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedResponse.error),
|
||||
})
|
||||
|
||||
const errorData = JSON.stringify({
|
||||
query: { url: urlString },
|
||||
error: validatedResponse.error,
|
||||
})
|
||||
|
||||
console.error("jobylon.feed error", errorData)
|
||||
metricsJobylonFeedGet.validationError(validatedResponse.error)
|
||||
throw new Error(
|
||||
`Failed to parse Jobylon feed: ${JSON.stringify(errorData)}`
|
||||
`Failed to parse Jobylon feed: ${JSON.stringify(validatedResponse.error)}`
|
||||
)
|
||||
}
|
||||
|
||||
getJobylonFeedSuccessCounter.add(1, {
|
||||
url: urlString,
|
||||
})
|
||||
console.info(
|
||||
"jobylon.feed success",
|
||||
JSON.stringify({
|
||||
query: { url: urlString },
|
||||
})
|
||||
)
|
||||
|
||||
return validatedResponse.data
|
||||
},
|
||||
"1d"
|
||||
)
|
||||
|
||||
metricsJobylonFeedGet.success()
|
||||
|
||||
return result
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
const meter = metrics.getMeter("trpc.booking")
|
||||
export const getJobylonFeedCounter = meter.createCounter("trpc.jobylon-feed")
|
||||
export const getJobylonFeedSuccessCounter = meter.createCounter(
|
||||
"trpc.jobylon-feed-success"
|
||||
)
|
||||
export const getJobylonFeedFailCounter = meter.createCounter(
|
||||
"trpc.jobylon-feed-fail"
|
||||
)
|
||||
@@ -11,11 +11,7 @@ export const staysInput = z
|
||||
.number()
|
||||
.optional()
|
||||
.transform((num) => (num ? String(num) : undefined)),
|
||||
limit: z
|
||||
.number()
|
||||
.min(0)
|
||||
.default(6)
|
||||
.transform((num) => String(num)),
|
||||
limit: z.number().min(0).default(6),
|
||||
lang: z.nativeEnum(Lang).optional(),
|
||||
})
|
||||
.default({})
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
import { signupVerify } from "@/constants/routes/signup"
|
||||
import { env } from "@/env/server"
|
||||
import * as api from "@/lib/api"
|
||||
@@ -8,6 +6,7 @@ import {
|
||||
initiateSaveCardSchema,
|
||||
subscriberIdSchema,
|
||||
} from "@/server/routers/user/output"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import { protectedProcedure, router, serviceProcedure } from "@/server/trpc"
|
||||
|
||||
import {
|
||||
@@ -17,20 +16,6 @@ import {
|
||||
signupInput,
|
||||
} 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"
|
||||
)
|
||||
const signupCounter = meter.createCounter("trpc.user.signup")
|
||||
const signupSuccessCounter = meter.createCounter("trpc.user.signup-success")
|
||||
const signupFailCounter = meter.createCounter("trpc.user.signup-fail")
|
||||
|
||||
export const userMutationRouter = router({
|
||||
creditCard: router({
|
||||
add: protectedProcedure.input(addCreditCardInput).mutation(async function ({
|
||||
@@ -159,7 +144,15 @@ export const userMutationRouter = router({
|
||||
generatePreferencesLink: protectedProcedure.mutation(async function ({
|
||||
ctx,
|
||||
}) {
|
||||
generatePreferencesLinkCounter.add(1)
|
||||
const generatePreferencesLinkCounter = createCounter(
|
||||
"trpc.user",
|
||||
"generatePreferencesLink"
|
||||
)
|
||||
|
||||
const metricsGeneratePreferencesLink = generatePreferencesLinkCounter.init()
|
||||
|
||||
metricsGeneratePreferencesLink.start()
|
||||
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.subscriberId, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
@@ -167,25 +160,7 @@ export const userMutationRouter = router({
|
||||
})
|
||||
|
||||
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,
|
||||
},
|
||||
})
|
||||
)
|
||||
await metricsGeneratePreferencesLink.httpError(apiResponse)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -194,31 +169,23 @@ export const userMutationRouter = router({
|
||||
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())
|
||||
|
||||
metricsGeneratePreferencesLink.validationError(validatedData.error)
|
||||
return null
|
||||
}
|
||||
|
||||
const preferencesLink = new URL(env.SALESFORCE_PREFERENCE_BASE_URL)
|
||||
preferencesLink.searchParams.set("subKey", validatedData.data.subscriberId)
|
||||
|
||||
generatePreferencesLinkSuccessCounter.add(1)
|
||||
metricsGeneratePreferencesLink.success()
|
||||
|
||||
return preferencesLink.toString()
|
||||
}),
|
||||
signup: serviceProcedure.input(signupInput).mutation(async function ({
|
||||
ctx,
|
||||
input,
|
||||
}) {
|
||||
signupCounter.add(1)
|
||||
const signupCounter = createCounter("trpc.user", "signup")
|
||||
const metricsSignup = signupCounter.init()
|
||||
|
||||
const apiResponse = await api.post(api.endpoints.v1.Profile.profile, {
|
||||
body: input,
|
||||
@@ -228,29 +195,13 @@ export const userMutationRouter = router({
|
||||
})
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
await metricsSignup.httpError(apiResponse)
|
||||
const text = await apiResponse.text()
|
||||
signupFailCounter.add(1, {
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
error: text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.user.signup api error",
|
||||
JSON.stringify({
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
error: text,
|
||||
},
|
||||
})
|
||||
)
|
||||
throw serverErrorByStatus(apiResponse.status, text)
|
||||
}
|
||||
signupSuccessCounter.add(1)
|
||||
console.info("api.user.signup success")
|
||||
|
||||
metricsSignup.success()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
redirectUrl: signupVerify[input.language],
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
import { countries } from "@/constants/countries"
|
||||
import * as api from "@/lib/api"
|
||||
import { dt } from "@/lib/dt"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
import {
|
||||
languageProtectedProcedure,
|
||||
protectedProcedure,
|
||||
@@ -10,8 +7,6 @@ import {
|
||||
safeProtectedProcedure,
|
||||
} from "@/server/trpc"
|
||||
|
||||
import { cache } from "@/utils/cache"
|
||||
import * as maskValue from "@/utils/maskValue"
|
||||
import { isValidSession } from "@/utils/session"
|
||||
import { getFriendsMembership, getMembershipCards } from "@/utils/user"
|
||||
|
||||
@@ -20,277 +15,21 @@ import {
|
||||
getSavedPaymentCardsInput,
|
||||
staysInput,
|
||||
} from "./input"
|
||||
import { getFriendTransactionsSchema } from "./output"
|
||||
import {
|
||||
creditCardsSchema,
|
||||
getFriendTransactionsSchema,
|
||||
getStaysSchema,
|
||||
getUserSchema,
|
||||
} from "./output"
|
||||
import { updateStaysBookingUrl } from "./utils"
|
||||
|
||||
import type { Session } from "next-auth"
|
||||
getCreditCards,
|
||||
getPreviousStays,
|
||||
getUpcomingStays,
|
||||
getVerifiedUser,
|
||||
parsedUser,
|
||||
updateStaysBookingUrl,
|
||||
} from "./utils"
|
||||
|
||||
import type {
|
||||
LoginType,
|
||||
TrackingSDKUserData,
|
||||
} from "@/types/components/tracking"
|
||||
import { Transactions } from "@/types/enums/transactions"
|
||||
import type { User } from "@/types/user"
|
||||
|
||||
// OpenTelemetry metrics: User
|
||||
const meter = metrics.getMeter("trpc.user")
|
||||
const getVerifiedUserCounter = meter.createCounter("trpc.user.get")
|
||||
const getVerifiedUserSuccessCounter = meter.createCounter(
|
||||
"trpc.user.get-success"
|
||||
)
|
||||
const getVerifiedUserFailCounter = meter.createCounter("trpc.user.get-fail")
|
||||
|
||||
// OpenTelemetry metrics: Stays
|
||||
const getPreviousStaysCounter = meter.createCounter("trpc.user.stays.previous")
|
||||
const getPreviousStaysSuccessCounter = meter.createCounter(
|
||||
"trpc.user.stays.previous-success"
|
||||
)
|
||||
const getPreviousStaysFailCounter = meter.createCounter(
|
||||
"trpc.user.stays.previous-fail"
|
||||
)
|
||||
const getUpcomingStaysCounter = meter.createCounter("trpc.user.stays.upcoming")
|
||||
const getUpcomingStaysSuccessCounter = meter.createCounter(
|
||||
"trpc.user.stays.upcoming-success"
|
||||
)
|
||||
const getUpcomingStaysFailCounter = meter.createCounter(
|
||||
"trpc.user.stays.upcoming-fail"
|
||||
)
|
||||
|
||||
// OpenTelemetry metrics: Transactions
|
||||
const getFriendTransactionsCounter = meter.createCounter(
|
||||
"trpc.user.transactions.friendTransactions"
|
||||
)
|
||||
const getFriendTransactionsSuccessCounter = meter.createCounter(
|
||||
"trpc.user.transactions.friendTransactions-success"
|
||||
)
|
||||
const getFriendTransactionsFailCounter = meter.createCounter(
|
||||
"trpc.user.transactions.friendTransactions-fail"
|
||||
)
|
||||
|
||||
// OpenTelemetry metrics: Credit Cards
|
||||
const getCreditCardsCounter = meter.createCounter("trpc.user.creditCards")
|
||||
const getCreditCardsSuccessCounter = meter.createCounter(
|
||||
"trpc.user.creditCards-success"
|
||||
)
|
||||
const getCreditCardsFailCounter = meter.createCounter(
|
||||
"trpc.user.creditCards-fail"
|
||||
)
|
||||
|
||||
export const getVerifiedUser = cache(
|
||||
async ({
|
||||
session,
|
||||
includeExtendedPartnerData,
|
||||
}: {
|
||||
session: Session
|
||||
includeExtendedPartnerData?: boolean
|
||||
}) => {
|
||||
const now = Date.now()
|
||||
if (session.token.expires_at && session.token.expires_at < now) {
|
||||
return { error: true, cause: "token_expired" } as const
|
||||
}
|
||||
getVerifiedUserCounter.add(1)
|
||||
console.info("api.user.profile getVerifiedUser start", JSON.stringify({}))
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v2.Profile.profile,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${session.token.access_token}`,
|
||||
},
|
||||
},
|
||||
includeExtendedPartnerData
|
||||
? { includes: "extendedPartnerInformation" }
|
||||
: {}
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
getVerifiedUserFailCounter.add(1, {
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.user.profile getVerifiedUser error",
|
||||
JSON.stringify({
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
if (apiResponse.status === 401) {
|
||||
return { error: true, cause: "unauthorized" } as const
|
||||
} else if (apiResponse.status === 403) {
|
||||
return { error: true, cause: "forbidden" } as const
|
||||
} else if (apiResponse.status === 404) {
|
||||
return { error: true, cause: "notfound" } as const
|
||||
}
|
||||
return {
|
||||
error: true,
|
||||
cause: "unknown",
|
||||
status: apiResponse.status,
|
||||
} as const
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
if (!apiJson.data?.attributes) {
|
||||
getVerifiedUserFailCounter.add(1, {
|
||||
error_type: "data_error",
|
||||
})
|
||||
console.error(
|
||||
"api.user.profile getVerifiedUser data error",
|
||||
JSON.stringify({
|
||||
apiResponse: apiJson,
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
const verifiedData = getUserSchema.safeParse(apiJson)
|
||||
if (!verifiedData.success) {
|
||||
getVerifiedUserFailCounter.add(1, {
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(verifiedData.error),
|
||||
})
|
||||
console.error(
|
||||
"api.user.profile validation error",
|
||||
JSON.stringify({
|
||||
errors: verifiedData.error,
|
||||
apiResponse: apiJson,
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
getVerifiedUserSuccessCounter.add(1)
|
||||
console.info("api.user.profile getVerifiedUser success", JSON.stringify({}))
|
||||
return verifiedData
|
||||
}
|
||||
)
|
||||
|
||||
export 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,
|
||||
membershipNumber: data.membershipNumber,
|
||||
membership: getFriendsMembership(data.loyalty),
|
||||
loyalty: data.loyalty,
|
||||
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
|
||||
}
|
||||
|
||||
const getCreditCards = cache(
|
||||
async ({
|
||||
session,
|
||||
onlyNonExpired,
|
||||
}: {
|
||||
session: Session
|
||||
onlyNonExpired?: boolean
|
||||
}) => {
|
||||
getCreditCardsCounter.add(1)
|
||||
console.info("api.profile.creditCards start", JSON.stringify({}))
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.creditCards, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${session.token.access_token}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
getCreditCardsFailCounter.add(1, {
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.profile.creditCards error ",
|
||||
JSON.stringify({
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const verifiedData = creditCardsSchema.safeParse(apiJson)
|
||||
if (!verifiedData.success) {
|
||||
getCreditCardsFailCounter.add(1, {
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(verifiedData.error),
|
||||
})
|
||||
console.error(
|
||||
"api.profile.creditCards validation error ",
|
||||
JSON.stringify({ error: verifiedData.error })
|
||||
)
|
||||
return null
|
||||
}
|
||||
getCreditCardsSuccessCounter.add(1)
|
||||
console.info("api.profile.creditCards success", JSON.stringify({}))
|
||||
|
||||
return verifiedData.data.data.filter((card) => {
|
||||
if (onlyNonExpired) {
|
||||
try {
|
||||
const expirationDate = dt(card.expirationDate).startOf("day")
|
||||
const currentDate = dt().startOf("day")
|
||||
return expirationDate > currentDate
|
||||
} catch (_) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
export const userQueryRouter = router({
|
||||
get: protectedProcedure
|
||||
@@ -385,11 +124,20 @@ export const userQueryRouter = router({
|
||||
return membershipLevel
|
||||
}),
|
||||
userTrackingInfo: safeProtectedProcedure.query(async function ({ ctx }) {
|
||||
const userTrackingInfoCounter = createCounter("user", "userTrackingInfo")
|
||||
const metricsUserTrackingInfo = userTrackingInfoCounter.init()
|
||||
|
||||
metricsUserTrackingInfo.start()
|
||||
|
||||
const notLoggedInUserTrackingData: TrackingSDKUserData = {
|
||||
loginStatus: "Non-logged in",
|
||||
}
|
||||
|
||||
if (!isValidSession(ctx.session)) {
|
||||
metricsUserTrackingInfo.success({
|
||||
reason: "invalid session",
|
||||
data: notLoggedInUserTrackingData,
|
||||
})
|
||||
return notLoggedInUserTrackingData
|
||||
}
|
||||
|
||||
@@ -397,62 +145,24 @@ export const userQueryRouter = router({
|
||||
const verifiedUserData = await getVerifiedUser({ session: ctx.session })
|
||||
|
||||
if (!verifiedUserData || "error" in verifiedUserData) {
|
||||
return notLoggedInUserTrackingData
|
||||
}
|
||||
|
||||
const params = new URLSearchParams()
|
||||
params.set("limit", "1")
|
||||
getPreviousStaysCounter.add(1, { query: JSON.stringify({ params }) })
|
||||
console.info(
|
||||
"api.booking.stays.past start",
|
||||
JSON.stringify({ query: { params } })
|
||||
)
|
||||
const previousStaysResponse = await api.get(
|
||||
api.endpoints.v1.Booking.Stays.past,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
|
||||
if (!previousStaysResponse.ok) {
|
||||
getPreviousStaysFailCounter.add(1, {
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: previousStaysResponse.status,
|
||||
statusText: previousStaysResponse.statusText,
|
||||
}),
|
||||
metricsUserTrackingInfo.success({
|
||||
reason: "invalid user data",
|
||||
data: notLoggedInUserTrackingData,
|
||||
})
|
||||
console.error(
|
||||
"api.booking.stays.past error",
|
||||
JSON.stringify({
|
||||
error: {
|
||||
status: previousStaysResponse.status,
|
||||
statusText: previousStaysResponse.statusText,
|
||||
},
|
||||
})
|
||||
)
|
||||
return notLoggedInUserTrackingData
|
||||
}
|
||||
|
||||
const previousStaysApiJson = await previousStaysResponse.json()
|
||||
const verifiedPreviousStaysData =
|
||||
getStaysSchema.safeParse(previousStaysApiJson)
|
||||
if (!verifiedPreviousStaysData.success) {
|
||||
getPreviousStaysFailCounter.add(1, {
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(verifiedPreviousStaysData.error),
|
||||
const previousStaysData = await getPreviousStays(
|
||||
ctx.session.token.access_token,
|
||||
1
|
||||
)
|
||||
if (!previousStaysData) {
|
||||
metricsUserTrackingInfo.success({
|
||||
reason: "no previous stays data",
|
||||
data: notLoggedInUserTrackingData,
|
||||
})
|
||||
console.error(
|
||||
"api.booking.stays.past validation error, ",
|
||||
JSON.stringify({ error: verifiedPreviousStaysData.error })
|
||||
)
|
||||
return notLoggedInUserTrackingData
|
||||
}
|
||||
getPreviousStaysSuccessCounter.add(1)
|
||||
console.info("api.booking.stays.past success", JSON.stringify({}))
|
||||
|
||||
const membership = getFriendsMembership(verifiedUserData.data.loyalty)
|
||||
|
||||
@@ -462,13 +172,19 @@ export const userQueryRouter = router({
|
||||
memberId: verifiedUserData.data.profileId,
|
||||
membershipNumber: membership?.membershipNumber,
|
||||
memberLevel: membership?.membershipLevel,
|
||||
noOfNightsStayed: verifiedPreviousStaysData.data.links?.totalCount ?? 0,
|
||||
noOfNightsStayed: previousStaysData.links?.totalCount ?? 0,
|
||||
totalPointsAvailableToSpend: membership?.currentPoints,
|
||||
loginAction: "login success",
|
||||
}
|
||||
|
||||
metricsUserTrackingInfo.success({
|
||||
reason: "valid logged in",
|
||||
data: loggedInUserTrackingData,
|
||||
})
|
||||
|
||||
return loggedInUserTrackingData
|
||||
} catch (error) {
|
||||
console.error("Error in userTrackingInfo:", error)
|
||||
metricsUserTrackingInfo.fail(error)
|
||||
return notLoggedInUserTrackingData
|
||||
}
|
||||
}),
|
||||
@@ -479,93 +195,31 @@ export const userQueryRouter = router({
|
||||
.query(async ({ ctx, input }) => {
|
||||
const { limit, cursor, lang } = input
|
||||
const language = lang || ctx.lang
|
||||
const params: Record<string, string> = { limit }
|
||||
if (cursor) {
|
||||
params.offset = cursor
|
||||
}
|
||||
getPreviousStaysCounter.add(1, { query: JSON.stringify({ params }) })
|
||||
console.info(
|
||||
"api.booking.stays.past start",
|
||||
JSON.stringify({ query: { params } })
|
||||
)
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Booking.Stays.past,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
getPreviousStaysFailCounter.add(1, {
|
||||
query: JSON.stringify({ params }),
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.booking.stays.past error ",
|
||||
JSON.stringify({
|
||||
query: { params },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
|
||||
const verifiedData = getStaysSchema.safeParse(apiJson)
|
||||
if (!verifiedData.success) {
|
||||
getPreviousStaysFailCounter.add(1, {
|
||||
query: JSON.stringify({ params }),
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(verifiedData.error),
|
||||
})
|
||||
console.error(
|
||||
"api.booking.stays.past validation error ",
|
||||
JSON.stringify({
|
||||
query: { params },
|
||||
error: verifiedData.error,
|
||||
})
|
||||
)
|
||||
|
||||
return null
|
||||
}
|
||||
getPreviousStaysSuccessCounter.add(1, {
|
||||
query: JSON.stringify({ params }),
|
||||
})
|
||||
console.info(
|
||||
"api.booking.stays.past success",
|
||||
JSON.stringify({ query: { params } })
|
||||
)
|
||||
const nextCursor =
|
||||
verifiedData.data.links &&
|
||||
verifiedData.data.links.offset < verifiedData.data.links.totalCount
|
||||
? verifiedData.data.links.offset
|
||||
: undefined
|
||||
|
||||
const updatedData = await updateStaysBookingUrl(
|
||||
verifiedData.data.data,
|
||||
const data = await getPreviousStays(
|
||||
ctx.session.token.access_token,
|
||||
language
|
||||
limit,
|
||||
cursor
|
||||
)
|
||||
|
||||
return {
|
||||
data: updatedData,
|
||||
nextCursor,
|
||||
if (data) {
|
||||
const nextCursor =
|
||||
data.links && data.links.offset < data.links.totalCount
|
||||
? data.links.offset
|
||||
: undefined
|
||||
|
||||
const updatedData = await updateStaysBookingUrl(
|
||||
data.data,
|
||||
ctx.session,
|
||||
language
|
||||
)
|
||||
|
||||
return {
|
||||
data: updatedData,
|
||||
nextCursor,
|
||||
}
|
||||
}
|
||||
return null
|
||||
}),
|
||||
|
||||
upcoming: languageProtectedProcedure
|
||||
@@ -573,92 +227,31 @@ export const userQueryRouter = router({
|
||||
.query(async ({ ctx, input }) => {
|
||||
const { limit, cursor, lang } = input
|
||||
const language = lang || ctx.lang
|
||||
const params: Record<string, string> = { limit }
|
||||
if (cursor) {
|
||||
params.offset = cursor
|
||||
}
|
||||
getUpcomingStaysCounter.add(1, {
|
||||
query: JSON.stringify({ params }),
|
||||
})
|
||||
console.info(
|
||||
"api.booking.stays.future start",
|
||||
JSON.stringify({ query: { params } })
|
||||
)
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Booking.Stays.future,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
},
|
||||
},
|
||||
params
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const text = await apiResponse.text()
|
||||
getUpcomingStaysFailCounter.add(1, {
|
||||
query: JSON.stringify({ params }),
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.booking.stays.future error ",
|
||||
JSON.stringify({
|
||||
query: { params },
|
||||
error_type: "http_error",
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const verifiedData = getStaysSchema.safeParse(apiJson)
|
||||
if (!verifiedData.success) {
|
||||
getUpcomingStaysFailCounter.add(1, {
|
||||
query: JSON.stringify({ params }),
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(verifiedData.error),
|
||||
})
|
||||
console.error(
|
||||
"api.booking.stays.future validation error ",
|
||||
JSON.stringify({
|
||||
query: { params },
|
||||
error: verifiedData.error,
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
getUpcomingStaysSuccessCounter.add(1, {
|
||||
query: JSON.stringify({ params }),
|
||||
})
|
||||
console.info("api.booking.stays.future success", {
|
||||
query: JSON.stringify({ params }),
|
||||
})
|
||||
const nextCursor =
|
||||
verifiedData.data.links &&
|
||||
verifiedData.data.links.offset < verifiedData.data.links.totalCount
|
||||
? verifiedData.data.links.offset
|
||||
: undefined
|
||||
|
||||
const updatedData = await updateStaysBookingUrl(
|
||||
verifiedData.data.data,
|
||||
const data = await getUpcomingStays(
|
||||
ctx.session.token.access_token,
|
||||
language
|
||||
limit,
|
||||
cursor
|
||||
)
|
||||
|
||||
return {
|
||||
data: updatedData,
|
||||
nextCursor,
|
||||
if (data) {
|
||||
const nextCursor =
|
||||
data.links && data.links.offset < data.links.totalCount
|
||||
? data.links.offset
|
||||
: undefined
|
||||
|
||||
const updatedData = await updateStaysBookingUrl(
|
||||
data.data,
|
||||
ctx.session,
|
||||
language
|
||||
)
|
||||
|
||||
return {
|
||||
data: updatedData,
|
||||
nextCursor,
|
||||
}
|
||||
}
|
||||
return null
|
||||
}),
|
||||
}),
|
||||
transaction: router({
|
||||
@@ -667,11 +260,18 @@ export const userQueryRouter = router({
|
||||
.query(async ({ ctx, input }) => {
|
||||
const { limit, page } = input
|
||||
|
||||
getFriendTransactionsCounter.add(1)
|
||||
console.info(
|
||||
"api.transaction.friendTransactions start",
|
||||
JSON.stringify({})
|
||||
const friendTransactionsCounter = createCounter(
|
||||
"trpc.user.transactions",
|
||||
"friendTransactions"
|
||||
)
|
||||
|
||||
const metricsFriendTransactions = friendTransactionsCounter.init({
|
||||
limit,
|
||||
page,
|
||||
})
|
||||
|
||||
metricsFriendTransactions.start()
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Profile.Transaction.friendTransactions,
|
||||
{
|
||||
@@ -682,61 +282,20 @@ export const userQueryRouter = router({
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
// switch (apiResponse.status) {
|
||||
// case 400:
|
||||
// throw badRequestError()
|
||||
// case 401:
|
||||
// throw unauthorizedError()
|
||||
// case 403:
|
||||
// throw forbiddenError()
|
||||
// default:
|
||||
// throw internalServerError()
|
||||
// }
|
||||
const text = await apiResponse.text()
|
||||
getFriendTransactionsFailCounter.add(1, {
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify({
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
}),
|
||||
})
|
||||
console.error(
|
||||
"api.transaction.friendTransactions error ",
|
||||
JSON.stringify({
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text,
|
||||
},
|
||||
})
|
||||
)
|
||||
await metricsFriendTransactions.httpError(apiResponse)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const verifiedData = getFriendTransactionsSchema.safeParse(apiJson)
|
||||
if (!verifiedData.success) {
|
||||
getFriendTransactionsFailCounter.add(1, {
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(verifiedData.error),
|
||||
})
|
||||
console.error(
|
||||
"api.transaction.friendTransactions validation error ",
|
||||
JSON.stringify({ error: verifiedData.error })
|
||||
)
|
||||
metricsFriendTransactions.validationError(verifiedData.error)
|
||||
return null
|
||||
}
|
||||
|
||||
getFriendTransactionsSuccessCounter.add(1)
|
||||
console.info(
|
||||
"api.transaction.friendTransactions success",
|
||||
JSON.stringify({})
|
||||
)
|
||||
|
||||
const updatedData = await updateStaysBookingUrl(
|
||||
verifiedData.data.data,
|
||||
ctx.session.token.access_token,
|
||||
ctx.session,
|
||||
ctx.lang
|
||||
)
|
||||
|
||||
@@ -763,7 +322,7 @@ export const userQueryRouter = router({
|
||||
|
||||
const slicedData = pageData.slice(limit * (page - 1), limit * page)
|
||||
|
||||
return {
|
||||
const result = {
|
||||
data: {
|
||||
transactions: slicedData.map(({ type, attributes }) => {
|
||||
return {
|
||||
@@ -786,6 +345,10 @@ export const userQueryRouter = router({
|
||||
totalPages: Math.ceil(pageData.length / limit),
|
||||
},
|
||||
}
|
||||
|
||||
metricsFriendTransactions.success()
|
||||
|
||||
return result
|
||||
}),
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,108 +1,363 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
import { countries } from "@/constants/countries"
|
||||
import { myBookingPath } from "@/constants/myBooking"
|
||||
import { myStay } from "@/constants/routes/myStay"
|
||||
import { env } from "@/env/server"
|
||||
import * as api from "@/lib/api"
|
||||
import { dt } from "@/lib/dt"
|
||||
import { encrypt } from "@/server/routers/utils/encryption"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
|
||||
import { cache } from "@/utils/cache"
|
||||
import * as maskValue from "@/utils/maskValue"
|
||||
import { isValidSession } from "@/utils/session"
|
||||
import { getCurrentWebUrl } from "@/utils/url"
|
||||
import { getFriendsMembership } from "@/utils/user"
|
||||
|
||||
import { encrypt } from "../utils/encryption"
|
||||
import {
|
||||
creditCardsSchema,
|
||||
type FriendTransaction,
|
||||
getStaysSchema,
|
||||
getUserSchema,
|
||||
type Stay,
|
||||
} from "./output"
|
||||
|
||||
import type { Session } from "next-auth"
|
||||
|
||||
import type { User } from "@/types/user"
|
||||
import type { Lang } from "@/constants/languages"
|
||||
import type { FriendTransaction, Stay } from "./output"
|
||||
|
||||
const meter = metrics.getMeter("trpc.user")
|
||||
const getProfileCounter = meter.createCounter("trpc.user.profile")
|
||||
const getProfileSuccessCounter = meter.createCounter(
|
||||
"trpc.user.profile-success"
|
||||
export const getVerifiedUser = cache(
|
||||
async ({
|
||||
session,
|
||||
includeExtendedPartnerData,
|
||||
}: {
|
||||
session: Session
|
||||
includeExtendedPartnerData?: boolean
|
||||
}) => {
|
||||
const getVerifiedUserCounter = createCounter("user", "getVerifiedUser")
|
||||
const metricsGetVerifiedUser = getVerifiedUserCounter.init()
|
||||
|
||||
metricsGetVerifiedUser.start()
|
||||
|
||||
const now = Date.now()
|
||||
if (session.token.expires_at && session.token.expires_at < now) {
|
||||
metricsGetVerifiedUser.dataError(`Token expired`)
|
||||
return { error: true, cause: "token_expired" } as const
|
||||
}
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v2.Profile.profile,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${session.token.access_token}`,
|
||||
},
|
||||
},
|
||||
includeExtendedPartnerData
|
||||
? { includes: "extendedPartnerInformation" }
|
||||
: {}
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
await metricsGetVerifiedUser.httpError(apiResponse)
|
||||
|
||||
if (apiResponse.status === 401) {
|
||||
return { error: true, cause: "unauthorized" } as const
|
||||
} else if (apiResponse.status === 403) {
|
||||
return { error: true, cause: "forbidden" } as const
|
||||
} else if (apiResponse.status === 404) {
|
||||
return { error: true, cause: "notfound" } as const
|
||||
}
|
||||
|
||||
return {
|
||||
error: true,
|
||||
cause: "unknown",
|
||||
status: apiResponse.status,
|
||||
} as const
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
if (!apiJson.data?.attributes) {
|
||||
metricsGetVerifiedUser.dataError(
|
||||
`Missing data attributes in API response`,
|
||||
{
|
||||
data: apiJson,
|
||||
}
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
const verifiedData = getUserSchema.safeParse(apiJson)
|
||||
if (!verifiedData.success) {
|
||||
metricsGetVerifiedUser.validationError(verifiedData.error)
|
||||
return null
|
||||
}
|
||||
|
||||
metricsGetVerifiedUser.success()
|
||||
|
||||
return verifiedData
|
||||
}
|
||||
)
|
||||
const getProfileFailCounter = meter.createCounter("trpc.user.profile-fail")
|
||||
|
||||
async function updateStaysBookingUrl(
|
||||
data: Stay[],
|
||||
token: string,
|
||||
lang: Lang
|
||||
): Promise<Stay[]>
|
||||
export async function getMembershipNumber(
|
||||
session: Session | null
|
||||
): Promise<string | undefined> {
|
||||
if (!isValidSession(session)) return undefined
|
||||
|
||||
async function updateStaysBookingUrl(
|
||||
data: FriendTransaction[],
|
||||
token: string,
|
||||
lang: Lang
|
||||
): Promise<FriendTransaction[]>
|
||||
const verifiedUser = await getVerifiedUser({ session })
|
||||
if (!verifiedUser || "error" in verifiedUser) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
async function updateStaysBookingUrl(
|
||||
data: Stay[] | FriendTransaction[],
|
||||
token: string,
|
||||
lang: Lang
|
||||
return verifiedUser.data.membershipNumber
|
||||
}
|
||||
|
||||
export async function getPreviousStays(
|
||||
accessToken: string,
|
||||
limit: number = 10,
|
||||
cursor?: string
|
||||
) {
|
||||
// Temporary API call needed till we have user name in ctx session data
|
||||
getProfileCounter.add(1)
|
||||
console.info("api.user.profile updatebookingurl start", JSON.stringify({}))
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.profile, {
|
||||
cache: "no-store",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
const getPreviousStaysCounter = createCounter("user", "getPreviousStays")
|
||||
const metricsGetPreviousStays = getPreviousStaysCounter.init({
|
||||
limit,
|
||||
cursor,
|
||||
})
|
||||
|
||||
metricsGetPreviousStays.start()
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Booking.Stays.past,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
limit,
|
||||
cursor,
|
||||
}
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
getProfileFailCounter.add(1, { error: JSON.stringify(apiResponse) })
|
||||
console.info(
|
||||
"api.user.profile updatebookingurl error",
|
||||
JSON.stringify({ error: apiResponse })
|
||||
)
|
||||
return data
|
||||
await metricsGetPreviousStays.httpError(apiResponse)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
if (!apiJson.data?.attributes) {
|
||||
return data
|
||||
|
||||
const verifiedData = getStaysSchema.safeParse(apiJson)
|
||||
if (!verifiedData.success) {
|
||||
metricsGetPreviousStays.validationError(verifiedData.error)
|
||||
return null
|
||||
}
|
||||
|
||||
getProfileSuccessCounter.add(1)
|
||||
console.info("api.user.profile updatebookingurl success", JSON.stringify({}))
|
||||
metricsGetPreviousStays.success()
|
||||
|
||||
return data.map((d) => {
|
||||
const originalString =
|
||||
d.attributes.confirmationNumber.toString() +
|
||||
"," +
|
||||
apiJson.data.attributes.lastName
|
||||
const encryptedBookingValue = encrypt(originalString)
|
||||
|
||||
// Get base URL with fallback for ephemeral environments (like deploy previews).
|
||||
const baseUrl = env.PUBLIC_URL || "https://www.scandichotels.com"
|
||||
|
||||
// Construct Booking URL.
|
||||
const bookingUrl = env.HIDE_FOR_NEXT_RELEASE
|
||||
? new URL(
|
||||
getCurrentWebUrl({
|
||||
path: myBookingPath[lang],
|
||||
lang,
|
||||
baseUrl,
|
||||
})
|
||||
)
|
||||
: new URL(myStay[lang], baseUrl)
|
||||
|
||||
// Add search parameters.
|
||||
if (encryptedBookingValue) {
|
||||
bookingUrl.searchParams.set("RefId", encryptedBookingValue)
|
||||
} else {
|
||||
bookingUrl.searchParams.set("lastName", apiJson.data.attributes.lastName)
|
||||
bookingUrl.searchParams.set(
|
||||
"bookingId",
|
||||
d.attributes.confirmationNumber.toString()
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
...d,
|
||||
attributes: {
|
||||
...d.attributes,
|
||||
bookingUrl: bookingUrl.toString(),
|
||||
},
|
||||
}
|
||||
})
|
||||
return verifiedData.data
|
||||
}
|
||||
|
||||
export { updateStaysBookingUrl }
|
||||
export async function getUpcomingStays(
|
||||
accessToken: string,
|
||||
limit: number = 10,
|
||||
cursor?: string
|
||||
) {
|
||||
const getUpcomingStaysCounter = createCounter("user", "getUpcomingStays")
|
||||
const metricsGetUpcomingStays = getUpcomingStaysCounter.init({
|
||||
limit,
|
||||
cursor,
|
||||
})
|
||||
|
||||
metricsGetUpcomingStays.start()
|
||||
|
||||
const apiResponse = await api.get(
|
||||
api.endpoints.v1.Booking.Stays.future,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
limit,
|
||||
cursor,
|
||||
}
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
await metricsGetUpcomingStays.httpError(apiResponse)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
|
||||
const verifiedData = getStaysSchema.safeParse(apiJson)
|
||||
if (!verifiedData.success) {
|
||||
metricsGetUpcomingStays.validationError(verifiedData.error)
|
||||
return null
|
||||
}
|
||||
|
||||
metricsGetUpcomingStays.success()
|
||||
|
||||
return verifiedData.data
|
||||
}
|
||||
|
||||
export 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,
|
||||
membershipNumber: data.membershipNumber,
|
||||
membership: getFriendsMembership(data.loyalty),
|
||||
loyalty: data.loyalty,
|
||||
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 getCreditCards = cache(
|
||||
async ({
|
||||
session,
|
||||
onlyNonExpired,
|
||||
}: {
|
||||
session: Session
|
||||
onlyNonExpired?: boolean
|
||||
}) => {
|
||||
const getCreditCardsCounter = createCounter("user", "getCreditCards")
|
||||
const metricsGetCreditCards = getCreditCardsCounter.init({
|
||||
onlyNonExpired,
|
||||
})
|
||||
|
||||
metricsGetCreditCards.start()
|
||||
|
||||
const apiResponse = await api.get(api.endpoints.v1.Profile.creditCards, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${session.token.access_token}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
await metricsGetCreditCards.httpError(apiResponse)
|
||||
return null
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const verifiedData = creditCardsSchema.safeParse(apiJson)
|
||||
if (!verifiedData.success) {
|
||||
metricsGetCreditCards.validationError(verifiedData.error)
|
||||
return null
|
||||
}
|
||||
|
||||
const result = verifiedData.data.data.filter((card) => {
|
||||
if (onlyNonExpired) {
|
||||
try {
|
||||
const expirationDate = dt(card.expirationDate).startOf("day")
|
||||
const currentDate = dt().startOf("day")
|
||||
return expirationDate > currentDate
|
||||
} catch (_) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
metricsGetCreditCards.success()
|
||||
|
||||
return result
|
||||
}
|
||||
)
|
||||
|
||||
export async function updateStaysBookingUrl(
|
||||
data: Stay[],
|
||||
session: Session,
|
||||
lang: Lang
|
||||
): Promise<Stay[]>
|
||||
|
||||
export async function updateStaysBookingUrl(
|
||||
data: FriendTransaction[],
|
||||
session: Session,
|
||||
lang: Lang
|
||||
): Promise<FriendTransaction[]>
|
||||
|
||||
export async function updateStaysBookingUrl(
|
||||
data: Stay[] | FriendTransaction[],
|
||||
session: Session,
|
||||
lang: Lang
|
||||
) {
|
||||
const user = await getVerifiedUser({
|
||||
session,
|
||||
})
|
||||
|
||||
if (user && !("error" in user)) {
|
||||
return data.map((d) => {
|
||||
const originalString =
|
||||
d.attributes.confirmationNumber.toString() + "," + user.data.lastName
|
||||
const encryptedBookingValue = encrypt(originalString)
|
||||
|
||||
// Get base URL with fallback for ephemeral environments (like deploy previews).
|
||||
const baseUrl = env.PUBLIC_URL || "https://www.scandichotels.com"
|
||||
|
||||
// Construct Booking URL.
|
||||
const bookingUrl = env.HIDE_FOR_NEXT_RELEASE
|
||||
? new URL(
|
||||
getCurrentWebUrl({
|
||||
path: myBookingPath[lang],
|
||||
lang,
|
||||
baseUrl,
|
||||
})
|
||||
)
|
||||
: new URL(myStay[lang], baseUrl)
|
||||
|
||||
// Add search parameters.
|
||||
if (encryptedBookingValue) {
|
||||
bookingUrl.searchParams.set("RefId", encryptedBookingValue)
|
||||
} else {
|
||||
bookingUrl.searchParams.set("lastName", user.data.lastName)
|
||||
bookingUrl.searchParams.set(
|
||||
"bookingId",
|
||||
d.attributes.confirmationNumber.toString()
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
...d,
|
||||
attributes: {
|
||||
...d.attributes,
|
||||
bookingUrl: bookingUrl.toString(),
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
135
apps/scandic-web/server/telemetry/index.test.ts
Normal file
135
apps/scandic-web/server/telemetry/index.test.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import { describe, expect, test } from "@jest/globals"
|
||||
|
||||
import { sanitize } from "./"
|
||||
|
||||
describe("sanitize", () => {
|
||||
test("should handle valid primitive attributes", () => {
|
||||
const input = {
|
||||
key1: "value1",
|
||||
key2: 10,
|
||||
key3: true,
|
||||
}
|
||||
const expected = {
|
||||
key1: "value1",
|
||||
key2: 10,
|
||||
key3: true,
|
||||
}
|
||||
expect(sanitize(input)).toEqual(expected)
|
||||
})
|
||||
|
||||
test("should handle valid array attributes", () => {
|
||||
const input = {
|
||||
key1: ["value1", "value2"],
|
||||
key2: [1, 2, 3],
|
||||
key3: [true, false, true],
|
||||
key4: [null, undefined, "a", 1, true],
|
||||
}
|
||||
const expected = {
|
||||
"key1.0": "value1",
|
||||
"key1.1": "value2",
|
||||
"key2.0": 1,
|
||||
"key2.1": 2,
|
||||
"key2.2": 3,
|
||||
"key3.0": true,
|
||||
"key3.1": false,
|
||||
"key3.2": true,
|
||||
"key4.0": null,
|
||||
"key4.1": undefined,
|
||||
"key4.2": "a",
|
||||
"key4.3": 1,
|
||||
"key4.4": true,
|
||||
}
|
||||
expect(sanitize(input)).toEqual(expected)
|
||||
})
|
||||
|
||||
test("should stringify non-valid attributes", () => {
|
||||
const input = {
|
||||
key1: new Date("2024-08-08T12:00:00Z"),
|
||||
key2: { nested: "object" },
|
||||
}
|
||||
const expected = {
|
||||
key1: '"2024-08-08T12:00:00.000Z"',
|
||||
"key2.nested": "object",
|
||||
}
|
||||
expect(sanitize(input)).toEqual(expected)
|
||||
})
|
||||
|
||||
test("should handle nested valid attributes", () => {
|
||||
const input = {
|
||||
key1: "Example",
|
||||
key2: 10,
|
||||
nested: {
|
||||
nestedKey1: "Value",
|
||||
nestedKey2: {
|
||||
nestedKey2Key1: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
const expected = {
|
||||
key1: "Example",
|
||||
key2: 10,
|
||||
"nested.nestedKey1": "Value",
|
||||
"nested.nestedKey2.nestedKey2Key1": true,
|
||||
}
|
||||
expect(sanitize(input)).toEqual(expected)
|
||||
})
|
||||
|
||||
test("should handle a mix of valid and non-valid nested attributes", () => {
|
||||
const input = {
|
||||
key1: "Example",
|
||||
key2: 10,
|
||||
nested: {
|
||||
nestedKey1: "Value",
|
||||
nestedKey2: {
|
||||
nestedKey2Key1: true,
|
||||
nestedKey2Key2: new Date("2024-08-08T12:00:00Z"),
|
||||
},
|
||||
nestedKey3: {
|
||||
reallyNested: "hello",
|
||||
},
|
||||
},
|
||||
nonPrimitive: new Date("2024-08-08T13:00:00Z"),
|
||||
}
|
||||
const expected = {
|
||||
key1: "Example",
|
||||
key2: 10,
|
||||
"nested.nestedKey1": "Value",
|
||||
"nested.nestedKey2.nestedKey2Key1": true,
|
||||
"nested.nestedKey2.nestedKey2Key2": '"2024-08-08T12:00:00.000Z"',
|
||||
"nested.nestedKey3.reallyNested": "hello",
|
||||
nonPrimitive: '"2024-08-08T13:00:00.000Z"',
|
||||
}
|
||||
expect(sanitize(input)).toEqual(expected)
|
||||
})
|
||||
|
||||
test("should throw an error when a function is passed", () => {
|
||||
const input = {
|
||||
key1: () => {},
|
||||
}
|
||||
expect(() => sanitize(input)).toThrowError("Cannot sanitize function")
|
||||
})
|
||||
|
||||
test("should throw an error when input not an object", () => {
|
||||
// @ts-expect-error: array not allowed. We do this here to make sure the
|
||||
// function not only relies on TS but actively blocks arrays as input.
|
||||
expect(() => sanitize(null)).toThrowError()
|
||||
|
||||
// @ts-expect-error: array not allowed. We do this here to make sure the
|
||||
// function not only relies on TS but actively blocks arrays as input.
|
||||
expect(() => sanitize(undefined)).toThrowError()
|
||||
|
||||
// @ts-expect-error: array not allowed. We do this here to make sure the
|
||||
// function not only relies on TS but actively blocks arrays as input.
|
||||
expect(() => sanitize("")).toThrowError()
|
||||
|
||||
// @ts-expect-error: array not allowed. We do this here to make sure the
|
||||
// function not only relies on TS but actively blocks arrays as input.
|
||||
expect(() => sanitize([1, 2, 3])).toThrowError()
|
||||
})
|
||||
|
||||
test("should handle empty input", () => {
|
||||
const input = {}
|
||||
const expected = {}
|
||||
expect(sanitize(input)).toEqual(expected)
|
||||
})
|
||||
})
|
||||
309
apps/scandic-web/server/telemetry/index.ts
Normal file
309
apps/scandic-web/server/telemetry/index.ts
Normal file
@@ -0,0 +1,309 @@
|
||||
// Central place for telemetry
|
||||
// TODO: Replace all of this with proper tracers and events
|
||||
|
||||
import {
|
||||
type Attributes,
|
||||
type AttributeValue,
|
||||
metrics,
|
||||
} from "@opentelemetry/api"
|
||||
import deepmerge from "deepmerge"
|
||||
import { flatten } from "flat"
|
||||
import {
|
||||
every,
|
||||
isArray,
|
||||
isBoolean,
|
||||
isFunction,
|
||||
isNull,
|
||||
isNumber,
|
||||
isObject,
|
||||
isPlainObject,
|
||||
isString,
|
||||
isUndefined,
|
||||
keys,
|
||||
mapValues,
|
||||
} from "lodash-es"
|
||||
|
||||
import type { ZodError } from "zod"
|
||||
|
||||
type AttributesInput = Record<string, unknown>
|
||||
|
||||
function isAttributesInput(value: unknown): value is AttributesInput {
|
||||
return (
|
||||
isObject(value) &&
|
||||
!isArray(value) &&
|
||||
!isNull(value) &&
|
||||
keys(value).length > 0 &&
|
||||
every(keys(value), isString)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given value is a valid OpenTelemetry `AttributeValue`.
|
||||
* An `AttributeValue` can be a `string`, `number`, `boolean`, or a homogenous
|
||||
* array containing only `null`, `undefined`, `string`, `number`, or `boolean`.
|
||||
*
|
||||
* @param value The value to check.
|
||||
* @returns `true` if the value is a valid `AttributeValue`, `false` otherwise.
|
||||
*/
|
||||
export function isValidAttributeValue(value: unknown): value is AttributeValue {
|
||||
if (isString(value) || isNumber(value) || isBoolean(value)) {
|
||||
return true
|
||||
}
|
||||
if (isArray(value)) {
|
||||
return every(
|
||||
value,
|
||||
(item) =>
|
||||
isNull(item) ||
|
||||
isUndefined(item) ||
|
||||
isString(item) ||
|
||||
isNumber(item) ||
|
||||
isBoolean(item)
|
||||
)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes an input object, ensuring its values are valid OpenTelemetry
|
||||
* `AttributeValue` or `JSON.stringify()` representations as a fallback.
|
||||
* It recursively processes nested objects and flattens the final object to one
|
||||
* level deep with dot delimited keys for nested values.
|
||||
*
|
||||
* @param data The input object to sanitize.
|
||||
* @returns The resulting object.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { sanitize } from '@/server/telemetry';
|
||||
*
|
||||
* const input = {
|
||||
* key1: "Example",
|
||||
* key2: 10,
|
||||
* nested: {
|
||||
* nestedKey1: "Value",
|
||||
* nestedKey2: {
|
||||
* nestedKey2Key1: true,
|
||||
* },
|
||||
* },
|
||||
* };
|
||||
*
|
||||
* const sanitized = sanitize(input);
|
||||
* console.log(sanitized);
|
||||
* // {
|
||||
* // key1: "Example",
|
||||
* // key2: 10,
|
||||
* // "nested.nestedKey1": "Value",
|
||||
* // "nested.nestedKey2.nestedKey2Key1": true,
|
||||
* // }
|
||||
* ```
|
||||
*/
|
||||
export function sanitize(data: AttributesInput): Attributes {
|
||||
if (!isPlainObject(data)) {
|
||||
throw new Error(`Input must be an object, got ${JSON.stringify(data)}`)
|
||||
}
|
||||
|
||||
return flatten(
|
||||
mapValues(data, (value) => {
|
||||
if (isFunction(value)) {
|
||||
throw new Error("Cannot sanitize function")
|
||||
} else if (isValidAttributeValue(value)) {
|
||||
return value
|
||||
} else if (isAttributesInput(value)) {
|
||||
return sanitize(value)
|
||||
}
|
||||
|
||||
return JSON.stringify(value)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an object that holds three OpenTelemetry counter instruments. One
|
||||
* that represents the counter itself, one for the success and one for any fail.
|
||||
* The object contains an `init method that acts as a factory to create the a
|
||||
* final object that holds methods to record different types of events one the
|
||||
* appropriate counter.
|
||||
*
|
||||
* @param meterName The name of the OpenTelemetry meter to create.
|
||||
* @param counterName The name of the counter instrument to create.
|
||||
* @returns An object with an `init` method that returns an object
|
||||
* with methods for recording counter events.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* See the codebase for reference usage.
|
||||
*/
|
||||
export function createCounter(meterName: string, counterName: string) {
|
||||
const meter = metrics.getMeter(meterName)
|
||||
|
||||
const fullName = `${meterName}.${counterName}`
|
||||
|
||||
const counter = meter.createCounter(fullName)
|
||||
const success = meter.createCounter(`${fullName}-success`)
|
||||
const fail = meter.createCounter(`${fullName}-fail`)
|
||||
|
||||
return {
|
||||
/**
|
||||
* Initializes the counter event handlers with a set of base attributes.
|
||||
* These attributes will be included in all recorded events.
|
||||
*
|
||||
* @param baseAttrs - The base attributes to associate with the counter. Defaults to an empty object.
|
||||
* @returns An object with methods to record specific counter events.
|
||||
*/
|
||||
init(baseAttrs: AttributesInput = {}) {
|
||||
return {
|
||||
/**
|
||||
* Records an event for the main counter.
|
||||
*
|
||||
* @param attrs - Additional attributes specific to this 'start' event. Defaults to an empty object.
|
||||
*/
|
||||
start(attrs: AttributesInput = {}) {
|
||||
const mergedAttrs = deepmerge.all<AttributesInput>([baseAttrs, attrs])
|
||||
const finalAttrs = sanitize(mergedAttrs)
|
||||
|
||||
counter.add(1, finalAttrs)
|
||||
console.info(`[${fullName}] start:`, finalAttrs)
|
||||
},
|
||||
|
||||
/**
|
||||
* Records an event for the success counter.
|
||||
*
|
||||
* @param attrs - Additional attributes specific to this 'success' event. Defaults to an empty object.
|
||||
*/
|
||||
success(attrs: AttributesInput = {}) {
|
||||
const mergedAttrs = deepmerge.all<AttributesInput>([baseAttrs, attrs])
|
||||
const finalAttrs = sanitize(mergedAttrs)
|
||||
|
||||
success.add(1, finalAttrs)
|
||||
console.info(`[${fullName}] success:`, finalAttrs)
|
||||
},
|
||||
|
||||
/**
|
||||
* Records an event of type `data_error` for the fail counter.
|
||||
* Used when some dependent data could not be resolved during the
|
||||
* operation. Note that "no data" also exists and might be more
|
||||
* appropriate in certain situations.
|
||||
*
|
||||
* @param errorMsg - A message describing the data error.
|
||||
* @param attrs - Additional attributes specific to this 'dataError' event. Defaults to an empty object.
|
||||
*/
|
||||
dataError(errorMsg: string, attrs: AttributesInput = {}) {
|
||||
const mergedAttrs = deepmerge.all<AttributesInput>([
|
||||
baseAttrs,
|
||||
attrs,
|
||||
{
|
||||
error_type: "data_error",
|
||||
error: errorMsg,
|
||||
},
|
||||
])
|
||||
const finalAttrs = sanitize(mergedAttrs)
|
||||
|
||||
fail.add(1, finalAttrs)
|
||||
console.error(`[${fullName}] dataError:`, finalAttrs)
|
||||
},
|
||||
|
||||
/**
|
||||
* Records an event of type `not_found` for the fail counter.
|
||||
* Used when some dependent data could not be found during the operation.
|
||||
* Note that when there is an error resolving the data, the
|
||||
* `dataError` method might be more appropriate.
|
||||
*
|
||||
* @param attrs - Additional attributes specific to this 'noDataError' event. Defaults to an empty object.
|
||||
*/
|
||||
noDataError(attrs: AttributesInput = {}) {
|
||||
const mergedAttrs = deepmerge.all<AttributesInput>([
|
||||
baseAttrs,
|
||||
attrs,
|
||||
{
|
||||
error_type: "not_found",
|
||||
},
|
||||
])
|
||||
const finalAttrs = sanitize(mergedAttrs)
|
||||
|
||||
fail.add(1, finalAttrs)
|
||||
console.error(`[${fullName}] noDataError:`, finalAttrs)
|
||||
},
|
||||
|
||||
/**
|
||||
* Records an event of type `validation_error` for the fail counter.
|
||||
* Used when a Zod schema fails validation.
|
||||
*
|
||||
* @param zodError - The {@link ZodError} object representing the validation error.
|
||||
*/
|
||||
validationError(zodError: ZodError) {
|
||||
const mergedAttrs = deepmerge.all<AttributesInput>([
|
||||
baseAttrs,
|
||||
{
|
||||
error_type: "validation_error",
|
||||
error: zodError,
|
||||
},
|
||||
])
|
||||
const finalAttrs = sanitize(mergedAttrs)
|
||||
|
||||
fail.add(1, finalAttrs)
|
||||
console.error(`[${fullName}] validationError:`, finalAttrs)
|
||||
},
|
||||
|
||||
/**
|
||||
* Records an event of type `http_error` for the fail counter.
|
||||
* Used when a `fetch(...)` call fails. **Note**: This method must be
|
||||
* `await`ed as it is asynchronous!
|
||||
* The given {@link Response} must be unprocessed and will be cloned
|
||||
* to avoid interfering with its consumption outside this function.
|
||||
*
|
||||
* @param response - The HTTP {@link Response} object.
|
||||
*/
|
||||
async httpError(response: Response) {
|
||||
const res = response.clone()
|
||||
const text = await res.text()
|
||||
|
||||
const mergedAttrs = deepmerge.all<AttributesInput>([
|
||||
baseAttrs,
|
||||
{
|
||||
error_type: "http_error",
|
||||
error: {
|
||||
status: res.status,
|
||||
statusText: res.statusText,
|
||||
text,
|
||||
},
|
||||
},
|
||||
])
|
||||
const finalAttrs = sanitize(mergedAttrs)
|
||||
|
||||
fail.add(1, finalAttrs)
|
||||
console.error(`[${fullName}] httpError:`, finalAttrs)
|
||||
},
|
||||
|
||||
/**
|
||||
* Records an event of type `error` for the fail counter.
|
||||
* Used when an error is thrown or an exception is caught, or as a
|
||||
* general-purpose way to record an 'error' on the fail counter.
|
||||
*
|
||||
* @param err - An optional error object or message associated with the
|
||||
* failure. Usually an instance of {@link Error} or a string.
|
||||
*/
|
||||
fail(err?: unknown) {
|
||||
let msg = "unknown"
|
||||
|
||||
if (err && err instanceof Error) {
|
||||
msg = err.message
|
||||
} else if (typeof err === "string") {
|
||||
msg = err
|
||||
}
|
||||
|
||||
const mergedAttrs = deepmerge.all<AttributesInput>([
|
||||
baseAttrs,
|
||||
{
|
||||
error_type: "error",
|
||||
error: msg,
|
||||
},
|
||||
])
|
||||
const finalAttrs = sanitize(mergedAttrs)
|
||||
|
||||
fail.add(1, finalAttrs)
|
||||
console.error(`[${fullName}] fail:`, finalAttrs)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,12 @@
|
||||
import { metrics, trace, type Tracer } from "@opentelemetry/api"
|
||||
import { trace, type Tracer } from "@opentelemetry/api"
|
||||
|
||||
import { env } from "@/env/server"
|
||||
import { createCounter } from "@/server/telemetry"
|
||||
|
||||
import { getCacheClient } from "@/services/dataCache"
|
||||
|
||||
import type { ServiceTokenResponse } from "@/types/tokens"
|
||||
|
||||
// OpenTelemetry metrics: Service token
|
||||
const meter = metrics.getMeter("trpc.context.serviceToken")
|
||||
const fetchServiceTokenCounter = meter.createCounter(
|
||||
"trpc.context.serviceToken.fetch-new-token"
|
||||
)
|
||||
|
||||
const fetchServiceTokenFailCounter = meter.createCounter(
|
||||
"trpc.context.serviceToken.fetch-fail"
|
||||
)
|
||||
|
||||
export async function getServiceToken() {
|
||||
const tracer = trace.getTracer("getServiceToken")
|
||||
|
||||
@@ -62,15 +53,32 @@ async function getOrSetServiceTokenFromCache(
|
||||
}
|
||||
|
||||
async function getJwt(scopes: string[]) {
|
||||
fetchServiceTokenCounter.add(1)
|
||||
const getJwtCounter = createCounter("tokenManager", "getJwt")
|
||||
const metricsGetJwt = getJwtCounter.init({
|
||||
scopes,
|
||||
})
|
||||
|
||||
metricsGetJwt.start()
|
||||
|
||||
const jwt = await fetchServiceToken(scopes)
|
||||
|
||||
const expiresAt = Date.now() + jwt.expires_in * 1000
|
||||
|
||||
metricsGetJwt.success()
|
||||
|
||||
return { expiresAt, jwt }
|
||||
}
|
||||
|
||||
async function fetchServiceToken(scopes: string[]) {
|
||||
fetchServiceTokenCounter.add(1)
|
||||
const fetchServiceTokenCounter = createCounter(
|
||||
"tokenManager",
|
||||
"fetchServiceToken"
|
||||
)
|
||||
const metricsFetchServiceToken = fetchServiceTokenCounter.init({
|
||||
scopes,
|
||||
})
|
||||
|
||||
metricsFetchServiceToken.start()
|
||||
|
||||
const response = await fetch(`${env.CURITY_ISSUER_USER}/oauth/v2/token`, {
|
||||
method: "POST",
|
||||
@@ -87,36 +95,23 @@ async function fetchServiceToken(scopes: string[]) {
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
await metricsFetchServiceToken.httpError(response)
|
||||
|
||||
const text = await response.text()
|
||||
const error = {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
text,
|
||||
}
|
||||
|
||||
fetchServiceTokenFailCounter.add(1, {
|
||||
error_type: "http_error",
|
||||
error: JSON.stringify(error),
|
||||
})
|
||||
|
||||
console.error(
|
||||
"fetchServiceToken error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
grant_type: "client_credentials",
|
||||
client_id: env.CURITY_CLIENT_ID_SERVICE,
|
||||
scope: scopes.join(" "),
|
||||
},
|
||||
error,
|
||||
})
|
||||
)
|
||||
|
||||
throw new Error(
|
||||
`[fetchServiceToken] Failed to obtain service token: ${JSON.stringify(error)}`
|
||||
`[fetchServiceToken] Failed to obtain service token: ${JSON.stringify({
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
text,
|
||||
})}`
|
||||
)
|
||||
}
|
||||
|
||||
return response.json() as Promise<ServiceTokenResponse>
|
||||
const result = response.json() as Promise<ServiceTokenResponse>
|
||||
|
||||
metricsFetchServiceToken.success()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function getServiceTokenCacheKey(scopes: string[]): string {
|
||||
|
||||
7
apps/scandic-web/tsconfig.spec.json
Normal file
7
apps/scandic-web/tsconfig.spec.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"target": "ESNext",
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,9 @@ export interface AncillaryPackagesInput
|
||||
export interface PackagesInput
|
||||
extends z.input<typeof roomPackagesInputSchema> {}
|
||||
|
||||
export interface PackagesOutput
|
||||
extends z.output<typeof roomPackagesInputSchema> {}
|
||||
|
||||
export type Packages = z.output<typeof packagesSchema>
|
||||
export type Package = NonNullable<Packages>[number]
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ export type HotelsByHotelIdsAvailabilityInputSchema = z.output<
|
||||
export type RoomsAvailabilityInputSchema = z.input<
|
||||
typeof selectRateRoomsAvailabilityInputSchema
|
||||
>
|
||||
export type RoomsAvailabilityOutputSchema = z.output<
|
||||
typeof selectRateRoomsAvailabilityInputSchema
|
||||
>
|
||||
export type RoomsAvailabilityInputRoom =
|
||||
RoomsAvailabilityInputSchema["booking"]["rooms"][number]
|
||||
export type RoomsAvailabilityExtendedInputSchema = z.input<
|
||||
|
||||
153
yarn.lock
153
yarn.lock
@@ -6402,6 +6402,7 @@ __metadata:
|
||||
"@types/geojson": "npm:^7946.0.16"
|
||||
"@types/jest": "npm:^29.5.12"
|
||||
"@types/json-stable-stringify-without-jsonify": "npm:^1.0.2"
|
||||
"@types/lodash-es": "npm:^4"
|
||||
"@types/node": "npm:^20"
|
||||
"@types/react": "npm:^18"
|
||||
"@types/react-dom": "npm:^18"
|
||||
@@ -6429,6 +6430,7 @@ __metadata:
|
||||
eslint-plugin-simple-import-sort: "npm:^12.1.0"
|
||||
fast-deep-equal: "npm:^3.1.3"
|
||||
fetch-retry: "npm:^6.0.0"
|
||||
flat: "npm:^6.0.1"
|
||||
framer-motion: "npm:^11.3.28"
|
||||
fuse.js: "npm:^7.1.0"
|
||||
graphql: "npm:^16.8.1"
|
||||
@@ -6446,6 +6448,7 @@ __metadata:
|
||||
json-stable-stringify-without-jsonify: "npm:^1.0.1"
|
||||
libphonenumber-js: "npm:^1.10.60"
|
||||
lint-staged: "npm:^15.2.2"
|
||||
lodash-es: "npm:^4.17.21"
|
||||
material-symbols: "npm:^0.29.0"
|
||||
nanoid: "npm:^5.0.9"
|
||||
netlify-plugin-cypress: "npm:^2.2.1"
|
||||
@@ -6464,13 +6467,13 @@ __metadata:
|
||||
react-material-symbols: "npm:^4.4.0"
|
||||
react-to-print: "npm:^3.0.2"
|
||||
schema-dts: "npm:^1.1.2"
|
||||
secure-json-parse: "npm:^4.0.0"
|
||||
server-only: "npm:^0.0.1"
|
||||
slugify: "npm:^1.6.6"
|
||||
sonner: "npm:^1.7.0"
|
||||
start-server-and-test: "npm:^2.0.3"
|
||||
supercluster: "npm:^8.0.1"
|
||||
superjson: "npm:^2.2.1"
|
||||
ts-jest: "npm:^29.3.2"
|
||||
ts-morph: "npm:^25.0.1"
|
||||
ts-node: "npm:^10.9.2"
|
||||
typescript: "npm:5.4.5"
|
||||
@@ -8227,6 +8230,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/lodash-es@npm:^4":
|
||||
version: 4.17.12
|
||||
resolution: "@types/lodash-es@npm:4.17.12"
|
||||
dependencies:
|
||||
"@types/lodash": "npm:*"
|
||||
checksum: 10c0/5d12d2cede07f07ab067541371ed1b838a33edb3c35cb81b73284e93c6fd0c4bbeaefee984e69294bffb53f62d7272c5d679fdba8e595ff71e11d00f2601dde0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/lodash@npm:*":
|
||||
version: 4.17.16
|
||||
resolution: "@types/lodash@npm:4.17.16"
|
||||
checksum: 10c0/cf017901b8ab1d7aabc86d5189d9288f4f99f19a75caf020c0e2c77b8d4cead4db0d0b842d009b029339f92399f49f34377dd7c2721053388f251778b4c23534
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/mdx@npm:^2.0.0":
|
||||
version: 2.0.13
|
||||
resolution: "@types/mdx@npm:2.0.13"
|
||||
@@ -9608,7 +9627,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"async@npm:^3.2.0":
|
||||
"async@npm:^3.2.0, async@npm:^3.2.3":
|
||||
version: 3.2.6
|
||||
resolution: "async@npm:3.2.6"
|
||||
checksum: 10c0/36484bb15ceddf07078688d95e27076379cc2f87b10c03b6dd8a83e89475a3c8df5848859dd06a4c95af1e4c16fc973de0171a77f18ea00be899aca2a4f85e70
|
||||
@@ -9961,6 +9980,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bs-logger@npm:^0.2.6":
|
||||
version: 0.2.6
|
||||
resolution: "bs-logger@npm:0.2.6"
|
||||
dependencies:
|
||||
fast-json-stable-stringify: "npm:2.x"
|
||||
checksum: 10c0/80e89aaaed4b68e3374ce936f2eb097456a0dddbf11f75238dbd53140b1e39259f0d248a5089ed456f1158984f22191c3658d54a713982f676709fbe1a6fa5a0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bser@npm:2.1.1":
|
||||
version: 2.1.1
|
||||
resolution: "bser@npm:2.1.1"
|
||||
@@ -10193,7 +10221,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chalk@npm:4, chalk@npm:^4.0.0, chalk@npm:^4.1.0":
|
||||
"chalk@npm:4, chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0":
|
||||
version: 4.1.2
|
||||
resolution: "chalk@npm:4.1.2"
|
||||
dependencies:
|
||||
@@ -11588,6 +11616,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ejs@npm:^3.1.10":
|
||||
version: 3.1.10
|
||||
resolution: "ejs@npm:3.1.10"
|
||||
dependencies:
|
||||
jake: "npm:^10.8.5"
|
||||
bin:
|
||||
ejs: bin/cli.js
|
||||
checksum: 10c0/52eade9e68416ed04f7f92c492183340582a36482836b11eab97b159fcdcfdedc62233a1bf0bf5e5e1851c501f2dca0e2e9afd111db2599e4e7f53ee29429ae1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"electron-to-chromium@npm:^1.5.73":
|
||||
version: 1.5.103
|
||||
resolution: "electron-to-chromium@npm:1.5.103"
|
||||
@@ -12646,7 +12685,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0":
|
||||
"fast-json-stable-stringify@npm:2.x, fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "fast-json-stable-stringify@npm:2.1.0"
|
||||
checksum: 10c0/7f081eb0b8a64e0057b3bb03f974b3ef00135fbf36c1c710895cd9300f13c94ba809bb3a81cf4e1b03f6e5285610a61abbd7602d0652de423144dfee5a389c9b
|
||||
@@ -12758,6 +12797,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"filelist@npm:^1.0.4":
|
||||
version: 1.0.4
|
||||
resolution: "filelist@npm:1.0.4"
|
||||
dependencies:
|
||||
minimatch: "npm:^5.0.1"
|
||||
checksum: 10c0/426b1de3944a3d153b053f1c0ebfd02dccd0308a4f9e832ad220707a6d1f1b3c9784d6cadf6b2f68f09a57565f63ebc7bcdc913ccf8012d834f472c46e596f41
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fill-range@npm:^7.1.1":
|
||||
version: 7.1.1
|
||||
resolution: "fill-range@npm:7.1.1"
|
||||
@@ -12807,6 +12855,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"flat@npm:^6.0.1":
|
||||
version: 6.0.1
|
||||
resolution: "flat@npm:6.0.1"
|
||||
bin:
|
||||
flat: cli.js
|
||||
checksum: 10c0/9dc0dbe6e2acc012512a53130d9ba1c82c1a596cdca91b23d11716348361c4a68928409bb4433c4493a17595c3efd0cab9f09e23dd3f9962a58af225c3efc23a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"flatted@npm:^3.2.9":
|
||||
version: 3.3.3
|
||||
resolution: "flatted@npm:3.3.3"
|
||||
@@ -14587,6 +14644,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"jake@npm:^10.8.5":
|
||||
version: 10.9.2
|
||||
resolution: "jake@npm:10.9.2"
|
||||
dependencies:
|
||||
async: "npm:^3.2.3"
|
||||
chalk: "npm:^4.0.2"
|
||||
filelist: "npm:^1.0.4"
|
||||
minimatch: "npm:^3.1.2"
|
||||
bin:
|
||||
jake: bin/cli.js
|
||||
checksum: 10c0/c4597b5ed9b6a908252feab296485a4f87cba9e26d6c20e0ca144fb69e0c40203d34a2efddb33b3d297b8bd59605e6c1f44f6221ca1e10e69175ecbf3ff5fe31
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"jest-changed-files@npm:^29.7.0":
|
||||
version: 29.7.0
|
||||
resolution: "jest-changed-files@npm:29.7.0"
|
||||
@@ -14972,7 +15043,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"jest-util@npm:^29.7.0":
|
||||
"jest-util@npm:^29.0.0, jest-util@npm:^29.7.0":
|
||||
version: 29.7.0
|
||||
resolution: "jest-util@npm:29.7.0"
|
||||
dependencies:
|
||||
@@ -15849,6 +15920,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash.memoize@npm:^4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "lodash.memoize@npm:4.1.2"
|
||||
checksum: 10c0/c8713e51eccc650422716a14cece1809cfe34bc5ab5e242b7f8b4e2241c2483697b971a604252807689b9dd69bfe3a98852e19a5b89d506b000b4187a1285df8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash.merge@npm:^4.6.2":
|
||||
version: 4.6.2
|
||||
resolution: "lodash.merge@npm:4.6.2"
|
||||
@@ -16207,7 +16285,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"make-error@npm:^1.1.1":
|
||||
"make-error@npm:^1.1.1, make-error@npm:^1.3.6":
|
||||
version: 1.3.6
|
||||
resolution: "make-error@npm:1.3.6"
|
||||
checksum: 10c0/171e458d86854c6b3fc46610cfacf0b45149ba043782558c6875d9f42f222124384ad0b468c92e996d815a8a2003817a710c0a160e49c1c394626f76fa45396f
|
||||
@@ -16437,6 +16515,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minimatch@npm:^5.0.1":
|
||||
version: 5.1.6
|
||||
resolution: "minimatch@npm:5.1.6"
|
||||
dependencies:
|
||||
brace-expansion: "npm:^2.0.1"
|
||||
checksum: 10c0/3defdfd230914f22a8da203747c42ee3c405c39d4d37ffda284dac5e45b7e1f6c49aa8be606509002898e73091ff2a3bbfc59c2c6c71d4660609f63aa92f98e3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minimatch@npm:^8.0.2":
|
||||
version: 8.0.4
|
||||
resolution: "minimatch@npm:8.0.4"
|
||||
@@ -19418,13 +19505,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"secure-json-parse@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "secure-json-parse@npm:4.0.0"
|
||||
checksum: 10c0/1a298cf00e1de91e833cee5eb406d6e77fb2f7eca9bef3902047d49e7f5d3e6c21b5de61ff73466c831e716430bfe87d732a6e645a7dabb5f1e8a8e4d3e15eb4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"semver@npm:^5.6.0":
|
||||
version: 5.7.2
|
||||
resolution: "semver@npm:5.7.2"
|
||||
@@ -19443,7 +19523,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"semver@npm:^7.3.5, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.2, semver@npm:^7.6.3":
|
||||
"semver@npm:^7.3.5, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.2, semver@npm:^7.6.3, semver@npm:^7.7.1":
|
||||
version: 7.7.1
|
||||
resolution: "semver@npm:7.7.1"
|
||||
bin:
|
||||
@@ -20799,6 +20879,44 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ts-jest@npm:^29.3.2":
|
||||
version: 29.3.2
|
||||
resolution: "ts-jest@npm:29.3.2"
|
||||
dependencies:
|
||||
bs-logger: "npm:^0.2.6"
|
||||
ejs: "npm:^3.1.10"
|
||||
fast-json-stable-stringify: "npm:^2.1.0"
|
||||
jest-util: "npm:^29.0.0"
|
||||
json5: "npm:^2.2.3"
|
||||
lodash.memoize: "npm:^4.1.2"
|
||||
make-error: "npm:^1.3.6"
|
||||
semver: "npm:^7.7.1"
|
||||
type-fest: "npm:^4.39.1"
|
||||
yargs-parser: "npm:^21.1.1"
|
||||
peerDependencies:
|
||||
"@babel/core": ">=7.0.0-beta.0 <8"
|
||||
"@jest/transform": ^29.0.0
|
||||
"@jest/types": ^29.0.0
|
||||
babel-jest: ^29.0.0
|
||||
jest: ^29.0.0
|
||||
typescript: ">=4.3 <6"
|
||||
peerDependenciesMeta:
|
||||
"@babel/core":
|
||||
optional: true
|
||||
"@jest/transform":
|
||||
optional: true
|
||||
"@jest/types":
|
||||
optional: true
|
||||
babel-jest:
|
||||
optional: true
|
||||
esbuild:
|
||||
optional: true
|
||||
bin:
|
||||
ts-jest: cli.js
|
||||
checksum: 10c0/84762720dbef45c1644348d67d0dcb8b7ad6369a16628c4752aceeb47f0ccdad63ae14485048b641c20ce096337a160ab816881361ef5517325bac6a5b3756e0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ts-morph@npm:^25.0.1":
|
||||
version: 25.0.1
|
||||
resolution: "ts-morph@npm:25.0.1"
|
||||
@@ -21029,6 +21147,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"type-fest@npm:^4.39.1":
|
||||
version: 4.40.0
|
||||
resolution: "type-fest@npm:4.40.0"
|
||||
checksum: 10c0/b39d4da6f9a154e3db7e714cd05ccf56b53f4f0bbf74dd294cb6be4921b16ecca5cb00cb81b53ab621a31c8e8509c74b5101895ada47af9de368a317d24538a3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"type-is@npm:^1.6.16, type-is@npm:^1.6.18":
|
||||
version: 1.6.18
|
||||
resolution: "type-is@npm:1.6.18"
|
||||
|
||||
Reference in New Issue
Block a user