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 { env } from "@/env/server"
|
||||||
import { dt } from "@/lib/dt"
|
import { dt } from "@/lib/dt"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getEntries,
|
getEntries,
|
||||||
@@ -13,22 +14,16 @@ import {
|
|||||||
} from "@/utils/sitemap"
|
} from "@/utils/sitemap"
|
||||||
|
|
||||||
import { contentstackSync } from "./sync"
|
import { contentstackSync } from "./sync"
|
||||||
import {
|
|
||||||
generateSitemapCounter,
|
|
||||||
generateSitemapFailCounter,
|
|
||||||
generateSitemapSuccessCounter,
|
|
||||||
saveEntriesCounter,
|
|
||||||
saveSitemapDataCounter,
|
|
||||||
saveSyncTokenCounter,
|
|
||||||
} from "./telemetry"
|
|
||||||
import { mapEntriesToSitemapData, mergeEntries } from "./utils"
|
import { mapEntriesToSitemapData, mergeEntries } from "./utils"
|
||||||
|
|
||||||
export const dynamic = "force-dynamic"
|
export const dynamic = "force-dynamic"
|
||||||
|
|
||||||
export async function GET(request: NextRequest) {
|
export async function GET(request: NextRequest) {
|
||||||
|
const generateSitemapCounter = createCounter("sitemap", "generate")
|
||||||
|
const metricsGenerateSitemap = generateSitemapCounter.init()
|
||||||
try {
|
try {
|
||||||
generateSitemapCounter.add(1)
|
metricsGenerateSitemap.start()
|
||||||
console.info("sitemap.generate start")
|
|
||||||
const headersList = request.headers
|
const headersList = request.headers
|
||||||
const secret = headersList.get("x-sitemap-sync-secret")
|
const secret = headersList.get("x-sitemap-sync-secret")
|
||||||
|
|
||||||
@@ -44,49 +39,46 @@ export async function GET(request: NextRequest) {
|
|||||||
const responseData = await contentstackSync(syncToken)
|
const responseData = await contentstackSync(syncToken)
|
||||||
const mergedEntries = mergeEntries(currentEntries, responseData.entries)
|
const mergedEntries = mergeEntries(currentEntries, responseData.entries)
|
||||||
|
|
||||||
saveEntriesCounter.add(1, { entriesCount: mergedEntries.length })
|
const entriesSaveCounter = createCounter("sitemap", "entries.save")
|
||||||
console.info(
|
const metricsEntriesSave = entriesSaveCounter.init({
|
||||||
"sitemap.entries.save",
|
entriesCount: mergedEntries.length,
|
||||||
JSON.stringify({ entriesCount: mergedEntries.length })
|
})
|
||||||
)
|
|
||||||
|
metricsEntriesSave.start()
|
||||||
await saveEntries(mergedEntries)
|
await saveEntries(mergedEntries)
|
||||||
|
metricsEntriesSave.success()
|
||||||
|
|
||||||
const sitemapData = mapEntriesToSitemapData(mergedEntries)
|
const sitemapData = mapEntriesToSitemapData(mergedEntries)
|
||||||
const lastUpdated = dt().utc().format()
|
const lastUpdated = dt().utc().format()
|
||||||
saveSitemapDataCounter.add(1, {
|
|
||||||
sitemapEntriesCount: sitemapData.length,
|
const saveDataCounter = createCounter("sitemap", "data.save")
|
||||||
})
|
const metricsDataSave = saveDataCounter.init({
|
||||||
console.info(
|
|
||||||
"sitemap.data.save",
|
|
||||||
JSON.stringify({
|
|
||||||
sitemapEntriesCount: sitemapData.length,
|
sitemapEntriesCount: sitemapData.length,
|
||||||
lastUpdated,
|
lastUpdated,
|
||||||
})
|
})
|
||||||
)
|
metricsDataSave.start()
|
||||||
await saveSitemapData(sitemapData)
|
await saveSitemapData(sitemapData)
|
||||||
await saveLastUpdatedDate(lastUpdated)
|
await saveLastUpdatedDate(lastUpdated)
|
||||||
|
metricsDataSave.success()
|
||||||
|
|
||||||
if (syncToken !== responseData.syncToken) {
|
if (syncToken !== responseData.syncToken) {
|
||||||
saveSyncTokenCounter.add(1, {
|
const syncTokenSaveCounter = createCounter("sitemap", "syncToken.save")
|
||||||
|
const metricsSyncTokenSave = syncTokenSaveCounter.init({
|
||||||
syncToken: responseData.syncToken,
|
syncToken: responseData.syncToken,
|
||||||
})
|
})
|
||||||
console.info(
|
metricsSyncTokenSave.start()
|
||||||
"sitemap.synctoken.save",
|
|
||||||
JSON.stringify({ syncToken: responseData.syncToken })
|
|
||||||
)
|
|
||||||
await saveSyncToken(responseData.syncToken)
|
await saveSyncToken(responseData.syncToken)
|
||||||
|
metricsSyncTokenSave.success()
|
||||||
}
|
}
|
||||||
|
|
||||||
generateSitemapSuccessCounter.add(1)
|
metricsGenerateSitemap.success()
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
message: "Sitemap data generated and stored successfully!",
|
message: "Sitemap data generated and stored successfully!",
|
||||||
now: dt().utc().format(),
|
now: dt().utc().format(),
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage =
|
metricsGenerateSitemap.fail(error)
|
||||||
error instanceof Error ? error.message : JSON.stringify(error)
|
|
||||||
generateSitemapFailCounter.add(1, { error: errorMessage })
|
|
||||||
console.error("sitemap.generate.fail", errorMessage)
|
|
||||||
|
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,13 +1,7 @@
|
|||||||
import { Region, Stack } from "contentstack"
|
import { Region, Stack } from "contentstack"
|
||||||
|
|
||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import {
|
|
||||||
syncEntriesCounter,
|
|
||||||
syncEntriesFailCounter,
|
|
||||||
syncEntriesPaginationCounter,
|
|
||||||
syncEntriesSuccessCounter,
|
|
||||||
} from "./telemetry"
|
|
||||||
|
|
||||||
import type { SyncResponse } from "@/types/sitemap"
|
import type { SyncResponse } from "@/types/sitemap"
|
||||||
|
|
||||||
@@ -24,72 +18,50 @@ export async function contentstackSync(syncToken: string | null) {
|
|||||||
const entries = []
|
const entries = []
|
||||||
const syncOptions = syncToken ? { sync_token: syncToken } : { init: true }
|
const syncOptions = syncToken ? { sync_token: syncToken } : { init: true }
|
||||||
|
|
||||||
syncEntriesCounter.add(1, {
|
const entriesSyncCounter = createCounter("sitemap", "entries.sync")
|
||||||
|
const metricsEntriesSync = entriesSyncCounter.init({
|
||||||
environment,
|
environment,
|
||||||
...syncOptions,
|
...syncOptions,
|
||||||
})
|
})
|
||||||
console.info(
|
|
||||||
"sitemap.entries.sync start",
|
metricsEntriesSync.start()
|
||||||
JSON.stringify({
|
|
||||||
environment,
|
|
||||||
...syncOptions,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
try {
|
try {
|
||||||
let syncResponse: SyncResponse = await stack.sync(syncOptions)
|
let syncResponse: SyncResponse = await stack.sync(syncOptions)
|
||||||
|
|
||||||
entries.push(...syncResponse.items)
|
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
|
// Check if there is a pagination token, and fetch more data if needed
|
||||||
while (syncResponse.pagination_token && !syncResponse.sync_token) {
|
while (syncResponse.pagination_token && !syncResponse.sync_token) {
|
||||||
syncEntriesPaginationCounter.add(1, {
|
metricsEntriesSyncPagination.start({
|
||||||
environment,
|
|
||||||
paginationToken: syncResponse.pagination_token,
|
paginationToken: syncResponse.pagination_token,
|
||||||
})
|
})
|
||||||
console.info(
|
|
||||||
"sitemap.entries.sync.pagination start",
|
|
||||||
JSON.stringify({
|
|
||||||
environment,
|
|
||||||
paginationToken: syncResponse.pagination_token,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
syncResponse = await stack.sync({
|
syncResponse = await stack.sync({
|
||||||
pagination_token: syncResponse.pagination_token,
|
pagination_token: syncResponse.pagination_token,
|
||||||
})
|
})
|
||||||
|
|
||||||
entries.push(...syncResponse.items)
|
entries.push(...syncResponse.items)
|
||||||
|
|
||||||
syncEntriesPaginationCounter.add(1, {
|
metricsEntriesSyncPagination.success({
|
||||||
environment,
|
|
||||||
paginationToken: syncResponse.pagination_token,
|
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) {
|
if (syncResponse.sync_token) {
|
||||||
syncEntriesSuccessCounter.add(1, {
|
metricsEntriesSync.success({
|
||||||
environment,
|
|
||||||
...syncOptions,
|
|
||||||
newSyncToken: syncResponse.sync_token,
|
newSyncToken: syncResponse.sync_token,
|
||||||
entriesCount: entries.length,
|
entriesCount: entries.length,
|
||||||
})
|
})
|
||||||
console.info(
|
|
||||||
"sitemap.entries.sync success",
|
|
||||||
JSON.stringify({
|
|
||||||
environment,
|
|
||||||
...syncOptions,
|
|
||||||
newSyncToken: syncResponse.sync_token,
|
|
||||||
entriesCount: entries.length,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return {
|
return {
|
||||||
syncToken: syncResponse.sync_token,
|
syncToken: syncResponse.sync_token,
|
||||||
entries,
|
entries,
|
||||||
@@ -98,14 +70,7 @@ export async function contentstackSync(syncToken: string | null) {
|
|||||||
throw new Error("No sync token received, something went wrong")
|
throw new Error("No sync token received, something went wrong")
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage =
|
metricsEntriesSync.fail(error)
|
||||||
error instanceof Error ? error.message : JSON.stringify(error)
|
|
||||||
syncEntriesFailCounter.add(1, {
|
|
||||||
environment,
|
|
||||||
...syncOptions,
|
|
||||||
error: errorMessage,
|
|
||||||
})
|
|
||||||
console.error("sitemap.entries.sync error", errorMessage)
|
|
||||||
|
|
||||||
throw new Error("Failed to sync entries")
|
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 { Lang } from "@/constants/languages"
|
||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
|
|
||||||
import { removeTrailingSlash } from "@/utils/url"
|
import { removeTrailingSlash } from "@/utils/url"
|
||||||
|
|
||||||
import {
|
|
||||||
mergeEntriesCounter,
|
|
||||||
mergeEntriesSuccessCounter,
|
|
||||||
transformEntriesCounter,
|
|
||||||
transformEntriesSuccessCounter,
|
|
||||||
} from "./telemetry"
|
|
||||||
|
|
||||||
import type { SitemapEntry, SyncItem } from "@/types/sitemap"
|
import type { SitemapEntry, SyncItem } from "@/types/sitemap"
|
||||||
|
|
||||||
export function mergeEntries(
|
export function mergeEntries(
|
||||||
currentEntries: SyncItem[],
|
currentEntries: SyncItem[],
|
||||||
newEntries: SyncItem[]
|
newEntries: SyncItem[]
|
||||||
) {
|
) {
|
||||||
mergeEntriesCounter.add(1, {
|
const entriesMergeCounter = createCounter("sitemap", "entries.merge")
|
||||||
|
const metricsEntriesMerge = entriesMergeCounter.init({
|
||||||
currentEntriesCount: currentEntries.length,
|
currentEntriesCount: currentEntries.length,
|
||||||
newEntriesCount: newEntries.length,
|
newEntriesCount: newEntries.length,
|
||||||
})
|
})
|
||||||
console.info(
|
|
||||||
"sitemap.entries.merge start",
|
metricsEntriesMerge.start()
|
||||||
JSON.stringify({
|
|
||||||
currentEntriesCount: currentEntries.length,
|
|
||||||
newEntriesCount: newEntries.length,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
const entries = [...currentEntries]
|
const entries = [...currentEntries]
|
||||||
newEntries.forEach((entry) => {
|
newEntries.forEach((entry) => {
|
||||||
const index = entries.findIndex(
|
const index = entries.findIndex(
|
||||||
@@ -40,27 +31,20 @@ export function mergeEntries(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
mergeEntriesSuccessCounter.add(1, {
|
metricsEntriesMerge.success({
|
||||||
entriesCount: entries.length,
|
entriesCount: entries.length,
|
||||||
})
|
})
|
||||||
console.info(
|
|
||||||
"sitemap.entries.merge success",
|
|
||||||
JSON.stringify({
|
|
||||||
entriesCount: entries.length,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapEntriesToSitemapData(entries: SyncItem[]) {
|
export function mapEntriesToSitemapData(entries: SyncItem[]) {
|
||||||
transformEntriesCounter.add(1, { entriesCount: entries.length })
|
const entriesTransformCounter = createCounter("sitemap", "entries.transform")
|
||||||
console.info(
|
const metricsEntriesTransform = entriesTransformCounter.init({
|
||||||
"sitemap.entries.transform start",
|
|
||||||
JSON.stringify({
|
|
||||||
entriesCount: entries.length,
|
entriesCount: entries.length,
|
||||||
})
|
})
|
||||||
)
|
|
||||||
|
metricsEntriesTransform.start()
|
||||||
|
|
||||||
const filteredEntries = filterEntriesToSitemapEntries(entries)
|
const filteredEntries = filterEntriesToSitemapEntries(entries)
|
||||||
|
|
||||||
@@ -69,17 +53,10 @@ export function mapEntriesToSitemapData(entries: SyncItem[]) {
|
|||||||
.map(([_, entries]) => mapEntriesToSitemapEntry(entries))
|
.map(([_, entries]) => mapEntriesToSitemapEntry(entries))
|
||||||
.filter((entry): entry is SitemapEntry => !!entry)
|
.filter((entry): entry is SitemapEntry => !!entry)
|
||||||
|
|
||||||
transformEntriesSuccessCounter.add(1, {
|
metricsEntriesTransform.success({
|
||||||
entriesCount: entries.length,
|
entriesCount: entries.length,
|
||||||
sitemapEntriesCount: sitemapEntries.length,
|
sitemapEntriesCount: sitemapEntries.length,
|
||||||
})
|
})
|
||||||
console.info(
|
|
||||||
"sitemap.entries.transform success",
|
|
||||||
JSON.stringify({
|
|
||||||
entriesCount: entries.length,
|
|
||||||
sitemapEntriesCount: sitemapEntries.length,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return sitemapEntries
|
return sitemapEntries
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export default function HotelPin({
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
<Typography variant="Body/Paragraph/mdRegular">
|
||||||
|
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
||||||
<p>{isNotAvailable ? "—" : formatPrice(intl, hotelPrice, currency)}</p>
|
<p>{isNotAvailable ? "—" : formatPrice(intl, hotelPrice, currency)}</p>
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,15 +3,18 @@
|
|||||||
* https://jestjs.io/docs/configuration
|
* https://jestjs.io/docs/configuration
|
||||||
*/
|
*/
|
||||||
import nextJest from "next/jest.js"
|
import nextJest from "next/jest.js"
|
||||||
|
import { createJsWithTsEsmPreset } from "ts-jest"
|
||||||
|
|
||||||
import type { Config } from "jest"
|
import type { Config } from "jest"
|
||||||
|
|
||||||
|
const presetConfig = createJsWithTsEsmPreset()
|
||||||
|
|
||||||
const createJestConfig = nextJest({
|
const createJestConfig = nextJest({
|
||||||
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
|
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
|
||||||
dir: "./",
|
dir: "./",
|
||||||
})
|
})
|
||||||
|
|
||||||
const config: Config = {
|
const config = {
|
||||||
// All imported modules in your tests should be mocked automatically
|
// All imported modules in your tests should be mocked automatically
|
||||||
// automock: false,
|
// automock: false,
|
||||||
|
|
||||||
@@ -202,6 +205,8 @@ const config: Config = {
|
|||||||
|
|
||||||
// Whether to use watchman for file crawling
|
// Whether to use watchman for file crawling
|
||||||
// watchman: true,
|
// watchman: true,
|
||||||
}
|
|
||||||
|
...presetConfig,
|
||||||
|
} satisfies Config
|
||||||
|
|
||||||
export default createJestConfig(config)
|
export default createJestConfig(config)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import "@testing-library/jest-dom/jest-globals"
|
import "@testing-library/jest-dom/jest-globals"
|
||||||
import "@testing-library/jest-dom"
|
import "@testing-library/jest-dom"
|
||||||
|
|
||||||
|
import { jest } from "@jest/globals"
|
||||||
|
|
||||||
jest.mock("next/navigation", () => ({
|
jest.mock("next/navigation", () => ({
|
||||||
useRouter: jest.fn(),
|
useRouter: jest.fn(),
|
||||||
usePathname: jest.fn().mockReturnValue("/"),
|
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: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",
|
"test:setup": "yarn build && yarn start",
|
||||||
"preinstall": "/bin/sh -c \"export $(cat .env.local | grep -v '^#' | xargs)\"",
|
"preinstall": "/bin/sh -c \"export $(cat .env.local | grep -v '^#' | xargs)\"",
|
||||||
"test": "jest",
|
"test": "node --experimental-vm-modules $(yarn bin jest)",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "node --experimental-vm-modules $(yarn bin jest) --watch",
|
||||||
"ci:build": "yarn lint && yarn test && yarn build",
|
"ci:build": "yarn lint && yarn test && yarn build",
|
||||||
"clean": "rm -rf .next",
|
"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",
|
"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",
|
"embla-carousel-react": "^8.5.2",
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"fetch-retry": "^6.0.0",
|
"fetch-retry": "^6.0.0",
|
||||||
|
"flat": "^6.0.1",
|
||||||
"framer-motion": "^11.3.28",
|
"framer-motion": "^11.3.28",
|
||||||
"fuse.js": "^7.1.0",
|
"fuse.js": "^7.1.0",
|
||||||
"graphql": "^16.8.1",
|
"graphql": "^16.8.1",
|
||||||
@@ -88,6 +89,7 @@
|
|||||||
"ioredis": "^5.5.0",
|
"ioredis": "^5.5.0",
|
||||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||||
"libphonenumber-js": "^1.10.60",
|
"libphonenumber-js": "^1.10.60",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
"nanoid": "^5.0.9",
|
"nanoid": "^5.0.9",
|
||||||
"next": "^14.2.25",
|
"next": "^14.2.25",
|
||||||
"next-auth": "5.0.0-beta.19",
|
"next-auth": "5.0.0-beta.19",
|
||||||
@@ -101,7 +103,6 @@
|
|||||||
"react-international-phone": "^4.2.6",
|
"react-international-phone": "^4.2.6",
|
||||||
"react-intl": "^6.6.8",
|
"react-intl": "^6.6.8",
|
||||||
"react-to-print": "^3.0.2",
|
"react-to-print": "^3.0.2",
|
||||||
"secure-json-parse": "^4.0.0",
|
|
||||||
"server-only": "^0.0.1",
|
"server-only": "^0.0.1",
|
||||||
"slugify": "^1.6.6",
|
"slugify": "^1.6.6",
|
||||||
"sonner": "^1.7.0",
|
"sonner": "^1.7.0",
|
||||||
@@ -124,6 +125,7 @@
|
|||||||
"@types/adm-zip": "^0.5.7",
|
"@types/adm-zip": "^0.5.7",
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
"@types/json-stable-stringify-without-jsonify": "^1.0.2",
|
"@types/json-stable-stringify-without-jsonify": "^1.0.2",
|
||||||
|
"@types/lodash-es": "^4",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
@@ -148,6 +150,7 @@
|
|||||||
"react-material-symbols": "^4.4.0",
|
"react-material-symbols": "^4.4.0",
|
||||||
"schema-dts": "^1.1.2",
|
"schema-dts": "^1.1.2",
|
||||||
"start-server-and-test": "^2.0.3",
|
"start-server-and-test": "^2.0.3",
|
||||||
|
"ts-jest": "^29.3.2",
|
||||||
"ts-morph": "^25.0.1",
|
"ts-morph": "^25.0.1",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "5.4.5",
|
"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 * 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 { router, safeProtectedServiceProcedure } from "@/server/trpc"
|
||||||
|
|
||||||
import { isValidSession } from "@/utils/session"
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
addPackageInput,
|
addPackageInput,
|
||||||
cancelBookingInput,
|
cancelBookingInput,
|
||||||
@@ -18,75 +14,6 @@ import {
|
|||||||
} from "./input"
|
} from "./input"
|
||||||
import { bookingConfirmationSchema, createBookingSchema } from "./output"
|
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({
|
export const bookingMutationRouter = router({
|
||||||
create: safeProtectedServiceProcedure
|
create: safeProtectedServiceProcedure
|
||||||
.input(createBookingInput)
|
.input(createBookingInput)
|
||||||
@@ -95,22 +22,17 @@ export const bookingMutationRouter = router({
|
|||||||
const { language, ...inputWithoutLang } = input
|
const { language, ...inputWithoutLang } = input
|
||||||
const { hotelId, checkInDate, checkOutDate } = inputWithoutLang
|
const { hotelId, checkInDate, checkOutDate } = inputWithoutLang
|
||||||
|
|
||||||
const loggingAttributes = {
|
const createBookingCounter = createCounter("trpc.booking", "create")
|
||||||
|
const metricsCreateBooking = createBookingCounter.init({
|
||||||
membershipNumber: await getMembershipNumber(ctx.session),
|
membershipNumber: await getMembershipNumber(ctx.session),
|
||||||
checkInDate,
|
checkInDate,
|
||||||
checkOutDate,
|
checkOutDate,
|
||||||
hotelId,
|
hotelId,
|
||||||
language,
|
language,
|
||||||
}
|
|
||||||
|
|
||||||
createBookingCounter.add(1, loggingAttributes)
|
|
||||||
|
|
||||||
console.info(
|
|
||||||
"api.booking.create start",
|
|
||||||
JSON.stringify({
|
|
||||||
query: loggingAttributes,
|
|
||||||
})
|
})
|
||||||
)
|
|
||||||
|
metricsCreateBooking.start()
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
}
|
}
|
||||||
@@ -125,29 +47,9 @@ export const bookingMutationRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsCreateBooking.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const apiJson = sjson.safeParse(text)
|
const apiJson = await apiResponse.json()
|
||||||
if ("errors" in apiJson && apiJson.errors.length) {
|
if ("errors" in apiJson && apiJson.errors.length) {
|
||||||
const error = apiJson.errors[0]
|
const error = apiJson.errors[0]
|
||||||
return { error: true, cause: error.code } as const
|
return { error: true, cause: error.code } as const
|
||||||
@@ -160,45 +62,25 @@ export const bookingMutationRouter = router({
|
|||||||
|
|
||||||
const verifiedData = createBookingSchema.safeParse(apiJson)
|
const verifiedData = createBookingSchema.safeParse(apiJson)
|
||||||
if (!verifiedData.success) {
|
if (!verifiedData.success) {
|
||||||
createBookingFailCounter.add(1, {
|
metricsCreateBooking.validationError(verifiedData.error)
|
||||||
hotelId,
|
|
||||||
checkInDate,
|
|
||||||
checkOutDate,
|
|
||||||
error_type: "validation_error",
|
|
||||||
})
|
|
||||||
|
|
||||||
console.error(
|
|
||||||
"api.booking.create validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: loggingAttributes,
|
|
||||||
error: verifiedData.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
createBookingSuccessCounter.add(1, {
|
metricsCreateBooking.success()
|
||||||
hotelId,
|
|
||||||
checkInDate,
|
|
||||||
checkOutDate,
|
|
||||||
})
|
|
||||||
|
|
||||||
console.info(
|
|
||||||
"api.booking.create success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: loggingAttributes,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return verifiedData.data
|
return verifiedData.data
|
||||||
}),
|
}),
|
||||||
priceChange: safeProtectedServiceProcedure
|
priceChange: safeProtectedServiceProcedure
|
||||||
.input(priceChangeInput)
|
.input(priceChangeInput)
|
||||||
.mutation(async function ({ ctx, input }) {
|
.mutation(async function ({ ctx, input }) {
|
||||||
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
|
|
||||||
const { confirmationNumber } = input
|
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 = {
|
const headers = {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
@@ -213,46 +95,18 @@ export const bookingMutationRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsPriceChange.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiJson = await apiResponse.json()
|
const apiJson = await apiResponse.json()
|
||||||
const verifiedData = createBookingSchema.safeParse(apiJson)
|
const verifiedData = createBookingSchema.safeParse(apiJson)
|
||||||
if (!verifiedData.success) {
|
if (!verifiedData.success) {
|
||||||
priceChangeFailCounter.add(1, {
|
metricsPriceChange.validationError(verifiedData.error)
|
||||||
confirmationNumber,
|
|
||||||
error_type: "validation_error",
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"api.booking.priceChange validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { confirmationNumber },
|
|
||||||
error: verifiedData.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
priceChangeSuccessCounter.add(1, { confirmationNumber })
|
metricsPriceChange.success()
|
||||||
|
|
||||||
return verifiedData.data
|
return verifiedData.data
|
||||||
}),
|
}),
|
||||||
@@ -262,6 +116,14 @@ export const bookingMutationRouter = router({
|
|||||||
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
|
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
|
||||||
const { confirmationNumber, language } = input
|
const { confirmationNumber, language } = input
|
||||||
|
|
||||||
|
const cancelBookingCounter = createCounter("trpc.booking", "cancel")
|
||||||
|
const metricsCancelBooking = cancelBookingCounter.init({
|
||||||
|
confirmationNumber,
|
||||||
|
language,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsCancelBooking.start()
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
}
|
}
|
||||||
@@ -271,21 +133,6 @@ export const bookingMutationRouter = router({
|
|||||||
reason: "WEB-CANCEL",
|
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(
|
const apiResponse = await api.remove(
|
||||||
api.endpoints.v1.Booking.cancel(confirmationNumber),
|
api.endpoints.v1.Booking.cancel(confirmationNumber),
|
||||||
{
|
{
|
||||||
@@ -296,25 +143,7 @@ export const bookingMutationRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsCancelBooking.httpError(apiResponse)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,29 +152,11 @@ export const bookingMutationRouter = router({
|
|||||||
const verifiedData = createBookingSchema.safeParse(apiJson)
|
const verifiedData = createBookingSchema.safeParse(apiJson)
|
||||||
|
|
||||||
if (!verifiedData.success) {
|
if (!verifiedData.success) {
|
||||||
cancelBookingFailCounter.add(1, {
|
metricsCancelBooking.validationError(verifiedData.error)
|
||||||
confirmationNumber,
|
|
||||||
error_type: "validation_error",
|
|
||||||
})
|
|
||||||
|
|
||||||
console.error(
|
|
||||||
"api.booking.cancel validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: loggingAttributes,
|
|
||||||
error: verifiedData.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelBookingSuccessCounter.add(1, loggingAttributes)
|
metricsCancelBooking.success()
|
||||||
|
|
||||||
console.info(
|
|
||||||
"api.booking.cancel success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: loggingAttributes,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return verifiedData.data
|
return verifiedData.data
|
||||||
}),
|
}),
|
||||||
@@ -355,7 +166,10 @@ export const bookingMutationRouter = router({
|
|||||||
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
|
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
|
||||||
const { confirmationNumber, ...body } = input
|
const { confirmationNumber, ...body } = input
|
||||||
|
|
||||||
addPackageCounter.add(1, { confirmationNumber })
|
const addPackageCounter = createCounter("trpc.booking", "package.add")
|
||||||
|
const metricsAddPackage = addPackageCounter.init({ confirmationNumber })
|
||||||
|
|
||||||
|
metricsAddPackage.start()
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
@@ -370,46 +184,18 @@ export const bookingMutationRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsAddPackage.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiJson = await apiResponse.json()
|
const apiJson = await apiResponse.json()
|
||||||
const verifiedData = createBookingSchema.safeParse(apiJson)
|
const verifiedData = createBookingSchema.safeParse(apiJson)
|
||||||
if (!verifiedData.success) {
|
if (!verifiedData.success) {
|
||||||
addPackageFailCounter.add(1, {
|
metricsAddPackage.validationError(verifiedData.error)
|
||||||
confirmationNumber,
|
|
||||||
error_type: "validation_error",
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"api.booking.addPackage validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { confirmationNumber },
|
|
||||||
error: verifiedData.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
addPackageSuccessCounter.add(1, { confirmationNumber })
|
metricsAddPackage.success()
|
||||||
|
|
||||||
return verifiedData.data
|
return verifiedData.data
|
||||||
}),
|
}),
|
||||||
@@ -419,7 +205,12 @@ export const bookingMutationRouter = router({
|
|||||||
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
|
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
|
||||||
const { confirmationNumber, language, ...body } = input
|
const { confirmationNumber, language, ...body } = input
|
||||||
|
|
||||||
guaranteeBookingCounter.add(1, { confirmationNumber })
|
const guaranteeBookingCounter = createCounter("trpc.booking", "guarantee")
|
||||||
|
const metricsGuaranteeBooking = guaranteeBookingCounter.init({
|
||||||
|
confirmationNumber,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGuaranteeBooking.start()
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
@@ -435,46 +226,18 @@ export const bookingMutationRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsGuaranteeBooking.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiJson = await apiResponse.json()
|
const apiJson = await apiResponse.json()
|
||||||
const verifiedData = createBookingSchema.safeParse(apiJson)
|
const verifiedData = createBookingSchema.safeParse(apiJson)
|
||||||
if (!verifiedData.success) {
|
if (!verifiedData.success) {
|
||||||
guaranteeBookingFailCounter.add(1, {
|
metricsGuaranteeBooking.validationError(verifiedData.error)
|
||||||
confirmationNumber,
|
|
||||||
error_type: "validation_error",
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"api.booking.guarantee validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { confirmationNumber },
|
|
||||||
error: verifiedData.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
guaranteeBookingSuccessCounter.add(1, { confirmationNumber })
|
metricsGuaranteeBooking.success()
|
||||||
|
|
||||||
return verifiedData.data
|
return verifiedData.data
|
||||||
}),
|
}),
|
||||||
@@ -484,7 +247,12 @@ export const bookingMutationRouter = router({
|
|||||||
const accessToken = ctx.session?.token.access_token || ctx.serviceToken
|
const accessToken = ctx.session?.token.access_token || ctx.serviceToken
|
||||||
const { confirmationNumber, ...body } = input
|
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(
|
const apiResponse = await api.put(
|
||||||
api.endpoints.v1.Booking.booking(confirmationNumber),
|
api.endpoints.v1.Booking.booking(confirmationNumber),
|
||||||
@@ -497,25 +265,7 @@ export const bookingMutationRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsUpdateBooking.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -523,21 +273,11 @@ export const bookingMutationRouter = router({
|
|||||||
|
|
||||||
const verifiedData = bookingConfirmationSchema.safeParse(apiJson)
|
const verifiedData = bookingConfirmationSchema.safeParse(apiJson)
|
||||||
if (!verifiedData.success) {
|
if (!verifiedData.success) {
|
||||||
updateBookingFailCounter.add(1, {
|
metricsUpdateBooking.validationError(verifiedData.error)
|
||||||
confirmationNumber,
|
|
||||||
error_type: "validation_error",
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"api.booking.updateBooking validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { confirmationNumber },
|
|
||||||
error: verifiedData.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
updateBookingSuccessCounter.add(1, { confirmationNumber })
|
metricsUpdateBooking.success()
|
||||||
|
|
||||||
return verifiedData.data
|
return verifiedData.data
|
||||||
}),
|
}),
|
||||||
@@ -547,25 +287,21 @@ export const bookingMutationRouter = router({
|
|||||||
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
|
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
|
||||||
const { confirmationNumber, codes, language } = input
|
const { confirmationNumber, codes, language } = input
|
||||||
|
|
||||||
const headers = {
|
const removePackageCounter = createCounter(
|
||||||
Authorization: `Bearer ${accessToken}`,
|
"trpc.booking",
|
||||||
}
|
"package.remove"
|
||||||
|
)
|
||||||
const loggingAttributes = {
|
const metricsRemovePackage = removePackageCounter.init({
|
||||||
confirmationNumber,
|
confirmationNumber,
|
||||||
codes,
|
codes,
|
||||||
language,
|
language,
|
||||||
}
|
|
||||||
|
|
||||||
removePackageCounter.add(1, loggingAttributes)
|
|
||||||
|
|
||||||
console.info(
|
|
||||||
"api.booking.remove-package start",
|
|
||||||
JSON.stringify({
|
|
||||||
request: loggingAttributes,
|
|
||||||
headers,
|
|
||||||
})
|
})
|
||||||
)
|
|
||||||
|
metricsRemovePackage.start()
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
}
|
||||||
|
|
||||||
const apiResponse = await api.remove(
|
const apiResponse = await api.remove(
|
||||||
api.endpoints.v1.Booking.packages(confirmationNumber),
|
api.endpoints.v1.Booking.packages(confirmationNumber),
|
||||||
@@ -576,36 +312,11 @@ export const bookingMutationRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsRemovePackage.httpError(apiResponse)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
removePackageSuccessCounter.add(1, loggingAttributes)
|
metricsRemovePackage.success()
|
||||||
|
|
||||||
console.info(
|
|
||||||
"api.booking.remove-package success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: loggingAttributes,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { metrics } from "@opentelemetry/api"
|
|
||||||
|
|
||||||
import * as api from "@/lib/api"
|
import * as api from "@/lib/api"
|
||||||
import { badRequestError, serverErrorByStatus } from "@/server/errors/trpc"
|
import { badRequestError, serverErrorByStatus } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import {
|
import {
|
||||||
router,
|
router,
|
||||||
safeProtectedServiceProcedure,
|
safeProtectedServiceProcedure,
|
||||||
@@ -18,19 +17,6 @@ import {
|
|||||||
import { bookingConfirmationSchema, createBookingSchema } from "./output"
|
import { bookingConfirmationSchema, createBookingSchema } from "./output"
|
||||||
import { getBookedHotelRoom } from "./utils"
|
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({
|
export const bookingQueryRouter = router({
|
||||||
get: safeProtectedServiceProcedure
|
get: safeProtectedServiceProcedure
|
||||||
.input(getBookingInput)
|
.input(getBookingInput)
|
||||||
@@ -38,7 +24,10 @@ export const bookingQueryRouter = router({
|
|||||||
ctx,
|
ctx,
|
||||||
input: { confirmationNumber, lang: inputLang },
|
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
|
let lang = ctx.lang ?? inputLang
|
||||||
|
|
||||||
@@ -54,23 +43,7 @@ export const bookingQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const responseMessage = await apiResponse.text()
|
await metricsGetBooking.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
// If the booking is not found, return null.
|
// 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.
|
// 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 apiJson = await apiResponse.json()
|
||||||
const booking = bookingConfirmationSchema.safeParse(apiJson)
|
const booking = bookingConfirmationSchema.safeParse(apiJson)
|
||||||
if (!booking.success) {
|
if (!booking.success) {
|
||||||
getBookingFailCounter.add(1, {
|
metricsGetBooking.validationError(booking.error)
|
||||||
confirmationNumber,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(booking.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"api.booking.confirmation validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { confirmationNumber },
|
|
||||||
error: booking.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw badRequestError()
|
throw badRequestError()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,34 +71,17 @@ export const bookingQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!hotelData) {
|
if (!hotelData) {
|
||||||
getBookingFailCounter.add(1, {
|
metricsGetBooking.dataError(
|
||||||
confirmationNumber,
|
`Failed to get hotel data for ${booking.data.hotelId}`,
|
||||||
|
{
|
||||||
hotelId: booking.data.hotelId,
|
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",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
throw serverErrorByStatus(404)
|
throw serverErrorByStatus(404)
|
||||||
}
|
}
|
||||||
|
|
||||||
getBookingSuccessCounter.add(1, { confirmationNumber })
|
metricsGetBooking.success()
|
||||||
console.info(
|
|
||||||
"api.booking.confirmation success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { confirmationNumber },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...hotelData,
|
...hotelData,
|
||||||
@@ -152,7 +97,13 @@ export const bookingQueryRouter = router({
|
|||||||
input,
|
input,
|
||||||
}) {
|
}) {
|
||||||
const { confirmationNumber } = 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(
|
const apiResponse = await api.get(
|
||||||
api.endpoints.v1.Booking.status(confirmationNumber),
|
api.endpoints.v1.Booking.status(confirmationNumber),
|
||||||
@@ -164,52 +115,18 @@ export const bookingQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const responseMessage = await apiResponse.text()
|
await metricsGetBookingStatus.httpError(apiResponse)
|
||||||
getBookingStatusFailCounter.add(1, {
|
|
||||||
confirmationNumber,
|
|
||||||
error_type: "http_error",
|
|
||||||
error: responseMessage,
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"api.booking.status error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { confirmationNumber },
|
|
||||||
error: {
|
|
||||||
status: apiResponse.status,
|
|
||||||
statusText: apiResponse.statusText,
|
|
||||||
text: responseMessage,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
throw serverErrorByStatus(apiResponse.status, apiResponse)
|
throw serverErrorByStatus(apiResponse.status, apiResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiJson = await apiResponse.json()
|
const apiJson = await apiResponse.json()
|
||||||
const verifiedData = createBookingSchema.safeParse(apiJson)
|
const verifiedData = createBookingSchema.safeParse(apiJson)
|
||||||
if (!verifiedData.success) {
|
if (!verifiedData.success) {
|
||||||
getBookingStatusFailCounter.add(1, {
|
metricsGetBookingStatus.validationError(verifiedData.error)
|
||||||
confirmationNumber,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(verifiedData.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"api.booking.status validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { confirmationNumber },
|
|
||||||
error: verifiedData.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw badRequestError()
|
throw badRequestError()
|
||||||
}
|
}
|
||||||
|
|
||||||
getBookingStatusSuccessCounter.add(1, { confirmationNumber })
|
metricsGetBookingStatus.success()
|
||||||
console.info(
|
|
||||||
"api.booking.status success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { confirmationNumber },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return verifiedData.data
|
return verifiedData.data
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { metrics } from "@opentelemetry/api"
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
GetAccountPage,
|
GetAccountPage,
|
||||||
GetAccountPageRefs,
|
GetAccountPageRefs,
|
||||||
} from "@/lib/graphql/Query/AccountPage/AccountPage.graphql"
|
} from "@/lib/graphql/Query/AccountPage/AccountPage.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -26,36 +25,17 @@ import type {
|
|||||||
GetAccountPageSchema,
|
GetAccountPageSchema,
|
||||||
} from "@/types/trpc/routers/contentstack/accountPage"
|
} 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({
|
export const accountPageQueryRouter = router({
|
||||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
||||||
const { lang, uid } = ctx
|
const { lang, uid } = ctx
|
||||||
getAccountPageRefsCounter.add(1, { lang, uid })
|
|
||||||
console.info(
|
const getAccountPageRefsCounter = createCounter(
|
||||||
"contentstack.accountPage.refs start",
|
"trpc.contentstack",
|
||||||
JSON.stringify({ query: { lang, uid } })
|
"accountPage.get.refs"
|
||||||
)
|
)
|
||||||
|
const metricsRefs = getAccountPageRefsCounter.init({ lang, uid })
|
||||||
|
metricsRefs.start()
|
||||||
|
|
||||||
const refsResponse = await request<GetAccountPageRefsSchema>(
|
const refsResponse = await request<GetAccountPageRefsSchema>(
|
||||||
GetAccountPageRefs,
|
GetAccountPageRefs,
|
||||||
{
|
{
|
||||||
@@ -70,19 +50,7 @@ export const accountPageQueryRouter = router({
|
|||||||
|
|
||||||
if (!refsResponse.data) {
|
if (!refsResponse.data) {
|
||||||
const notFoundError = notFound(refsResponse)
|
const notFoundError = notFound(refsResponse)
|
||||||
getAccountPageRefsFailCounter.add(1, {
|
metricsRefs.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,19 +58,7 @@ export const accountPageQueryRouter = router({
|
|||||||
refsResponse.data
|
refsResponse.data
|
||||||
)
|
)
|
||||||
if (!validatedAccountPageRefs.success) {
|
if (!validatedAccountPageRefs.success) {
|
||||||
getAccountPageRefsFailCounter.add(1, {
|
metricsRefs.validationError(validatedAccountPageRefs.error)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,12 +68,16 @@ export const accountPageQueryRouter = router({
|
|||||||
generateTagsFromSystem(lang, connections),
|
generateTagsFromSystem(lang, connections),
|
||||||
generateTag(lang, validatedAccountPageRefs.data.account_page.system.uid),
|
generateTag(lang, validatedAccountPageRefs.data.account_page.system.uid),
|
||||||
].flat()
|
].flat()
|
||||||
getAccountPageRefsSuccessCounter.add(1, { lang, uid, tags })
|
|
||||||
getAccountPageCounter.add(1, { lang, uid })
|
metricsRefs.success()
|
||||||
console.info(
|
|
||||||
"contentstack.accountPage start",
|
const getAccountPageCounter = createCounter(
|
||||||
JSON.stringify({ query: { lang, uid } })
|
"trpc.contentstack",
|
||||||
|
"accountPage.get"
|
||||||
)
|
)
|
||||||
|
const metrics = getAccountPageCounter.init({ lang, uid })
|
||||||
|
metrics.start()
|
||||||
|
|
||||||
const response = await request<GetAccountPageSchema>(
|
const response = await request<GetAccountPageSchema>(
|
||||||
GetAccountPage,
|
GetAccountPage,
|
||||||
{
|
{
|
||||||
@@ -132,45 +92,18 @@ export const accountPageQueryRouter = router({
|
|||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
const notFoundError = notFound(response)
|
const notFoundError = notFound(response)
|
||||||
getAccountPageFailCounter.add(1, {
|
metrics.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
const validatedAccountPage = accountPageSchema.safeParse(response.data)
|
const validatedAccountPage = accountPageSchema.safeParse(response.data)
|
||||||
|
|
||||||
if (!validatedAccountPage.success) {
|
if (!validatedAccountPage.success) {
|
||||||
getAccountPageFailCounter.add(1, {
|
metrics.validationError(validatedAccountPage.error)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
getAccountPageSuccessCounter.add(1, { lang, uid })
|
|
||||||
console.info(
|
metrics.success()
|
||||||
"contentstack.accountPage success",
|
|
||||||
JSON.stringify({ query: { lang, uid } })
|
|
||||||
)
|
|
||||||
|
|
||||||
const parsedtitle = response.data.account_page.title
|
const parsedtitle = response.data.account_page.title
|
||||||
.replaceAll(" ", "")
|
.replaceAll(" ", "")
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
} from "@/lib/graphql/Query/SiteConfig.graphql"
|
} from "@/lib/graphql/Query/SiteConfig.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import { contentstackBaseProcedure, router } from "@/server/trpc"
|
import { contentstackBaseProcedure, router } from "@/server/trpc"
|
||||||
import { langInput } from "@/server/utils"
|
import { langInput } from "@/server/utils"
|
||||||
|
|
||||||
@@ -43,37 +44,6 @@ import {
|
|||||||
validateFooterConfigSchema,
|
validateFooterConfigSchema,
|
||||||
validateFooterRefConfigSchema,
|
validateFooterRefConfigSchema,
|
||||||
} from "./output"
|
} 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 {
|
import {
|
||||||
getAlertPhoneContactData,
|
getAlertPhoneContactData,
|
||||||
getConnections,
|
getConnections,
|
||||||
@@ -96,11 +66,14 @@ import type {
|
|||||||
import type { Lang } from "@/constants/languages"
|
import type { Lang } from "@/constants/languages"
|
||||||
|
|
||||||
const getContactConfig = cache(async (lang: Lang) => {
|
const getContactConfig = cache(async (lang: Lang) => {
|
||||||
getContactConfigCounter.add(1, { lang })
|
const getContactConfigCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.contactConfig start",
|
"contactConfig.get"
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
)
|
||||||
|
const metricsGetContactConfig = getContactConfigCounter.init({ lang })
|
||||||
|
|
||||||
|
metricsGetContactConfig.start()
|
||||||
|
|
||||||
const response = await request<ContactConfigData>(
|
const response = await request<ContactConfigData>(
|
||||||
GetContactConfig,
|
GetContactConfig,
|
||||||
{
|
{
|
||||||
@@ -114,46 +87,20 @@ const getContactConfig = cache(async (lang: Lang) => {
|
|||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
const notFoundError = notFound(response)
|
const notFoundError = notFound(response)
|
||||||
|
metricsGetContactConfig.noDataError()
|
||||||
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 } })
|
|
||||||
)
|
|
||||||
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
const validatedContactConfigConfig = validateContactConfigSchema.safeParse(
|
const verifiedData = validateContactConfigSchema.safeParse(response.data)
|
||||||
response.data
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!validatedContactConfigConfig.success) {
|
if (!verifiedData.success) {
|
||||||
getContactConfigFailCounter.add(1, {
|
metricsGetContactConfig.validationError(verifiedData.error)
|
||||||
lang,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedContactConfigConfig.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.contactConfig validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang },
|
|
||||||
error: validatedContactConfigConfig.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
getContactConfigSuccessCounter.add(1, { lang })
|
|
||||||
console.info(
|
metricsGetContactConfig.success()
|
||||||
"contentstack.contactConfig success",
|
|
||||||
JSON.stringify({ query: { lang } })
|
return verifiedData.data.all_contact_config.items[0]
|
||||||
)
|
|
||||||
return validatedContactConfigConfig.data.all_contact_config.items[0]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const baseQueryRouter = router({
|
export const baseQueryRouter = router({
|
||||||
@@ -162,11 +109,14 @@ export const baseQueryRouter = router({
|
|||||||
}),
|
}),
|
||||||
header: contentstackBaseProcedure.query(async ({ ctx }) => {
|
header: contentstackBaseProcedure.query(async ({ ctx }) => {
|
||||||
const { lang } = ctx
|
const { lang } = ctx
|
||||||
getHeaderRefsCounter.add(1, { lang })
|
|
||||||
console.info(
|
const getHeaderRefsCounter = createCounter(
|
||||||
"contentstack.header.refs start",
|
"trpc.contentstack",
|
||||||
JSON.stringify({ query: { lang } })
|
"header.get.refs"
|
||||||
)
|
)
|
||||||
|
const metricsGetHeaderRefs = getHeaderRefsCounter.init({ lang })
|
||||||
|
|
||||||
|
metricsGetHeaderRefs.start()
|
||||||
|
|
||||||
const responseRef = await request<GetHeaderRefs>(
|
const responseRef = await request<GetHeaderRefs>(
|
||||||
GetHeaderRef,
|
GetHeaderRef,
|
||||||
@@ -181,56 +131,25 @@ export const baseQueryRouter = router({
|
|||||||
|
|
||||||
if (!responseRef.data) {
|
if (!responseRef.data) {
|
||||||
const notFoundError = notFound(responseRef)
|
const notFoundError = notFound(responseRef)
|
||||||
getHeaderRefsFailCounter.add(1, {
|
metricsGetHeaderRefs.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
const validatedHeaderRefs = headerRefsSchema.safeParse(responseRef.data)
|
const validatedHeaderRefs = headerRefsSchema.safeParse(responseRef.data)
|
||||||
|
|
||||||
if (!validatedHeaderRefs.success) {
|
if (!validatedHeaderRefs.success) {
|
||||||
getHeaderRefsFailCounter.add(1, {
|
metricsGetHeaderRefs.validationError(validatedHeaderRefs.error)
|
||||||
lang,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedHeaderRefs.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.header.refs validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: {
|
|
||||||
lang,
|
|
||||||
},
|
|
||||||
error: validatedHeaderRefs.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getHeaderRefsSuccessCounter.add(1, { lang })
|
metricsGetHeaderRefs.success()
|
||||||
console.info(
|
|
||||||
"contentstack.header.refs success",
|
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
|
||||||
|
|
||||||
const connections = getConnections(validatedHeaderRefs.data)
|
const connections = getConnections(validatedHeaderRefs.data)
|
||||||
|
|
||||||
getHeaderCounter.add(1, { lang })
|
const getHeaderCounter = createCounter("trpc.contentstack", "header.get")
|
||||||
console.info(
|
const metricsGetHeader = getHeaderCounter.init({ lang })
|
||||||
"contentstack.header start",
|
|
||||||
JSON.stringify({ query: { lang } })
|
metricsGetHeader.start()
|
||||||
)
|
|
||||||
|
|
||||||
const tags = [
|
const tags = [
|
||||||
generateTagsFromSystem(lang, connections),
|
generateTagsFromSystem(lang, connections),
|
||||||
@@ -245,43 +164,18 @@ export const baseQueryRouter = router({
|
|||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
const notFoundError = notFound(response)
|
const notFoundError = notFound(response)
|
||||||
getHeaderFailCounter.add(1, {
|
metricsGetHeader.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
const validatedHeaderConfig = headerSchema.safeParse(response.data)
|
const validatedHeaderConfig = headerSchema.safeParse(response.data)
|
||||||
|
|
||||||
if (!validatedHeaderConfig.success) {
|
if (!validatedHeaderConfig.success) {
|
||||||
getHeaderFailCounter.add(1, {
|
metricsGetHeader.validationError(validatedHeaderConfig.error)
|
||||||
lang,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedHeaderConfig.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.header validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang },
|
|
||||||
error: validatedHeaderConfig.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
getHeaderSuccessCounter.add(1, { lang })
|
|
||||||
console.info(
|
metricsGetHeader.success()
|
||||||
"contentstack.header success",
|
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: validatedHeaderConfig.data.header,
|
data: validatedHeaderConfig.data.header,
|
||||||
@@ -290,11 +184,16 @@ export const baseQueryRouter = router({
|
|||||||
currentHeader: contentstackBaseProcedure
|
currentHeader: contentstackBaseProcedure
|
||||||
.input(langInput)
|
.input(langInput)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
getCurrentHeaderRefCounter.add(1, { lang: input.lang })
|
const getCurrentHeaderRefsCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.currentHeader.ref start",
|
"currentHeader.get.refs"
|
||||||
JSON.stringify({ query: { lang: input.lang } })
|
|
||||||
)
|
)
|
||||||
|
const metricsGetCurrentHeaderRefs = getCurrentHeaderRefsCounter.init({
|
||||||
|
lang: input.lang,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetCurrentHeaderRefs.start()
|
||||||
|
|
||||||
const responseRef = await request<CurrentHeaderRefDataRaw>(
|
const responseRef = await request<CurrentHeaderRefDataRaw>(
|
||||||
GetCurrentHeaderRef,
|
GetCurrentHeaderRef,
|
||||||
{
|
{
|
||||||
@@ -305,13 +204,16 @@ export const baseQueryRouter = router({
|
|||||||
ttl: "max",
|
ttl: "max",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
getCurrentHeaderCounter.add(1, { lang: input.lang })
|
|
||||||
console.info(
|
const getCurrentHeaderCounter = createCounter(
|
||||||
"contentstack.currentHeader start",
|
"trpc.contentstack",
|
||||||
JSON.stringify({
|
"currentHeader.get"
|
||||||
query: { lang: input.lang },
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetCurrentHeader = getCurrentHeaderCounter.init({
|
||||||
|
lang: input.lang,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetCurrentHeader.start()
|
||||||
|
|
||||||
const currentHeaderUID =
|
const currentHeaderUID =
|
||||||
responseRef.data.all_current_header.items[0].system.uid
|
responseRef.data.all_current_header.items[0].system.uid
|
||||||
@@ -327,20 +229,7 @@ export const baseQueryRouter = router({
|
|||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
const notFoundError = notFound(response)
|
const notFoundError = notFound(response)
|
||||||
getCurrentHeaderFailCounter.add(1, {
|
metricsGetCurrentHeader.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,40 +238,27 @@ export const baseQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!validatedHeaderConfig.success) {
|
if (!validatedHeaderConfig.success) {
|
||||||
getCurrentHeaderFailCounter.add(1, {
|
metricsGetCurrentHeader.validationError(validatedHeaderConfig.error)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
getCurrentHeaderSuccessCounter.add(1, { lang: input.lang })
|
|
||||||
console.info(
|
metricsGetCurrentHeader.success()
|
||||||
"contentstack.currentHeader success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang: input.lang },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return validatedHeaderConfig.data
|
return validatedHeaderConfig.data
|
||||||
}),
|
}),
|
||||||
currentFooter: contentstackBaseProcedure
|
currentFooter: contentstackBaseProcedure
|
||||||
.input(langInput)
|
.input(langInput)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
getCurrentFooterRefCounter.add(1, { lang: input.lang })
|
const getCurrentFooterRefsCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.currentFooter.ref start",
|
"currentFooter.get.refs"
|
||||||
JSON.stringify({ query: { lang: input.lang } })
|
|
||||||
)
|
)
|
||||||
|
const metricsGetCurrentFooterRefs = getCurrentFooterRefsCounter.init({
|
||||||
|
lang: input.lang,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetCurrentFooterRefs.start()
|
||||||
|
|
||||||
const responseRef = await request<CurrentFooterRefDataRaw>(
|
const responseRef = await request<CurrentFooterRefDataRaw>(
|
||||||
GetCurrentFooterRef,
|
GetCurrentFooterRef,
|
||||||
{
|
{
|
||||||
@@ -393,16 +269,17 @@ export const baseQueryRouter = router({
|
|||||||
ttl: "max",
|
ttl: "max",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// There's currently no error handling/validation for the responseRef, should it be added?
|
|
||||||
getCurrentFooterCounter.add(1, { lang: input.lang })
|
const getCurrentFooterCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.currentFooter start",
|
"currentFooter.get"
|
||||||
JSON.stringify({
|
|
||||||
query: {
|
|
||||||
lang: input.lang,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetCurrentFooter = getCurrentFooterCounter.init({
|
||||||
|
lang: input.lang,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetCurrentFooter.start()
|
||||||
|
|
||||||
const currentFooterUID =
|
const currentFooterUID =
|
||||||
responseRef.data.all_current_footer.items[0].system.uid
|
responseRef.data.all_current_footer.items[0].system.uid
|
||||||
|
|
||||||
@@ -419,20 +296,7 @@ export const baseQueryRouter = router({
|
|||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
const notFoundError = notFound(response)
|
const notFoundError = notFound(response)
|
||||||
getCurrentFooterFailCounter.add(1, {
|
metricsGetCurrentFooter.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,34 +304,27 @@ export const baseQueryRouter = router({
|
|||||||
validateCurrentFooterConfigSchema.safeParse(response.data)
|
validateCurrentFooterConfigSchema.safeParse(response.data)
|
||||||
|
|
||||||
if (!validatedCurrentFooterConfig.success) {
|
if (!validatedCurrentFooterConfig.success) {
|
||||||
getFooterFailCounter.add(1, {
|
metricsGetCurrentFooter.validationError(
|
||||||
lang: input.lang,
|
validatedCurrentFooterConfig.error
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedCurrentFooterConfig.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.currentFooter validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang: input.lang },
|
|
||||||
error: validatedCurrentFooterConfig.error,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
getCurrentFooterSuccessCounter.add(1, { lang: input.lang })
|
|
||||||
console.info(
|
metricsGetCurrentFooter.success()
|
||||||
"contentstack.currentFooter success",
|
|
||||||
JSON.stringify({ query: { lang: input.lang } })
|
|
||||||
)
|
|
||||||
return validatedCurrentFooterConfig.data.all_current_footer.items[0]
|
return validatedCurrentFooterConfig.data.all_current_footer.items[0]
|
||||||
}),
|
}),
|
||||||
footer: contentstackBaseProcedure.query(async ({ ctx }) => {
|
footer: contentstackBaseProcedure.query(async ({ ctx }) => {
|
||||||
const { lang } = ctx
|
const { lang } = ctx
|
||||||
getFooterRefCounter.add(1, { lang })
|
|
||||||
console.info(
|
const getFooterRefsCounter = createCounter(
|
||||||
"contentstack.footer.ref start",
|
"trpc.contentstack",
|
||||||
JSON.stringify({ query: { lang } })
|
"footer.get.refs"
|
||||||
)
|
)
|
||||||
|
const metricsGetFooterRefs = getFooterRefsCounter.init({ lang })
|
||||||
|
|
||||||
|
metricsGetFooterRefs.start()
|
||||||
|
|
||||||
const responseRef = await request<FooterRefDataRaw>(
|
const responseRef = await request<FooterRefDataRaw>(
|
||||||
GetFooterRef,
|
GetFooterRef,
|
||||||
{
|
{
|
||||||
@@ -481,20 +338,7 @@ export const baseQueryRouter = router({
|
|||||||
|
|
||||||
if (!responseRef.data) {
|
if (!responseRef.data) {
|
||||||
const notFoundError = notFound(responseRef)
|
const notFoundError = notFound(responseRef)
|
||||||
getFooterRefFailCounter.add(1, {
|
metricsGetFooterRefs.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,41 +347,20 @@ export const baseQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!validatedFooterRefs.success) {
|
if (!validatedFooterRefs.success) {
|
||||||
getFooterRefFailCounter.add(1, {
|
metricsGetFooterRefs.validationError(validatedFooterRefs.error)
|
||||||
lang,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedFooterRefs.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.footer.refs validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: {
|
|
||||||
lang,
|
|
||||||
},
|
|
||||||
error: validatedFooterRefs.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getFooterRefSuccessCounter.add(1, { lang })
|
metricsGetFooterRefs.success()
|
||||||
console.info(
|
|
||||||
"contentstack.footer.refs success",
|
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
|
||||||
|
|
||||||
const connections = getFooterConnections(validatedFooterRefs.data)
|
const connections = getFooterConnections(validatedFooterRefs.data)
|
||||||
const footerUID = responseRef.data.all_footer.items[0].system.uid
|
const footerUID = responseRef.data.all_footer.items[0].system.uid
|
||||||
|
|
||||||
getFooterCounter.add(1, { lang: lang })
|
const getFooterCounter = createCounter("trpc.contentstack", "footer.get")
|
||||||
console.info(
|
const metricsGetFooter = getFooterCounter.init({ lang })
|
||||||
"contentstack.footer start",
|
|
||||||
JSON.stringify({
|
metricsGetFooter.start()
|
||||||
query: {
|
|
||||||
lang,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
const tags = [
|
const tags = [
|
||||||
generateTags(lang, connections),
|
generateTags(lang, connections),
|
||||||
generateTag(lang, footerUID),
|
generateTag(lang, footerUID),
|
||||||
@@ -556,20 +379,7 @@ export const baseQueryRouter = router({
|
|||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
const notFoundError = notFound(response)
|
const notFoundError = notFound(response)
|
||||||
getFooterFailCounter.add(1, {
|
metricsGetFooter.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -578,25 +388,11 @@ export const baseQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!validatedFooterConfig.success) {
|
if (!validatedFooterConfig.success) {
|
||||||
getFooterFailCounter.add(1, {
|
metricsGetFooter.validationError(validatedFooterConfig.error)
|
||||||
lang,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedFooterConfig.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.footer validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang: lang },
|
|
||||||
error: validatedFooterConfig.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
getFooterSuccessCounter.add(1, { lang })
|
|
||||||
console.info(
|
metricsGetFooter.success()
|
||||||
"contentstack.footer success",
|
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
|
||||||
|
|
||||||
return validatedFooterConfig.data
|
return validatedFooterConfig.data
|
||||||
}),
|
}),
|
||||||
@@ -605,11 +401,14 @@ export const baseQueryRouter = router({
|
|||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
const lang = input.lang ?? ctx.lang
|
const lang = input.lang ?? ctx.lang
|
||||||
|
|
||||||
getSiteConfigRefCounter.add(1, { lang })
|
const getSiteConfigRefsCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.siteConfig.ref start",
|
"siteConfig.get.refs"
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
)
|
||||||
|
const metricsGetSiteConfigRefs = getSiteConfigRefsCounter.init({ lang })
|
||||||
|
|
||||||
|
metricsGetSiteConfigRefs.start()
|
||||||
|
|
||||||
const responseRef = await request<GetSiteConfigRefData>(
|
const responseRef = await request<GetSiteConfigRefData>(
|
||||||
GetSiteConfigRef,
|
GetSiteConfigRef,
|
||||||
{
|
{
|
||||||
@@ -623,20 +422,7 @@ export const baseQueryRouter = router({
|
|||||||
|
|
||||||
if (!responseRef.data) {
|
if (!responseRef.data) {
|
||||||
const notFoundError = notFound(responseRef)
|
const notFoundError = notFound(responseRef)
|
||||||
getSiteConfigRefFailCounter.add(1, {
|
metricsGetSiteConfigRefs.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -645,20 +431,7 @@ export const baseQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!validatedSiteConfigRef.success) {
|
if (!validatedSiteConfigRef.success) {
|
||||||
getSiteConfigRefFailCounter.add(1, {
|
metricsGetSiteConfigRefs.validationError(validatedSiteConfigRef.error)
|
||||||
lang,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedSiteConfigRef.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.siteConfig.refs validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: {
|
|
||||||
lang,
|
|
||||||
},
|
|
||||||
error: validatedSiteConfigRef.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,17 +443,16 @@ export const baseQueryRouter = router({
|
|||||||
generateTag(lang, siteConfigUid),
|
generateTag(lang, siteConfigUid),
|
||||||
].flat()
|
].flat()
|
||||||
|
|
||||||
getSiteConfigRefSuccessCounter.add(1, { lang })
|
metricsGetSiteConfigRefs.success()
|
||||||
console.info(
|
|
||||||
"contentstack.siteConfig.refs success",
|
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
|
||||||
|
|
||||||
getSiteConfigCounter.add(1, { lang })
|
const getSiteConfigCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.siteConfig start",
|
"siteConfig.get"
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
)
|
||||||
|
const metricsGetSiteConfig = getSiteConfigCounter.init({ lang })
|
||||||
|
|
||||||
|
metricsGetSiteConfig.start()
|
||||||
|
|
||||||
const [siteConfigResponse, contactConfig] = await Promise.all([
|
const [siteConfigResponse, contactConfig] = await Promise.all([
|
||||||
request<GetSiteConfigData>(
|
request<GetSiteConfigData>(
|
||||||
GetSiteConfig,
|
GetSiteConfig,
|
||||||
@@ -697,21 +469,7 @@ export const baseQueryRouter = router({
|
|||||||
|
|
||||||
if (!siteConfigResponse.data) {
|
if (!siteConfigResponse.data) {
|
||||||
const notFoundError = notFound(siteConfigResponse)
|
const notFoundError = notFound(siteConfigResponse)
|
||||||
|
metricsGetSiteConfig.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -720,26 +478,11 @@ export const baseQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!validatedSiteConfig.success) {
|
if (!validatedSiteConfig.success) {
|
||||||
getSiteConfigFailCounter.add(1, {
|
metricsGetSiteConfig.validationError(validatedSiteConfig.error)
|
||||||
lang,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedSiteConfig.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.siteConfig validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang },
|
|
||||||
error: validatedSiteConfig.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getSiteConfigSuccessCounter.add(1, { lang })
|
metricsGetSiteConfig.success()
|
||||||
console.info(
|
|
||||||
"contentstack.siteConfig success",
|
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
|
||||||
|
|
||||||
const { sitewideAlert } = validatedSiteConfig.data
|
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 { cache } from "react"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -35,6 +34,7 @@ import {
|
|||||||
} from "@/lib/graphql/Query/Breadcrumbs/LoyaltyPage.graphql"
|
} from "@/lib/graphql/Query/Breadcrumbs/LoyaltyPage.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
||||||
|
|
||||||
import { generateRefsResponseTag } from "@/utils/generateTag"
|
import { generateRefsResponseTag } from "@/utils/generateTag"
|
||||||
@@ -49,28 +49,6 @@ import type {
|
|||||||
} from "@/types/trpc/routers/contentstack/breadcrumbs"
|
} from "@/types/trpc/routers/contentstack/breadcrumbs"
|
||||||
import type { Lang } from "@/constants/languages"
|
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> {
|
interface BreadcrumbsPageData<T> {
|
||||||
dataKey: keyof T
|
dataKey: keyof T
|
||||||
refQuery: string
|
refQuery: string
|
||||||
@@ -81,11 +59,17 @@ const getBreadcrumbs = cache(async function fetchMemoizedBreadcrumbs<T>(
|
|||||||
{ dataKey, refQuery, query }: BreadcrumbsPageData<T>,
|
{ dataKey, refQuery, query }: BreadcrumbsPageData<T>,
|
||||||
{ uid, lang }: { uid: string; lang: Lang }
|
{ uid, lang }: { uid: string; lang: Lang }
|
||||||
) {
|
) {
|
||||||
getBreadcrumbsRefsCounter.add(1, { lang, uid })
|
const getBreadcrumbsRefsCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.breadcrumbs refs get start",
|
"breadcrumbs.get.refs"
|
||||||
JSON.stringify({ query: { lang, uid } })
|
|
||||||
)
|
)
|
||||||
|
const metricsGetBreadcrumbsRefs = getBreadcrumbsRefsCounter.init({
|
||||||
|
lang,
|
||||||
|
uid,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetBreadcrumbsRefs.start()
|
||||||
|
|
||||||
const refsResponse = await request<{ [K in keyof T]: BreadcrumbsRefsSchema }>(
|
const refsResponse = await request<{ [K in keyof T]: BreadcrumbsRefsSchema }>(
|
||||||
refQuery,
|
refQuery,
|
||||||
{ locale: lang, uid },
|
{ locale: lang, uid },
|
||||||
@@ -100,32 +84,25 @@ const getBreadcrumbs = cache(async function fetchMemoizedBreadcrumbs<T>(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!validatedRefsData.success) {
|
if (!validatedRefsData.success) {
|
||||||
getBreadcrumbsRefsFailCounter.add(1, {
|
metricsGetBreadcrumbsRefs.validationError(validatedRefsData.error)
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedRefsData.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.breadcrumbs refs validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
error: validatedRefsData.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
getBreadcrumbsRefsSuccessCounter.add(1, { lang, uid })
|
metricsGetBreadcrumbsRefs.success()
|
||||||
console.info(
|
|
||||||
"contentstack.breadcrumbs refs get success",
|
|
||||||
JSON.stringify({ query: { lang, uid } })
|
|
||||||
)
|
|
||||||
|
|
||||||
const tags = getTags(validatedRefsData.data, lang)
|
const tags = getTags(validatedRefsData.data, lang)
|
||||||
|
|
||||||
getBreadcrumbsCounter.add(1, { lang, uid })
|
const getBreadcrumbsCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.breadcrumbs get start",
|
"breadcrumbs.get"
|
||||||
JSON.stringify({ query: { lang, uid } })
|
|
||||||
)
|
)
|
||||||
|
const metricsGetBreadcrumbs = getBreadcrumbsCounter.init({
|
||||||
|
lang,
|
||||||
|
uid,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetBreadcrumbs.start()
|
||||||
|
|
||||||
const response = await request<T>(
|
const response = await request<T>(
|
||||||
query,
|
query,
|
||||||
{ locale: lang, uid },
|
{ locale: lang, uid },
|
||||||
@@ -137,19 +114,7 @@ const getBreadcrumbs = cache(async function fetchMemoizedBreadcrumbs<T>(
|
|||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
const notFoundError = notFound(response)
|
const notFoundError = notFound(response)
|
||||||
getBreadcrumbsFailCounter.add(1, {
|
metricsGetBreadcrumbs.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,24 +123,11 @@ const getBreadcrumbs = cache(async function fetchMemoizedBreadcrumbs<T>(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!validatedBreadcrumbs.success) {
|
if (!validatedBreadcrumbs.success) {
|
||||||
getBreadcrumbsFailCounter.add(1, {
|
metricsGetBreadcrumbs.validationError(validatedBreadcrumbs.error)
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedBreadcrumbs.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.breadcrumbs validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
error: validatedBreadcrumbs.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
getBreadcrumbsSuccessCounter.add(1, { lang, uid })
|
metricsGetBreadcrumbs.success()
|
||||||
console.info(
|
|
||||||
"contentstack.breadcrumbs get success",
|
|
||||||
JSON.stringify({ query: { lang, uid } })
|
|
||||||
)
|
|
||||||
|
|
||||||
return validatedBreadcrumbs.data
|
return validatedBreadcrumbs.data
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import {
|
|||||||
cardsGridSchema,
|
cardsGridSchema,
|
||||||
} from "../schemas/blocks/cardsGrid"
|
} from "../schemas/blocks/cardsGrid"
|
||||||
import {
|
import {
|
||||||
dynamicContentSchema as blockDynamicContentSchema,
|
|
||||||
dynamicContentRefsSchema,
|
dynamicContentRefsSchema,
|
||||||
|
dynamicContentSchema as blockDynamicContentSchema,
|
||||||
} from "../schemas/blocks/dynamicContent"
|
} from "../schemas/blocks/dynamicContent"
|
||||||
import {
|
import {
|
||||||
shortcutsRefsSchema,
|
shortcutsRefsSchema,
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { GetCollectionPage } from "@/lib/graphql/Query/CollectionPage/CollectionPage.graphql"
|
import { GetCollectionPage } from "@/lib/graphql/Query/CollectionPage/CollectionPage.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
||||||
|
|
||||||
import { collectionPageSchema } from "./output"
|
import { collectionPageSchema } from "./output"
|
||||||
import {
|
import {
|
||||||
fetchCollectionPageRefs,
|
fetchCollectionPageRefs,
|
||||||
generatePageTags,
|
generatePageTags,
|
||||||
getCollectionPageCounter,
|
|
||||||
validateCollectionPageRefs,
|
validateCollectionPageRefs,
|
||||||
} from "./utils"
|
} from "./utils"
|
||||||
|
|
||||||
@@ -32,13 +32,16 @@ export const collectionPageQueryRouter = router({
|
|||||||
}
|
}
|
||||||
const tags = generatePageTags(collectionPageRefs, lang)
|
const tags = generatePageTags(collectionPageRefs, lang)
|
||||||
|
|
||||||
getCollectionPageCounter.add(1, { lang, uid })
|
const getCollectionPageCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.collectionPage start",
|
"collectionPage.get"
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetCollectionPage = getCollectionPageCounter.init({
|
||||||
|
lang,
|
||||||
|
uid,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetCollectionPage.start()
|
||||||
|
|
||||||
const response = await request<GetCollectionPageSchema>(
|
const response = await request<GetCollectionPageSchema>(
|
||||||
GetCollectionPage,
|
GetCollectionPage,
|
||||||
@@ -51,13 +54,12 @@ export const collectionPageQueryRouter = router({
|
|||||||
|
|
||||||
const collectionPage = collectionPageSchema.safeParse(response.data)
|
const collectionPage = collectionPageSchema.safeParse(response.data)
|
||||||
if (!collectionPage.success) {
|
if (!collectionPage.success) {
|
||||||
console.error(
|
metricsGetCollectionPage.validationError(collectionPage.error)
|
||||||
`Failed to validate CollectionPage Data - (lang: ${lang}, uid: ${uid})`
|
|
||||||
)
|
|
||||||
console.error(collectionPage.error?.format())
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
metricsGetCollectionPage.success()
|
||||||
|
|
||||||
const tracking: TrackingSDKPageData = {
|
const tracking: TrackingSDKPageData = {
|
||||||
pageId: collectionPage.data.collection_page.system.uid,
|
pageId: collectionPage.data.collection_page.system.uid,
|
||||||
domainLanguage: lang,
|
domainLanguage: lang,
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { metrics } from "@opentelemetry/api"
|
|
||||||
|
|
||||||
import { GetCollectionPageRefs } from "@/lib/graphql/Query/CollectionPage/CollectionPage.graphql"
|
import { GetCollectionPageRefs } from "@/lib/graphql/Query/CollectionPage/CollectionPage.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
|
|
||||||
import { getCacheClient } from "@/services/dataCache"
|
import { getCacheClient } from "@/services/dataCache"
|
||||||
import {
|
import {
|
||||||
@@ -21,31 +20,17 @@ import type {
|
|||||||
} from "@/types/trpc/routers/contentstack/collectionPage"
|
} from "@/types/trpc/routers/contentstack/collectionPage"
|
||||||
import type { Lang } from "@/constants/languages"
|
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) {
|
export async function fetchCollectionPageRefs(lang: Lang, uid: string) {
|
||||||
getCollectionPageRefsCounter.add(1, { lang, uid })
|
const getCollectionPageRefsCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.collectionPage.refs start",
|
"collectionPage.get.refs"
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetCollectionPageRefs = getCollectionPageRefsCounter.init({
|
||||||
|
lang,
|
||||||
|
uid,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetCollectionPageRefs.start()
|
||||||
|
|
||||||
const cacheClient = await getCacheClient()
|
const cacheClient = await getCacheClient()
|
||||||
const cacheKey = generateRefsResponseTag(lang, uid)
|
const cacheKey = generateRefsResponseTag(lang, uid)
|
||||||
@@ -61,24 +46,7 @@ export async function fetchCollectionPageRefs(lang: Lang, uid: string) {
|
|||||||
|
|
||||||
if (!refsResponse.data) {
|
if (!refsResponse.data) {
|
||||||
const notFoundError = notFound(refsResponse)
|
const notFoundError = notFound(refsResponse)
|
||||||
getCollectionPageRefsFailCounter.add(1, {
|
metricsGetCollectionPageRefs.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,30 +58,22 @@ export function validateCollectionPageRefs(
|
|||||||
lang: Lang,
|
lang: Lang,
|
||||||
uid: string
|
uid: string
|
||||||
) {
|
) {
|
||||||
const validatedData = collectionPageRefsSchema.safeParse(data)
|
const getCollectionPageRefsCounter = createCounter(
|
||||||
if (!validatedData.success) {
|
"trpc.contentstack",
|
||||||
getCollectionPageRefsFailCounter.add(1, {
|
"collectionPage.get.refs"
|
||||||
|
)
|
||||||
|
const metricsGetCollectionPageRefs = getCollectionPageRefsCounter.init({
|
||||||
lang,
|
lang,
|
||||||
uid,
|
uid,
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedData.error),
|
|
||||||
})
|
})
|
||||||
console.error(
|
|
||||||
"contentstack.collectionPage.refs validation error",
|
const validatedData = collectionPageRefsSchema.safeParse(data)
|
||||||
JSON.stringify({
|
if (!validatedData.success) {
|
||||||
query: { lang, uid },
|
metricsGetCollectionPageRefs.validationError(validatedData.error)
|
||||||
error: validatedData.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
getCollectionPageRefsSuccessCounter.add(1, { lang, uid })
|
|
||||||
console.info(
|
metricsGetCollectionPageRefs.success()
|
||||||
"contentstack.collectionPage.refs success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return validatedData.data
|
return validatedData.data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
GetContentPageBlocksBatch1,
|
GetContentPageBlocksBatch1,
|
||||||
GetContentPageBlocksBatch2,
|
GetContentPageBlocksBatch2,
|
||||||
} from "@/lib/graphql/Query/ContentPage/ContentPage.graphql"
|
} from "@/lib/graphql/Query/ContentPage/ContentPage.graphql"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
||||||
|
|
||||||
import { contentPageSchema } from "./output"
|
import { contentPageSchema } from "./output"
|
||||||
@@ -12,7 +13,6 @@ import {
|
|||||||
createPageType,
|
createPageType,
|
||||||
fetchContentPageRefs,
|
fetchContentPageRefs,
|
||||||
generatePageTags,
|
generatePageTags,
|
||||||
getContentPageCounter,
|
|
||||||
} from "./utils"
|
} from "./utils"
|
||||||
|
|
||||||
import type { TrackingSDKPageData } from "@/types/components/tracking"
|
import type { TrackingSDKPageData } from "@/types/components/tracking"
|
||||||
@@ -30,13 +30,16 @@ export const contentPageQueryRouter = router({
|
|||||||
|
|
||||||
const tags = generatePageTags(contentPageRefs, lang)
|
const tags = generatePageTags(contentPageRefs, lang)
|
||||||
|
|
||||||
getContentPageCounter.add(1, { lang, uid })
|
const getContentPageCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.contentPage start",
|
"contentPage.get"
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetContentPage = getContentPageCounter.init({
|
||||||
|
lang,
|
||||||
|
uid,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetContentPage.start()
|
||||||
|
|
||||||
const contentPageRequest = await batchRequest<GetContentPageSchema>([
|
const contentPageRequest = await batchRequest<GetContentPageSchema>([
|
||||||
{
|
{
|
||||||
@@ -69,13 +72,12 @@ export const contentPageQueryRouter = router({
|
|||||||
|
|
||||||
const contentPage = contentPageSchema.safeParse(contentPageRequest.data)
|
const contentPage = contentPageSchema.safeParse(contentPageRequest.data)
|
||||||
if (!contentPage.success) {
|
if (!contentPage.success) {
|
||||||
console.error(
|
metricsGetContentPage.validationError(contentPage.error)
|
||||||
`Failed to validate Contentpage Data - (lang: ${lang}, uid: ${uid})`
|
|
||||||
)
|
|
||||||
console.error(contentPage.error?.format())
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
metricsGetContentPage.success()
|
||||||
|
|
||||||
const tracking: TrackingSDKPageData = {
|
const tracking: TrackingSDKPageData = {
|
||||||
pageId: contentPage.data.content_page.system.uid,
|
pageId: contentPage.data.content_page.system.uid,
|
||||||
domainLanguage: lang,
|
domainLanguage: lang,
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { metrics } from "@opentelemetry/api"
|
|
||||||
|
|
||||||
import { batchRequest } from "@/lib/graphql/batchRequest"
|
import { batchRequest } from "@/lib/graphql/batchRequest"
|
||||||
import {
|
import {
|
||||||
GetContentPageBlocksRefs,
|
GetContentPageBlocksRefs,
|
||||||
GetContentPageRefs,
|
GetContentPageRefs,
|
||||||
} from "@/lib/graphql/Query/ContentPage/ContentPage.graphql"
|
} from "@/lib/graphql/Query/ContentPage/ContentPage.graphql"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
generateRefsResponseTag,
|
generateRefsResponseTag,
|
||||||
@@ -18,37 +17,24 @@ import { contentPageRefsSchema } from "./output"
|
|||||||
import { TrackingChannelEnum } from "@/types/components/tracking"
|
import { TrackingChannelEnum } from "@/types/components/tracking"
|
||||||
import { ContentPageEnum } from "@/types/enums/contentPage"
|
import { ContentPageEnum } from "@/types/enums/contentPage"
|
||||||
import type { System } from "@/types/requests/system"
|
import type { System } from "@/types/requests/system"
|
||||||
import type {
|
import {
|
||||||
ContentPageRefs,
|
type ContentPageRefs,
|
||||||
GetContentPageRefsSchema,
|
type GetContentPageRefsSchema,
|
||||||
} from "@/types/trpc/routers/contentstack/contentPage"
|
} from "@/types/trpc/routers/contentstack/contentPage"
|
||||||
import type { Lang } from "@/constants/languages"
|
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) {
|
export async function fetchContentPageRefs(lang: Lang, uid: string) {
|
||||||
getContentPageRefsCounter.add(1, { lang, uid })
|
const getContentPageRefsCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.contentPage.refs start",
|
"contentPage.get.refs"
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetContentPageRefs = getContentPageRefsCounter.init({
|
||||||
|
lang,
|
||||||
|
uid,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetContentPageRefs.start()
|
||||||
|
|
||||||
const res = await batchRequest<GetContentPageRefsSchema>([
|
const res = await batchRequest<GetContentPageRefsSchema>([
|
||||||
{
|
{
|
||||||
document: GetContentPageRefs,
|
document: GetContentPageRefs,
|
||||||
@@ -69,50 +55,17 @@ export async function fetchContentPageRefs(lang: Lang, uid: string) {
|
|||||||
])
|
])
|
||||||
if (!res.data) {
|
if (!res.data) {
|
||||||
const notFoundError = notFound(res)
|
const notFoundError = notFound(res)
|
||||||
getContentPageRefsFailCounter.add(1, {
|
metricsGetContentPageRefs.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
const validatedData = contentPageRefsSchema.safeParse(res.data)
|
const validatedData = contentPageRefsSchema.safeParse(res.data)
|
||||||
if (!validatedData.success) {
|
if (!validatedData.success) {
|
||||||
getContentPageRefsFailCounter.add(1, {
|
metricsGetContentPageRefs.validationError(validatedData.error)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
getContentPageRefsSuccessCounter.add(1, { lang, uid })
|
|
||||||
console.info(
|
metricsGetContentPageRefs.success()
|
||||||
"contentstack.contentPage.refs success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return validatedData.data
|
return validatedData.data
|
||||||
}
|
}
|
||||||
@@ -142,8 +95,7 @@ export function getConnections({ content_page }: ContentPageRefs) {
|
|||||||
case ContentPageEnum.ContentStack.blocks.Content:
|
case ContentPageEnum.ContentStack.blocks.Content:
|
||||||
{
|
{
|
||||||
if (block.content.length) {
|
if (block.content.length) {
|
||||||
// TS has trouble infering the filtered types
|
// @ts-expect-error: TS has trouble infering the filtered types
|
||||||
// @ts-ignore
|
|
||||||
connections.push(...block.content)
|
connections.push(...block.content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
} from "@/lib/graphql/Query/DestinationCityPage/DestinationCityPage.graphql"
|
} from "@/lib/graphql/Query/DestinationCityPage/DestinationCityPage.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import { contentStackUidWithServiceProcedure, router } from "@/server/trpc"
|
import { contentStackUidWithServiceProcedure, router } from "@/server/trpc"
|
||||||
|
|
||||||
import { generateRefsResponseTag } from "@/utils/generateTag"
|
import { generateRefsResponseTag } from "@/utils/generateTag"
|
||||||
@@ -13,14 +14,6 @@ import {
|
|||||||
destinationCityPageRefsSchema,
|
destinationCityPageRefsSchema,
|
||||||
destinationCityPageSchema,
|
destinationCityPageSchema,
|
||||||
} from "./output"
|
} from "./output"
|
||||||
import {
|
|
||||||
getDestinationCityPageCounter,
|
|
||||||
getDestinationCityPageFailCounter,
|
|
||||||
getDestinationCityPageRefsCounter,
|
|
||||||
getDestinationCityPageRefsFailCounter,
|
|
||||||
getDestinationCityPageRefsSuccessCounter,
|
|
||||||
getDestinationCityPageSuccessCounter,
|
|
||||||
} from "./telemetry"
|
|
||||||
import { generatePageTags } from "./utils"
|
import { generatePageTags } from "./utils"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -36,11 +29,14 @@ export const destinationCityPageQueryRouter = router({
|
|||||||
get: contentStackUidWithServiceProcedure.query(async ({ ctx }) => {
|
get: contentStackUidWithServiceProcedure.query(async ({ ctx }) => {
|
||||||
const { lang, uid, serviceToken } = ctx
|
const { lang, uid, serviceToken } = ctx
|
||||||
|
|
||||||
getDestinationCityPageRefsCounter.add(1, { lang, uid })
|
const getDestinationCityPageRefsCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.destinationCityPage.refs start",
|
"destinationCityPage.get.refs"
|
||||||
JSON.stringify({ query: { lang, uid } })
|
|
||||||
)
|
)
|
||||||
|
const metricsGetDestinationCityPageRefs =
|
||||||
|
getDestinationCityPageRefsCounter.init({ lang, uid })
|
||||||
|
|
||||||
|
metricsGetDestinationCityPageRefs.start()
|
||||||
|
|
||||||
const refsResponse = await request<GetDestinationCityPageRefsSchema>(
|
const refsResponse = await request<GetDestinationCityPageRefsSchema>(
|
||||||
GetDestinationCityPageRefs,
|
GetDestinationCityPageRefs,
|
||||||
@@ -53,19 +49,7 @@ export const destinationCityPageQueryRouter = router({
|
|||||||
|
|
||||||
if (!refsResponse.data) {
|
if (!refsResponse.data) {
|
||||||
const notFoundError = notFound(refsResponse)
|
const notFoundError = notFound(refsResponse)
|
||||||
getDestinationCityPageRefsFailCounter.add(1, {
|
metricsGetDestinationCityPageRefs.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,33 +57,25 @@ export const destinationCityPageQueryRouter = router({
|
|||||||
refsResponse.data
|
refsResponse.data
|
||||||
)
|
)
|
||||||
if (!validatedRefsData.success) {
|
if (!validatedRefsData.success) {
|
||||||
getDestinationCityPageRefsFailCounter.add(1, {
|
metricsGetDestinationCityPageRefs.validationError(validatedRefsData.error)
|
||||||
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 })
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
getDestinationCityPageRefsSuccessCounter.add(1, { lang, uid: `${uid}` })
|
|
||||||
console.info(
|
metricsGetDestinationCityPageRefs.success()
|
||||||
"contentstack.destinationCityPage.refs success",
|
|
||||||
JSON.stringify({ query: { lang, uid } })
|
|
||||||
)
|
|
||||||
|
|
||||||
const tags = generatePageTags(validatedRefsData.data, lang)
|
const tags = generatePageTags(validatedRefsData.data, lang)
|
||||||
|
|
||||||
getDestinationCityPageCounter.add(1, { lang, uid: `${uid}` })
|
const getDestinationCityPageCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.destinationCityPage start",
|
"destinationCityPage.get"
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetDestinationCityPage = getDestinationCityPageCounter.init({
|
||||||
|
lang,
|
||||||
|
uid,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetDestinationCityPage.start()
|
||||||
|
|
||||||
const response = await request<GetDestinationCityPageData>(
|
const response = await request<GetDestinationCityPageData>(
|
||||||
GetDestinationCityPage,
|
GetDestinationCityPage,
|
||||||
{
|
{
|
||||||
@@ -113,40 +89,17 @@ export const destinationCityPageQueryRouter = router({
|
|||||||
)
|
)
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
const notFoundError = notFound(response)
|
const notFoundError = notFound(response)
|
||||||
getDestinationCityPageFailCounter.add(1, {
|
metricsGetDestinationCityPage.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
const validatedResponse = destinationCityPageSchema.safeParse(response.data)
|
const validatedResponse = destinationCityPageSchema.safeParse(response.data)
|
||||||
|
|
||||||
if (!validatedResponse.success) {
|
if (!validatedResponse.success) {
|
||||||
getDestinationCityPageFailCounter.add(1, {
|
metricsGetDestinationCityPage.validationError(validatedResponse.error)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const destinationCityPage = validatedResponse.data.destination_city_page
|
const destinationCityPage = validatedResponse.data.destination_city_page
|
||||||
const cityIdentifier = destinationCityPage.destination_settings.city
|
const cityIdentifier = destinationCityPage.destination_settings.city
|
||||||
if (!cityIdentifier) {
|
if (!cityIdentifier) {
|
||||||
@@ -160,30 +113,16 @@ export const destinationCityPageQueryRouter = router({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!city) {
|
if (!city) {
|
||||||
getDestinationCityPageFailCounter.add(1, {
|
metricsGetDestinationCityPage.dataError(
|
||||||
lang,
|
`Failed to get city data for ${cityIdentifier}`,
|
||||||
uid: `${uid}`,
|
{
|
||||||
error_type: "not_found",
|
cityIdentifier,
|
||||||
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}`,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getDestinationCityPageSuccessCounter.add(1, { lang, uid: `${uid}` })
|
metricsGetDestinationCityPage.success()
|
||||||
console.info(
|
|
||||||
"contentstack.destinationCityPage success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const system = destinationCityPage.system
|
const system = destinationCityPage.system
|
||||||
const pageName = `destinations|${city.country}|${city.name}`
|
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 { GetCityPageCount } from "@/lib/graphql/Query/DestinationCityPage/DestinationCityPageCount.graphql"
|
||||||
import { GetCityPageUrls } from "@/lib/graphql/Query/DestinationCityPage/DestinationCityPageUrl.graphql"
|
import { GetCityPageUrls } from "@/lib/graphql/Query/DestinationCityPage/DestinationCityPageUrl.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
|
|
||||||
import { generateTag, generateTagsFromSystem } from "@/utils/generateTag"
|
import { generateTag, generateTagsFromSystem } from "@/utils/generateTag"
|
||||||
|
|
||||||
import { batchedCityPageUrlsSchema, cityPageCountSchema } from "./output"
|
import { batchedCityPageUrlsSchema, cityPageCountSchema } from "./output"
|
||||||
import {
|
|
||||||
getCityPageCountCounter,
|
|
||||||
getCityPageCountFailCounter,
|
|
||||||
getCityPageCountSuccessCounter,
|
|
||||||
getCityPageUrlsCounter,
|
|
||||||
getCityPageUrlsFailCounter,
|
|
||||||
getCityPageUrlsSuccessCounter,
|
|
||||||
} from "./telemetry"
|
|
||||||
|
|
||||||
import { DestinationCityPageEnum } from "@/types/enums/destinationCityPage"
|
import { DestinationCityPageEnum } from "@/types/enums/destinationCityPage"
|
||||||
import type { System } from "@/types/requests/system"
|
import type { System } from "@/types/requests/system"
|
||||||
@@ -76,11 +69,13 @@ export function getConnections({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getCityPageCount(lang: Lang) {
|
export async function getCityPageCount(lang: Lang) {
|
||||||
getCityPageCountCounter.add(1, { lang })
|
const getCityPageCountCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.cityPageCount start",
|
"cityPageCount.get"
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
)
|
||||||
|
const metricsGetCityPageCount = getCityPageCountCounter.init({ lang })
|
||||||
|
|
||||||
|
metricsGetCityPageCount.start()
|
||||||
|
|
||||||
const response = await request<GetCityPageCountData>(
|
const response = await request<GetCityPageCountData>(
|
||||||
GetCityPageCount,
|
GetCityPageCount,
|
||||||
@@ -92,15 +87,10 @@ export async function getCityPageCount(lang: Lang) {
|
|||||||
ttl: "max",
|
ttl: "max",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
getCityPageCountFailCounter.add(1, {
|
metricsGetCityPageCount.dataError(
|
||||||
lang,
|
`Failed to get city pages count for ${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 } })
|
|
||||||
)
|
)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -108,35 +98,24 @@ export async function getCityPageCount(lang: Lang) {
|
|||||||
const validatedResponse = cityPageCountSchema.safeParse(response.data)
|
const validatedResponse = cityPageCountSchema.safeParse(response.data)
|
||||||
|
|
||||||
if (!validatedResponse.success) {
|
if (!validatedResponse.success) {
|
||||||
getCityPageCountFailCounter.add(1, {
|
metricsGetCityPageCount.validationError(validatedResponse.error)
|
||||||
lang,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedResponse.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.hotelPageCount validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang },
|
|
||||||
error: validatedResponse.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
getCityPageCountSuccessCounter.add(1, { lang })
|
|
||||||
console.info(
|
metricsGetCityPageCount.success()
|
||||||
"contentstack.cityPageCount success",
|
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
|
||||||
|
|
||||||
return validatedResponse.data
|
return validatedResponse.data
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCityPageUrls(lang: Lang) {
|
export async function getCityPageUrls(lang: Lang) {
|
||||||
getCityPageUrlsCounter.add(1, { lang })
|
const getCityPageUrlsCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.cityPageUrls start",
|
"cityPageUrls.get"
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
)
|
||||||
|
const metricsGetCityPageUrls = getCityPageUrlsCounter.init({ lang })
|
||||||
|
|
||||||
|
metricsGetCityPageUrls.start()
|
||||||
|
|
||||||
const count = await getCityPageCount(lang)
|
const count = await getCityPageCount(lang)
|
||||||
|
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
@@ -162,25 +141,11 @@ export async function getCityPageUrls(lang: Lang) {
|
|||||||
const validatedResponse = batchedCityPageUrlsSchema.safeParse(batchedResponse)
|
const validatedResponse = batchedCityPageUrlsSchema.safeParse(batchedResponse)
|
||||||
|
|
||||||
if (!validatedResponse.success) {
|
if (!validatedResponse.success) {
|
||||||
getCityPageUrlsFailCounter.add(1, {
|
metricsGetCityPageUrls.validationError(validatedResponse.error)
|
||||||
lang,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedResponse.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.cityPageUrls validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang },
|
|
||||||
error: validatedResponse.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
getCityPageUrlsSuccessCounter.add(1, { lang })
|
|
||||||
console.info(
|
metricsGetCityPageUrls.success()
|
||||||
"contentstack.cityPageUrls success",
|
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
|
||||||
|
|
||||||
return validatedResponse.data
|
return validatedResponse.data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
} from "@/lib/graphql/Query/DestinationCountryPage/DestinationCountryPage.graphql"
|
} from "@/lib/graphql/Query/DestinationCountryPage/DestinationCountryPage.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import {
|
import {
|
||||||
contentStackBaseWithServiceProcedure,
|
contentStackBaseWithServiceProcedure,
|
||||||
contentstackExtendedProcedureUID,
|
contentstackExtendedProcedureUID,
|
||||||
@@ -17,14 +18,6 @@ import {
|
|||||||
destinationCountryPageRefsSchema,
|
destinationCountryPageRefsSchema,
|
||||||
destinationCountryPageSchema,
|
destinationCountryPageSchema,
|
||||||
} from "./output"
|
} from "./output"
|
||||||
import {
|
|
||||||
getDestinationCountryPageCounter,
|
|
||||||
getDestinationCountryPageFailCounter,
|
|
||||||
getDestinationCountryPageRefsCounter,
|
|
||||||
getDestinationCountryPageRefsFailCounter,
|
|
||||||
getDestinationCountryPageRefsSuccessCounter,
|
|
||||||
getDestinationCountryPageSuccessCounter,
|
|
||||||
} from "./telemetry"
|
|
||||||
import { generatePageTags, getCityPages } from "./utils"
|
import { generatePageTags, getCityPages } from "./utils"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -41,11 +34,14 @@ export const destinationCountryPageQueryRouter = router({
|
|||||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
||||||
const { lang, uid } = ctx
|
const { lang, uid } = ctx
|
||||||
|
|
||||||
getDestinationCountryPageRefsCounter.add(1, { lang, uid })
|
const getDestinationCountryPageRefsCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.destinationCountryPage.refs start",
|
"destinationCountryPage.get.refs"
|
||||||
JSON.stringify({ query: { lang, uid } })
|
|
||||||
)
|
)
|
||||||
|
const metricsGetDestinationCountryPageRefs =
|
||||||
|
getDestinationCountryPageRefsCounter.init({ lang, uid })
|
||||||
|
|
||||||
|
metricsGetDestinationCountryPageRefs.start()
|
||||||
|
|
||||||
const refsResponse = await request<GetDestinationCountryPageRefsSchema>(
|
const refsResponse = await request<GetDestinationCountryPageRefsSchema>(
|
||||||
GetDestinationCountryPageRefs,
|
GetDestinationCountryPageRefs,
|
||||||
@@ -58,19 +54,7 @@ export const destinationCountryPageQueryRouter = router({
|
|||||||
|
|
||||||
if (!refsResponse.data) {
|
if (!refsResponse.data) {
|
||||||
const notFoundError = notFound(refsResponse)
|
const notFoundError = notFound(refsResponse)
|
||||||
getDestinationCountryPageRefsFailCounter.add(1, {
|
metricsGetDestinationCountryPageRefs.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,33 +62,25 @@ export const destinationCountryPageQueryRouter = router({
|
|||||||
refsResponse.data
|
refsResponse.data
|
||||||
)
|
)
|
||||||
if (!validatedRefsData.success) {
|
if (!validatedRefsData.success) {
|
||||||
getDestinationCountryPageRefsFailCounter.add(1, {
|
metricsGetDestinationCountryPageRefs.validationError(
|
||||||
lang,
|
validatedRefsData.error
|
||||||
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 })
|
|
||||||
)
|
)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
getDestinationCountryPageRefsSuccessCounter.add(1, { lang, uid: `${uid}` })
|
|
||||||
console.info(
|
metricsGetDestinationCountryPageRefs.success()
|
||||||
"contentstack.destinationCountryPage.refs success",
|
|
||||||
JSON.stringify({ query: { lang, uid } })
|
|
||||||
)
|
|
||||||
|
|
||||||
const tags = generatePageTags(validatedRefsData.data, lang)
|
const tags = generatePageTags(validatedRefsData.data, lang)
|
||||||
|
|
||||||
getDestinationCountryPageCounter.add(1, { lang, uid: `${uid}` })
|
const getDestinationCountryPageCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.destinationCountryPage start",
|
"destinationCountryPage.get"
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetDestinationCountryPage =
|
||||||
|
getDestinationCountryPageCounter.init({ lang, uid })
|
||||||
|
|
||||||
|
metricsGetDestinationCountryPage.start()
|
||||||
|
|
||||||
const response = await request<GetDestinationCountryPageData>(
|
const response = await request<GetDestinationCountryPageData>(
|
||||||
GetDestinationCountryPage,
|
GetDestinationCountryPage,
|
||||||
{
|
{
|
||||||
@@ -118,19 +94,7 @@ export const destinationCountryPageQueryRouter = router({
|
|||||||
)
|
)
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
const notFoundError = notFound(response)
|
const notFoundError = notFound(response)
|
||||||
getDestinationCountryPageFailCounter.add(1, {
|
metricsGetDestinationCountryPage.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,32 +103,15 @@ export const destinationCountryPageQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!validatedResponse.success) {
|
if (!validatedResponse.success) {
|
||||||
getDestinationCountryPageFailCounter.add(1, {
|
metricsGetDestinationCountryPage.validationError(validatedResponse.error)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const destinationCountryPage =
|
const destinationCountryPage =
|
||||||
validatedResponse.data.destination_country_page
|
validatedResponse.data.destination_country_page
|
||||||
const country = destinationCountryPage.destination_settings.country
|
const country = destinationCountryPage.destination_settings.country
|
||||||
|
|
||||||
getDestinationCountryPageSuccessCounter.add(1, { lang, uid: `${uid}` })
|
metricsGetDestinationCountryPage.success()
|
||||||
console.info(
|
|
||||||
"contentstack.destinationCountryPage success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const system = destinationCountryPage.system
|
const system = destinationCountryPage.system
|
||||||
const pageName = `destinations|${country}`
|
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 { GetDestinationCityListData } from "@/lib/graphql/Query/DestinationCityPage/DestinationCityListData.graphql"
|
||||||
import { GetCountryPageUrls } from "@/lib/graphql/Query/DestinationCountryPage/DestinationCountryPageUrl.graphql"
|
import { GetCountryPageUrls } from "@/lib/graphql/Query/DestinationCountryPage/DestinationCountryPageUrl.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
|
|
||||||
import { generateTag, generateTagsFromSystem } from "@/utils/generateTag"
|
import { generateTag, generateTagsFromSystem } from "@/utils/generateTag"
|
||||||
|
|
||||||
import { getCitiesByCountry } from "../../hotels/utils"
|
import { getCitiesByCountry } from "../../hotels/utils"
|
||||||
import { destinationCityListDataSchema } from "../destinationCityPage/output"
|
import { destinationCityListDataSchema } from "../destinationCityPage/output"
|
||||||
import { countryPageUrlsSchema } from "./output"
|
import { countryPageUrlsSchema } from "./output"
|
||||||
import {
|
|
||||||
getCityListDataCounter,
|
|
||||||
getCityListDataFailCounter,
|
|
||||||
getCityListDataSuccessCounter,
|
|
||||||
getCountryPageUrlsCounter,
|
|
||||||
getCountryPageUrlsFailCounter,
|
|
||||||
getCountryPageUrlsSuccessCounter,
|
|
||||||
} from "./telemetry"
|
|
||||||
|
|
||||||
import { ApiCountry, type Country } from "@/types/enums/country"
|
import { ApiCountry, type Country } from "@/types/enums/country"
|
||||||
import { DestinationCountryPageEnum } from "@/types/enums/destinationCountryPage"
|
import { DestinationCountryPageEnum } from "@/types/enums/destinationCountryPage"
|
||||||
@@ -77,11 +70,16 @@ export async function getCityListDataByCityIdentifier(
|
|||||||
lang: Lang,
|
lang: Lang,
|
||||||
cityIdentifier: string
|
cityIdentifier: string
|
||||||
) {
|
) {
|
||||||
getCityListDataCounter.add(1, { lang, cityIdentifier })
|
const getCityListDataCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.cityListData start",
|
"cityListData.get"
|
||||||
JSON.stringify({ query: { lang, cityIdentifier } })
|
|
||||||
)
|
)
|
||||||
|
const metricsGetCityListData = getCityListDataCounter.init({
|
||||||
|
lang,
|
||||||
|
cityIdentifier,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetCityListData.start()
|
||||||
|
|
||||||
const response = await request<GetDestinationCityListDataResponse>(
|
const response = await request<GetDestinationCityListDataResponse>(
|
||||||
GetDestinationCityListData,
|
GetDestinationCityListData,
|
||||||
@@ -96,15 +94,8 @@ export async function getCityListDataByCityIdentifier(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
getCityListDataFailCounter.add(1, {
|
metricsGetCityListData.dataError(
|
||||||
lang,
|
`Failed to get destination city page for cityIdentifier: ${cityIdentifier}`
|
||||||
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 } })
|
|
||||||
)
|
)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -114,26 +105,11 @@ export async function getCityListDataByCityIdentifier(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!validatedResponse.success) {
|
if (!validatedResponse.success) {
|
||||||
getCityListDataFailCounter.add(1, {
|
metricsGetCityListData.validationError(validatedResponse.error)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
getCityListDataSuccessCounter.add(1, { lang, cityIdentifier })
|
|
||||||
console.info(
|
metricsGetCityListData.success()
|
||||||
"contentstack.cityListData success",
|
|
||||||
JSON.stringify({ query: { lang, cityIdentifier } })
|
|
||||||
)
|
|
||||||
|
|
||||||
return validatedResponse.data
|
return validatedResponse.data
|
||||||
}
|
}
|
||||||
@@ -172,11 +148,13 @@ export async function getCityPages(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getCountryPageUrls(lang: Lang) {
|
export async function getCountryPageUrls(lang: Lang) {
|
||||||
getCountryPageUrlsCounter.add(1, { lang })
|
const getCountryPageUrlsCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.countryPageUrls start",
|
"getCountryPageUrls"
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
)
|
||||||
|
const metricsGetCountryPageUrls = getCountryPageUrlsCounter.init({ lang })
|
||||||
|
|
||||||
|
metricsGetCountryPageUrls.start()
|
||||||
|
|
||||||
const tag = `${lang}:country_page_urls`
|
const tag = `${lang}:country_page_urls`
|
||||||
const response = await request<GetCountryPageUrlsData>(
|
const response = await request<GetCountryPageUrlsData>(
|
||||||
@@ -191,14 +169,8 @@ export async function getCountryPageUrls(lang: Lang) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
getCountryPageUrlsFailCounter.add(1, {
|
metricsGetCountryPageUrls.dataError(
|
||||||
lang,
|
`Failed to get country pages for lang: ${lang}`
|
||||||
error_type: "not_found",
|
|
||||||
error: `Country pages not found for lang: ${lang}`,
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.countryPageUrls not found error",
|
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
)
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@@ -208,26 +180,11 @@ export async function getCountryPageUrls(lang: Lang) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!validatedCountryPageUrls.success) {
|
if (!validatedCountryPageUrls.success) {
|
||||||
getCountryPageUrlsFailCounter.add(1, {
|
metricsGetCountryPageUrls.validationError(validatedCountryPageUrls.error)
|
||||||
lang,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedCountryPageUrls.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.countryPageUrls validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang },
|
|
||||||
error: validatedCountryPageUrls.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
getCountryPageUrlsSuccessCounter.add(1, { lang })
|
metricsGetCountryPageUrls.success()
|
||||||
console.info(
|
|
||||||
"contentstack.countryPageUrls success",
|
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
|
||||||
|
|
||||||
return validatedCountryPageUrls.data
|
return validatedCountryPageUrls.data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
} from "@/lib/graphql/Query/DestinationOverviewPage/DestinationOverviewPage.graphql"
|
} from "@/lib/graphql/Query/DestinationOverviewPage/DestinationOverviewPage.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import {
|
import {
|
||||||
contentstackExtendedProcedureUID,
|
contentstackExtendedProcedureUID,
|
||||||
router,
|
router,
|
||||||
@@ -31,14 +32,6 @@ import {
|
|||||||
destinationOverviewPageRefsSchema,
|
destinationOverviewPageRefsSchema,
|
||||||
destinationOverviewPageSchema,
|
destinationOverviewPageSchema,
|
||||||
} from "./output"
|
} from "./output"
|
||||||
import {
|
|
||||||
getDestinationOverviewPageCounter,
|
|
||||||
getDestinationOverviewPageFailCounter,
|
|
||||||
getDestinationOverviewPageRefsCounter,
|
|
||||||
getDestinationOverviewPageRefsFailCounter,
|
|
||||||
getDestinationOverviewPageRefsSuccessCounter,
|
|
||||||
getDestinationOverviewPageSuccessCounter,
|
|
||||||
} from "./telemetry"
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
Cities,
|
Cities,
|
||||||
@@ -57,13 +50,15 @@ export const destinationOverviewPageQueryRouter = router({
|
|||||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
||||||
const { lang, uid } = ctx
|
const { lang, uid } = ctx
|
||||||
|
|
||||||
getDestinationOverviewPageRefsCounter.add(1, { lang, uid: `${uid}` })
|
const getDestinationOverviewPageRefsCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.destinationOverviewPage.refs start",
|
"destinationOverviewPage.get.refs"
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetDestinationOverviewPageRefs =
|
||||||
|
getDestinationOverviewPageRefsCounter.init({ lang, uid })
|
||||||
|
|
||||||
|
metricsGetDestinationOverviewPageRefs.start()
|
||||||
|
|
||||||
const refsResponse = await request<GetDestinationOverviewPageRefsSchema>(
|
const refsResponse = await request<GetDestinationOverviewPageRefsSchema>(
|
||||||
GetDestinationOverviewPageRefs,
|
GetDestinationOverviewPageRefs,
|
||||||
{
|
{
|
||||||
@@ -77,19 +72,7 @@ export const destinationOverviewPageQueryRouter = router({
|
|||||||
)
|
)
|
||||||
if (!refsResponse.data) {
|
if (!refsResponse.data) {
|
||||||
const notFoundError = notFound(refsResponse)
|
const notFoundError = notFound(refsResponse)
|
||||||
getDestinationOverviewPageRefsFailCounter.add(1, {
|
metricsGetDestinationOverviewPageRefs.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,37 +81,23 @@ export const destinationOverviewPageQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!validatedRefsData.success) {
|
if (!validatedRefsData.success) {
|
||||||
getDestinationOverviewPageRefsFailCounter.add(1, {
|
metricsGetDestinationOverviewPageRefs.validationError(
|
||||||
lang,
|
validatedRefsData.error
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getDestinationOverviewPageRefsSuccessCounter.add(1, { lang, uid: `${uid}` })
|
metricsGetDestinationOverviewPageRefs.success()
|
||||||
console.info(
|
|
||||||
"contentstack.destinationOverviewPage.refs success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
getDestinationOverviewPageCounter.add(1, { lang, uid: `${uid}` })
|
const getDestinationOverviewPageCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.destinationOverviewPage start",
|
"destinationOverviewPage.get"
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetDestinationOverviewPage =
|
||||||
|
getDestinationOverviewPageCounter.init({ lang, uid })
|
||||||
|
|
||||||
|
metricsGetDestinationOverviewPage.start()
|
||||||
|
|
||||||
const response = await request<GetDestinationOverviewPageData>(
|
const response = await request<GetDestinationOverviewPageData>(
|
||||||
GetDestinationOverviewPage,
|
GetDestinationOverviewPage,
|
||||||
{
|
{
|
||||||
@@ -142,19 +111,7 @@ export const destinationOverviewPageQueryRouter = router({
|
|||||||
)
|
)
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
const notFoundError = notFound(response)
|
const notFoundError = notFound(response)
|
||||||
getDestinationOverviewPageFailCounter.add(1, {
|
metricsGetDestinationOverviewPage.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,29 +120,13 @@ export const destinationOverviewPageQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!destinationOverviewPage.success) {
|
if (!destinationOverviewPage.success) {
|
||||||
getDestinationOverviewPageFailCounter.add(1, {
|
metricsGetDestinationOverviewPage.validationError(
|
||||||
lang,
|
destinationOverviewPage.error
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getDestinationOverviewPageSuccessCounter.add(1, { lang, uid: `${uid}` })
|
metricsGetDestinationOverviewPage.success()
|
||||||
console.info(
|
|
||||||
"contentstack.destinationOverviewPage success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const system = destinationOverviewPage.data.destination_overview_page.system
|
const system = destinationOverviewPage.data.destination_overview_page.system
|
||||||
const tracking: TrackingSDKPageData = {
|
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 { GetHotelPage } from "@/lib/graphql/Query/HotelPage/HotelPage.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
||||||
|
|
||||||
import { generateTag } from "@/utils/generateTag"
|
import { generateTag } from "@/utils/generateTag"
|
||||||
|
|
||||||
import { hotelPageSchema } from "./output"
|
import { hotelPageSchema } from "./output"
|
||||||
import {
|
|
||||||
getHotelPageCounter,
|
|
||||||
getHotelPageFailCounter,
|
|
||||||
getHotelPageSuccessCounter,
|
|
||||||
} from "./telemetry"
|
|
||||||
|
|
||||||
import type { GetHotelPageData } from "@/types/trpc/routers/contentstack/hotelPage"
|
import type { GetHotelPageData } from "@/types/trpc/routers/contentstack/hotelPage"
|
||||||
|
|
||||||
export const hotelPageQueryRouter = router({
|
export const hotelPageQueryRouter = router({
|
||||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
||||||
const { lang, uid } = ctx
|
const { lang, uid } = ctx
|
||||||
getHotelPageCounter.add(1, { lang, uid: `${uid}` })
|
|
||||||
console.info(
|
const getHotelPageCounter = createCounter(
|
||||||
"contentstack.hotelPage start",
|
"trpc.contentstack",
|
||||||
JSON.stringify({
|
"hotelPage.get"
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetHotelPage = getHotelPageCounter.init({ lang, uid })
|
||||||
|
|
||||||
|
metricsGetHotelPage.start()
|
||||||
|
|
||||||
const response = await request<GetHotelPageData>(
|
const response = await request<GetHotelPageData>(
|
||||||
GetHotelPage,
|
GetHotelPage,
|
||||||
{
|
{
|
||||||
@@ -37,48 +35,19 @@ export const hotelPageQueryRouter = router({
|
|||||||
)
|
)
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
const notFoundError = notFound(response)
|
const notFoundError = notFound(response)
|
||||||
getHotelPageFailCounter.add(1, {
|
metricsGetHotelPage.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
const validatedHotelPage = hotelPageSchema.safeParse(response.data)
|
const validatedHotelPage = hotelPageSchema.safeParse(response.data)
|
||||||
|
|
||||||
if (!validatedHotelPage.success) {
|
if (!validatedHotelPage.success) {
|
||||||
getHotelPageFailCounter.add(1, {
|
metricsGetHotelPage.validationError(validatedHotelPage.error)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getHotelPageSuccessCounter.add(1, { lang, uid: `${uid}` })
|
metricsGetHotelPage.success()
|
||||||
console.info(
|
|
||||||
"contentstack.hotelPage success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return validatedHotelPage.data.hotel_page
|
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 { GetHotelPageUrls } from "@/lib/graphql/Query/HotelPage/HotelPageUrl.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
generateRefsResponseTag,
|
generateRefsResponseTag,
|
||||||
@@ -15,17 +16,6 @@ import {
|
|||||||
hotelPageCountSchema,
|
hotelPageCountSchema,
|
||||||
hotelPageRefsSchema,
|
hotelPageRefsSchema,
|
||||||
} from "./output"
|
} from "./output"
|
||||||
import {
|
|
||||||
getHotelPageCountCounter,
|
|
||||||
getHotelPageCountFailCounter,
|
|
||||||
getHotelPageCountSuccessCounter,
|
|
||||||
getHotelPageRefsCounter,
|
|
||||||
getHotelPageRefsFailCounter,
|
|
||||||
getHotelPageRefsSuccessCounter,
|
|
||||||
getHotelPageUrlsCounter,
|
|
||||||
getHotelPageUrlsFailCounter,
|
|
||||||
getHotelPageUrlsSuccessCounter,
|
|
||||||
} from "./telemetry"
|
|
||||||
|
|
||||||
import { HotelPageEnum } from "@/types/enums/hotelPage"
|
import { HotelPageEnum } from "@/types/enums/hotelPage"
|
||||||
import type { System } from "@/types/requests/system"
|
import type { System } from "@/types/requests/system"
|
||||||
@@ -38,13 +28,13 @@ import type {
|
|||||||
import type { Lang } from "@/constants/languages"
|
import type { Lang } from "@/constants/languages"
|
||||||
|
|
||||||
export async function fetchHotelPageRefs(lang: Lang, uid: string) {
|
export async function fetchHotelPageRefs(lang: Lang, uid: string) {
|
||||||
getHotelPageRefsCounter.add(1, { lang, uid })
|
const getHotelPageRefsCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.hotelPage.refs start",
|
"hotelPage.get.refs"
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetHotelPageRefs = getHotelPageRefsCounter.init({ lang, uid })
|
||||||
|
|
||||||
|
metricsGetHotelPageRefs.start()
|
||||||
|
|
||||||
const refsResponse = await request<GetHotelPageRefsSchema>(
|
const refsResponse = await request<GetHotelPageRefsSchema>(
|
||||||
GetHotelPageRefs,
|
GetHotelPageRefs,
|
||||||
@@ -56,26 +46,10 @@ export async function fetchHotelPageRefs(lang: Lang, uid: string) {
|
|||||||
)
|
)
|
||||||
if (!refsResponse.data) {
|
if (!refsResponse.data) {
|
||||||
const notFoundError = notFound(refsResponse)
|
const notFoundError = notFound(refsResponse)
|
||||||
getHotelPageRefsFailCounter.add(1, {
|
metricsGetHotelPageRefs.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
return refsResponse.data
|
return refsResponse.data
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,30 +58,20 @@ export function validateHotelPageRefs(
|
|||||||
lang: Lang,
|
lang: Lang,
|
||||||
uid: string
|
uid: string
|
||||||
) {
|
) {
|
||||||
const validatedData = hotelPageRefsSchema.safeParse(data)
|
const getHotelPageRefsCounter = createCounter(
|
||||||
if (!validatedData.success) {
|
"trpc.contentstack",
|
||||||
getHotelPageRefsFailCounter.add(1, {
|
"hotelPage.get.refs"
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetHotelPageRefs = getHotelPageRefsCounter.init({ lang, uid })
|
||||||
|
|
||||||
|
const validatedData = hotelPageRefsSchema.safeParse(data)
|
||||||
|
|
||||||
|
if (!validatedData.success) {
|
||||||
|
metricsGetHotelPageRefs.validationError(validatedData.error)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
getHotelPageRefsSuccessCounter.add(1, { lang, uid })
|
|
||||||
console.info(
|
metricsGetHotelPageRefs.success()
|
||||||
"contentstack.hotelPage.refs success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return validatedData.data
|
return validatedData.data
|
||||||
}
|
}
|
||||||
@@ -144,11 +108,14 @@ export function getConnections({ hotel_page }: HotelPageRefs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getHotelPageCount(lang: Lang) {
|
export async function getHotelPageCount(lang: Lang) {
|
||||||
getHotelPageCountCounter.add(1, { lang })
|
const getHotelPageCountCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.hotelPageCount start",
|
"hotelPageCount.get"
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
)
|
||||||
|
const metricsGetHotelPageCount = getHotelPageCountCounter.init({ lang })
|
||||||
|
|
||||||
|
metricsGetHotelPageCount.start()
|
||||||
|
|
||||||
const response = await request<GetHotelPageCountData>(
|
const response = await request<GetHotelPageCountData>(
|
||||||
GetHotelPageCount,
|
GetHotelPageCount,
|
||||||
{
|
{
|
||||||
@@ -161,50 +128,31 @@ export async function getHotelPageCount(lang: Lang) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
getHotelPageCountFailCounter.add(1, {
|
metricsGetHotelPageCount.noDataError()
|
||||||
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 } })
|
|
||||||
)
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const validatedResponse = hotelPageCountSchema.safeParse(response.data)
|
const validatedResponse = hotelPageCountSchema.safeParse(response.data)
|
||||||
|
|
||||||
if (!validatedResponse.success) {
|
if (!validatedResponse.success) {
|
||||||
getHotelPageCountFailCounter.add(1, {
|
metricsGetHotelPageCount.validationError(validatedResponse.error)
|
||||||
lang,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedResponse.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.hotelPageCount validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang },
|
|
||||||
error: validatedResponse.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
getHotelPageCountSuccessCounter.add(1, { lang })
|
|
||||||
console.info(
|
metricsGetHotelPageCount.success()
|
||||||
"contentstack.hotelPageCount success",
|
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
|
||||||
|
|
||||||
return validatedResponse.data
|
return validatedResponse.data
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getHotelPageUrls(lang: Lang) {
|
export async function getHotelPageUrls(lang: Lang) {
|
||||||
getHotelPageUrlsCounter.add(1, { lang })
|
const getHotelPageUrlsCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.hotelPageUrls start",
|
"hotelPageUrls.get"
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
)
|
||||||
|
const metricsGetHotelPageUrls = getHotelPageUrlsCounter.init({ lang })
|
||||||
|
|
||||||
|
metricsGetHotelPageUrls.start()
|
||||||
|
|
||||||
const count = await getHotelPageCount(lang)
|
const count = await getHotelPageCount(lang)
|
||||||
|
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
@@ -236,25 +184,11 @@ export async function getHotelPageUrls(lang: Lang) {
|
|||||||
batchedHotelPageUrlsSchema.safeParse(batchedResponse)
|
batchedHotelPageUrlsSchema.safeParse(batchedResponse)
|
||||||
|
|
||||||
if (!validatedResponse.success) {
|
if (!validatedResponse.success) {
|
||||||
getHotelPageUrlsFailCounter.add(1, {
|
metricsGetHotelPageUrls.validationError(validatedResponse.error)
|
||||||
lang,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedResponse.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.hotelPageUrls validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang },
|
|
||||||
error: validatedResponse.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
getHotelPageUrlsSuccessCounter.add(1, { lang })
|
|
||||||
console.info(
|
metricsGetHotelPageUrls.success()
|
||||||
"contentstack.hotelPageUrl success",
|
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
|
||||||
|
|
||||||
return validatedResponse.data
|
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,
|
GetFiNoSvUrlsStartPage,
|
||||||
} from "@/lib/graphql/Query/StartPage/StartPage.graphql"
|
} from "@/lib/graphql/Query/StartPage/StartPage.graphql"
|
||||||
import { internalServerError } from "@/server/errors/trpc"
|
import { internalServerError } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
|
|
||||||
import { generateTag } from "@/utils/generateTag"
|
import { generateTag } from "@/utils/generateTag"
|
||||||
import { removeTrailingSlash } from "@/utils/url"
|
import { removeTrailingSlash } from "@/utils/url"
|
||||||
|
|
||||||
import { validateLanguageSwitcherData } from "./output"
|
import { validateLanguageSwitcherData } from "./output"
|
||||||
import {
|
|
||||||
getLanguageSwitcherCounter,
|
|
||||||
getLanguageSwitcherFailCounter,
|
|
||||||
getLanguageSwitcherSuccessCounter,
|
|
||||||
} from "./telemetry"
|
|
||||||
|
|
||||||
import { PageContentTypeEnum } from "@/types/requests/contentType"
|
import { PageContentTypeEnum } from "@/types/requests/contentType"
|
||||||
import type {
|
import type {
|
||||||
@@ -65,21 +61,17 @@ export async function getUrlsOfAllLanguages(
|
|||||||
uid: string,
|
uid: string,
|
||||||
contentType: string
|
contentType: string
|
||||||
) {
|
) {
|
||||||
getLanguageSwitcherCounter.add(1, {
|
const getLanguageSwitcherCounter = createCounter(
|
||||||
uid,
|
"trpc.contentstack",
|
||||||
lang,
|
"languageSwitcher.get"
|
||||||
contentType,
|
|
||||||
})
|
|
||||||
console.info(
|
|
||||||
"contentstack.languageSwitcher start",
|
|
||||||
JSON.stringify({
|
|
||||||
query: {
|
|
||||||
uid,
|
|
||||||
lang,
|
|
||||||
contentType,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetLanguageSwitcher = getLanguageSwitcherCounter.init({
|
||||||
|
lang,
|
||||||
|
uid,
|
||||||
|
contentType,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetLanguageSwitcher.start()
|
||||||
|
|
||||||
const variables = { uid }
|
const variables = { uid }
|
||||||
const tagsDaDeEn = [
|
const tagsDaDeEn = [
|
||||||
@@ -184,42 +176,13 @@ export async function getUrlsOfAllLanguages(
|
|||||||
validateLanguageSwitcherData.safeParse(urls)
|
validateLanguageSwitcherData.safeParse(urls)
|
||||||
|
|
||||||
if (!validatedLanguageSwitcherData.success) {
|
if (!validatedLanguageSwitcherData.success) {
|
||||||
getLanguageSwitcherFailCounter.add(1, {
|
metricsGetLanguageSwitcher.validationError(
|
||||||
uid,
|
validatedLanguageSwitcherData.error
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getLanguageSwitcherSuccessCounter.add(1, {
|
metricsGetLanguageSwitcher.success()
|
||||||
uid,
|
|
||||||
lang,
|
|
||||||
contentType,
|
|
||||||
})
|
|
||||||
console.info(
|
|
||||||
"contentstack.languageSwitcher success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: {
|
|
||||||
uid,
|
|
||||||
lang,
|
|
||||||
contentType,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return urls
|
return urls
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { metrics } from "@opentelemetry/api"
|
|
||||||
import { cache } from "react"
|
import { cache } from "react"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -11,6 +10,7 @@ import {
|
|||||||
} from "@/lib/graphql/Query/LoyaltyLevels.graphql"
|
} from "@/lib/graphql/Query/LoyaltyLevels.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import { contentstackBaseProcedure, router } from "@/server/trpc"
|
import { contentstackBaseProcedure, router } from "@/server/trpc"
|
||||||
|
|
||||||
import { generateLoyaltyConfigTag } from "@/utils/generateTag"
|
import { generateLoyaltyConfigTag } from "@/utils/generateTag"
|
||||||
@@ -24,32 +24,14 @@ import {
|
|||||||
|
|
||||||
import type { Context } from "@/server/context"
|
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) => {
|
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
|
// 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
|
// 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) {
|
if (!loyaltyLevelsConfigResponse.data) {
|
||||||
getAllLoyaltyLevelFailCounter.add(1)
|
|
||||||
const notFoundError = notFound(loyaltyLevelsConfigResponse)
|
const notFoundError = notFound(loyaltyLevelsConfigResponse)
|
||||||
console.error(
|
metricsGetLoyaltyLevelAll.noDataError()
|
||||||
"contentstack.loyaltyLevels not found error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: {
|
|
||||||
lang: ctx.lang,
|
|
||||||
},
|
|
||||||
error: { code: notFoundError.code },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,30 +58,28 @@ export const getAllLoyaltyLevels = cache(async (ctx: Context) => {
|
|||||||
loyaltyLevelsConfigResponse.data
|
loyaltyLevelsConfigResponse.data
|
||||||
)
|
)
|
||||||
if (!validatedLoyaltyLevels.success) {
|
if (!validatedLoyaltyLevels.success) {
|
||||||
getAllLoyaltyLevelFailCounter.add(1)
|
metricsGetLoyaltyLevelAll.validationError(validatedLoyaltyLevels.error)
|
||||||
console.error(validatedLoyaltyLevels.error)
|
|
||||||
console.error(
|
|
||||||
"contentstack.rewards validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: {
|
|
||||||
lang: ctx.lang,
|
|
||||||
},
|
|
||||||
error: validatedLoyaltyLevels.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllLoyaltyLevelSuccessCounter.add(1)
|
metricsGetLoyaltyLevelAll.success()
|
||||||
|
|
||||||
return validatedLoyaltyLevels.data
|
return validatedLoyaltyLevels.data
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getLoyaltyLevel = cache(
|
export const getLoyaltyLevel = cache(
|
||||||
async (ctx: Context, level_id: MembershipLevel) => {
|
async (ctx: Context, level_id: MembershipLevel) => {
|
||||||
getByLevelLoyaltyLevelCounter.add(1, {
|
const getLoyaltyLevelCounter = createCounter(
|
||||||
query: JSON.stringify({ lang: ctx.lang, level_id }),
|
"trpc.contentstack",
|
||||||
|
"loyaltyLevel.get"
|
||||||
|
)
|
||||||
|
const metricsGetLoyaltyLevel = getLoyaltyLevelCounter.init({
|
||||||
|
lang: ctx.lang,
|
||||||
|
level_id,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
metricsGetLoyaltyLevel.start()
|
||||||
|
|
||||||
const loyaltyLevelsConfigResponse = await request<LoyaltyLevelsResponse>(
|
const loyaltyLevelsConfigResponse = await request<LoyaltyLevelsResponse>(
|
||||||
GetLoyaltyLevel,
|
GetLoyaltyLevel,
|
||||||
{ lang: ctx.lang, level_id },
|
{ lang: ctx.lang, level_id },
|
||||||
@@ -121,15 +92,8 @@ export const getLoyaltyLevel = cache(
|
|||||||
!loyaltyLevelsConfigResponse.data ||
|
!loyaltyLevelsConfigResponse.data ||
|
||||||
!loyaltyLevelsConfigResponse.data.all_loyalty_level.items.length
|
!loyaltyLevelsConfigResponse.data.all_loyalty_level.items.length
|
||||||
) {
|
) {
|
||||||
getByLevelLoyaltyLevelFailCounter.add(1)
|
|
||||||
const notFoundError = notFound(loyaltyLevelsConfigResponse)
|
const notFoundError = notFound(loyaltyLevelsConfigResponse)
|
||||||
console.error(
|
metricsGetLoyaltyLevel.noDataError()
|
||||||
"contentstack.loyaltyLevel not found error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang: ctx.lang, level_id },
|
|
||||||
error: { code: notFoundError.code },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,19 +101,12 @@ export const getLoyaltyLevel = cache(
|
|||||||
loyaltyLevelsConfigResponse.data
|
loyaltyLevelsConfigResponse.data
|
||||||
)
|
)
|
||||||
if (!validatedLoyaltyLevels.success) {
|
if (!validatedLoyaltyLevels.success) {
|
||||||
getByLevelLoyaltyLevelFailCounter.add(1)
|
metricsGetLoyaltyLevel.validationError(validatedLoyaltyLevels.error)
|
||||||
console.error(validatedLoyaltyLevels.error)
|
|
||||||
console.error(
|
|
||||||
"contentstack.loyaltyLevel validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang: ctx.lang, level_id },
|
|
||||||
error: validatedLoyaltyLevels.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getByLevelLoyaltyLevelSuccessCounter.add(1)
|
metricsGetLoyaltyLevel.success()
|
||||||
|
|
||||||
const result: LoyaltyLevel = validatedLoyaltyLevels.data[0]
|
const result: LoyaltyLevel = validatedLoyaltyLevels.data[0]
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { metrics } from "@opentelemetry/api"
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
GetLoyaltyPage,
|
GetLoyaltyPage,
|
||||||
GetLoyaltyPageRefs,
|
GetLoyaltyPageRefs,
|
||||||
} from "@/lib/graphql/Query/LoyaltyPage/LoyaltyPage.graphql"
|
} from "@/lib/graphql/Query/LoyaltyPage/LoyaltyPage.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -26,39 +25,22 @@ import type {
|
|||||||
GetLoyaltyPageSchema,
|
GetLoyaltyPageSchema,
|
||||||
} from "@/types/trpc/routers/contentstack/loyaltyPage"
|
} 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({
|
export const loyaltyPageQueryRouter = router({
|
||||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
||||||
const { lang, uid } = ctx
|
const { lang, uid } = ctx
|
||||||
const metricsVariables = { lang, uid }
|
|
||||||
const variables = { locale: lang, uid }
|
const getLoyaltyPageRefsCounter = createCounter(
|
||||||
getLoyaltyPageRefsCounter.add(1, metricsVariables)
|
"trpc.contentstack",
|
||||||
console.info(
|
"loyaltyPage.get.refs"
|
||||||
"contentstack.loyaltyPage.refs start",
|
|
||||||
JSON.stringify({
|
|
||||||
query: metricsVariables,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetLoyaltyPageRefs = getLoyaltyPageRefsCounter.init({
|
||||||
|
lang,
|
||||||
|
uid,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetLoyaltyPageRefs.start()
|
||||||
|
|
||||||
|
const variables = { locale: lang, uid }
|
||||||
const refsResponse = await request<GetLoyaltyPageRefsSchema>(
|
const refsResponse = await request<GetLoyaltyPageRefsSchema>(
|
||||||
GetLoyaltyPageRefs,
|
GetLoyaltyPageRefs,
|
||||||
variables,
|
variables,
|
||||||
@@ -70,20 +52,7 @@ export const loyaltyPageQueryRouter = router({
|
|||||||
|
|
||||||
if (!refsResponse.data) {
|
if (!refsResponse.data) {
|
||||||
const notFoundError = notFound(refsResponse)
|
const notFoundError = notFound(refsResponse)
|
||||||
getLoyaltyPageRefsFailCounter.add(1, {
|
metricsGetLoyaltyPageRefs.noDataError()
|
||||||
...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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,27 +60,11 @@ export const loyaltyPageQueryRouter = router({
|
|||||||
refsResponse.data
|
refsResponse.data
|
||||||
)
|
)
|
||||||
if (!validatedLoyaltyPageRefs.success) {
|
if (!validatedLoyaltyPageRefs.success) {
|
||||||
getLoyaltyPageRefsFailCounter.add(1, {
|
metricsGetLoyaltyPageRefs.validationError(validatedLoyaltyPageRefs.error)
|
||||||
...metricsVariables,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedLoyaltyPageRefs.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.loyaltyPage.refs validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: metricsVariables,
|
|
||||||
error: validatedLoyaltyPageRefs.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
getLoyaltyPageRefsSuccessCounter.add(1, metricsVariables)
|
|
||||||
console.info(
|
metricsGetLoyaltyPageRefs.success()
|
||||||
"contentstack.loyaltyPage.refs success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: metricsVariables,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const connections = getConnections(validatedLoyaltyPageRefs.data)
|
const connections = getConnections(validatedLoyaltyPageRefs.data)
|
||||||
|
|
||||||
@@ -119,13 +72,15 @@ export const loyaltyPageQueryRouter = router({
|
|||||||
generateTagsFromSystem(lang, connections),
|
generateTagsFromSystem(lang, connections),
|
||||||
generateTag(lang, validatedLoyaltyPageRefs.data.loyalty_page.system.uid),
|
generateTag(lang, validatedLoyaltyPageRefs.data.loyalty_page.system.uid),
|
||||||
].flat()
|
].flat()
|
||||||
getLoyaltyPageCounter.add(1, metricsVariables)
|
|
||||||
console.info(
|
const getLoyaltyPageCounter = createCounter(
|
||||||
"contentstack.loyaltyPage start",
|
"trpc.contentstack",
|
||||||
JSON.stringify({
|
"loyaltyPage.get"
|
||||||
query: metricsVariables,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetLoyaltyPage = getLoyaltyPageCounter.init({ lang, uid })
|
||||||
|
|
||||||
|
metricsGetLoyaltyPage.start()
|
||||||
|
|
||||||
const response = await request<GetLoyaltyPageSchema>(
|
const response = await request<GetLoyaltyPageSchema>(
|
||||||
GetLoyaltyPage,
|
GetLoyaltyPage,
|
||||||
variables,
|
variables,
|
||||||
@@ -137,35 +92,13 @@ export const loyaltyPageQueryRouter = router({
|
|||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
const notFoundError = notFound(response)
|
const notFoundError = notFound(response)
|
||||||
getLoyaltyPageFailCounter.add(1, {
|
metricsGetLoyaltyPage.noDataError()
|
||||||
...metricsVariables,
|
throw notFoundError
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const validatedLoyaltyPage = loyaltyPageSchema.safeParse(response.data)
|
const validatedLoyaltyPage = loyaltyPageSchema.safeParse(response.data)
|
||||||
if (!validatedLoyaltyPage.success) {
|
if (!validatedLoyaltyPage.success) {
|
||||||
getLoyaltyPageFailCounter.add(1, {
|
metricsGetLoyaltyPage.validationError(validatedLoyaltyPage.error)
|
||||||
...metricsVariables,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedLoyaltyPage.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.loyaltyPage validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: metricsVariables,
|
|
||||||
error: validatedLoyaltyPage.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,11 +115,8 @@ export const loyaltyPageQueryRouter = router({
|
|||||||
siteSections: validatedLoyaltyPage.data.trackingProps.url,
|
siteSections: validatedLoyaltyPage.data.trackingProps.url,
|
||||||
siteVersion: "new-web",
|
siteVersion: "new-web",
|
||||||
}
|
}
|
||||||
getLoyaltyPageSuccessCounter.add(1, metricsVariables)
|
|
||||||
console.info(
|
metricsGetLoyaltyPage.success()
|
||||||
"contentstack.loyaltyPage success",
|
|
||||||
JSON.stringify({ query: metricsVariables })
|
|
||||||
)
|
|
||||||
|
|
||||||
// Assert LoyaltyPage type to get correct typings for RTE fields
|
// Assert LoyaltyPage type to get correct typings for RTE fields
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { metrics } from "@opentelemetry/api"
|
|
||||||
import { cache } from "react"
|
import { cache } from "react"
|
||||||
|
|
||||||
import { GetAccountPageMetadata } from "@/lib/graphql/Query/AccountPage/Metadata.graphql"
|
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 { GetStartPageMetadata } from "@/lib/graphql/Query/StartPage/Metadata.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import { contentStackUidWithServiceProcedure, router } from "@/server/trpc"
|
import { contentStackUidWithServiceProcedure, router } from "@/server/trpc"
|
||||||
|
|
||||||
import { generateTag } from "@/utils/generateTag"
|
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 { RawMetadataSchema } from "@/types/trpc/routers/contentstack/metadata"
|
||||||
import type { Lang } from "@/constants/languages"
|
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>(
|
const fetchMetadata = cache(async function fetchMemoizedMetadata<T>(
|
||||||
query: string,
|
query: string,
|
||||||
{ uid, lang }: { uid: string; lang: Lang }
|
{ uid, lang }: { uid: string; lang: Lang }
|
||||||
) {
|
) {
|
||||||
fetchMetadataCounter.add(1, { lang, uid })
|
const getMetadataCounter = createCounter("trpc.contentstack", "metadata.get")
|
||||||
console.info(
|
const metricsGetMetadata = getMetadataCounter.init({ lang, uid })
|
||||||
"contentstack.metadata fetch start",
|
|
||||||
JSON.stringify({ query: { lang, uid } })
|
metricsGetMetadata.start()
|
||||||
)
|
|
||||||
const response = await request<T>(
|
const response = await request<T>(
|
||||||
query,
|
query,
|
||||||
{ locale: lang, uid },
|
{ locale: lang, uid },
|
||||||
@@ -68,54 +46,35 @@ const fetchMetadata = cache(async function fetchMemoizedMetadata<T>(
|
|||||||
ttl: "max",
|
ttl: "max",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
const notFoundError = notFound(response)
|
const notFoundError = notFound(response)
|
||||||
fetchMetadataFailCounter.add(1, {
|
metricsGetMetadata.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchMetadataSuccessCounter.add(1, { lang, uid })
|
metricsGetMetadata.success()
|
||||||
console.info(
|
|
||||||
"contentstack.metadata fetch success",
|
|
||||||
JSON.stringify({ query: { lang, uid } })
|
|
||||||
)
|
|
||||||
|
|
||||||
return response.data
|
return response.data
|
||||||
})
|
})
|
||||||
|
|
||||||
async function getTransformedMetadata(data: unknown) {
|
async function getTransformedMetadata(data: unknown) {
|
||||||
transformMetadataCounter.add(1)
|
const transformMetadataCounter = createCounter(
|
||||||
console.info("contentstack.metadata transform start")
|
"trpc.contentstack",
|
||||||
|
"metadata.transform"
|
||||||
|
)
|
||||||
|
const metricsTransformMetadata = transformMetadataCounter.init()
|
||||||
|
|
||||||
|
metricsTransformMetadata.start()
|
||||||
|
|
||||||
const validatedMetadata = await metadataSchema.safeParseAsync(data)
|
const validatedMetadata = await metadataSchema.safeParseAsync(data)
|
||||||
|
|
||||||
if (!validatedMetadata.success) {
|
if (!validatedMetadata.success) {
|
||||||
transformMetadataFailCounter.add(1, {
|
metricsTransformMetadata.validationError(validatedMetadata.error)
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedMetadata.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.metadata validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
error: validatedMetadata.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
transformMetadataSuccessCounter.add(1)
|
metricsTransformMetadata.success()
|
||||||
console.info("contentstack.metadata transform success")
|
|
||||||
|
|
||||||
return validatedMetadata.data
|
return validatedMetadata.data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { metrics } from "@opentelemetry/api"
|
|
||||||
import { cache } from "react"
|
import { cache } from "react"
|
||||||
|
|
||||||
import { GetAllSasTierComparison } from "@/lib/graphql/Query/SASTierComparison.graphql"
|
import { GetAllSasTierComparison } from "@/lib/graphql/Query/SASTierComparison.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import { contentstackBaseProcedure, router } from "@/server/trpc"
|
import { contentstackBaseProcedure, router } from "@/server/trpc"
|
||||||
|
|
||||||
import { validateSasTierComparisonSchema } from "./output"
|
import { validateSasTierComparisonSchema } from "./output"
|
||||||
@@ -11,20 +11,16 @@ import { validateSasTierComparisonSchema } from "./output"
|
|||||||
import type { SasTierComparisonResponse } from "@/types/trpc/routers/contentstack/partner"
|
import type { SasTierComparisonResponse } from "@/types/trpc/routers/contentstack/partner"
|
||||||
import type { Context } from "@/server/context"
|
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) => {
|
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 tag = `${ctx.lang}:sas_tier_comparison`
|
||||||
const sasTierComparisonConfigResponse =
|
const sasTierComparisonConfigResponse =
|
||||||
@@ -38,17 +34,8 @@ export const getSasTierComparison = cache(async (ctx: Context) => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!sasTierComparisonConfigResponse.data) {
|
if (!sasTierComparisonConfigResponse.data) {
|
||||||
getSasTierComparisonFailCounter.add(1)
|
|
||||||
const notFoundError = notFound(sasTierComparisonConfigResponse)
|
const notFoundError = notFound(sasTierComparisonConfigResponse)
|
||||||
console.error(
|
metricsGetSasTierComparison.noDataError()
|
||||||
"contentstack.sas not found error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: {
|
|
||||||
lang: ctx.lang,
|
|
||||||
},
|
|
||||||
error: { code: notFoundError.code },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,21 +44,14 @@ export const getSasTierComparison = cache(async (ctx: Context) => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!validatedSasTierComparison.success) {
|
if (!validatedSasTierComparison.success) {
|
||||||
getSasTierComparisonFailCounter.add(1)
|
metricsGetSasTierComparison.validationError(
|
||||||
console.error(validatedSasTierComparison.error)
|
validatedSasTierComparison.error
|
||||||
console.error(
|
|
||||||
"contentstack.sas validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: {
|
|
||||||
lang: ctx.lang,
|
|
||||||
},
|
|
||||||
error: validatedSasTierComparison.error,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getSasTierComparisonSuccessCounter.add(1)
|
metricsGetSasTierComparison.success()
|
||||||
|
|
||||||
return validatedSasTierComparison.data
|
return validatedSasTierComparison.data
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as api from "@/lib/api"
|
import * as api from "@/lib/api"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import {
|
import {
|
||||||
contentStackBaseWithProtectedProcedure,
|
contentStackBaseWithProtectedProcedure,
|
||||||
contentStackBaseWithServiceProcedure,
|
contentStackBaseWithServiceProcedure,
|
||||||
@@ -22,24 +23,9 @@ import {
|
|||||||
} from "./input"
|
} from "./input"
|
||||||
import { validateCategorizedRewardsSchema } from "./output"
|
import { validateCategorizedRewardsSchema } from "./output"
|
||||||
import {
|
import {
|
||||||
getAllRewardCounter,
|
|
||||||
getAllRewardFailCounter,
|
|
||||||
getAllRewardSuccessCounter,
|
|
||||||
getByLevelRewardCounter,
|
|
||||||
getByLevelRewardFailCounter,
|
|
||||||
getByLevelRewardSuccessCounter,
|
|
||||||
getCachedAllTierRewards,
|
getCachedAllTierRewards,
|
||||||
getCmsRewards,
|
getCmsRewards,
|
||||||
getCurrentRewardCounter,
|
|
||||||
getCurrentRewardFailCounter,
|
|
||||||
getCurrentRewardSuccessCounter,
|
|
||||||
getRedeemCounter,
|
|
||||||
getRedeemFailCounter,
|
|
||||||
getRedeemSuccessCounter,
|
|
||||||
getUniqueRewardIds,
|
getUniqueRewardIds,
|
||||||
getUnwrapSurpriseCounter,
|
|
||||||
getUnwrapSurpriseFailCounter,
|
|
||||||
getUnwrapSurpriseSuccessCounter,
|
|
||||||
} from "./utils"
|
} from "./utils"
|
||||||
|
|
||||||
import type { BaseReward, Surprise } from "@/types/components/myPages/rewards"
|
import type { BaseReward, Surprise } from "@/types/components/myPages/rewards"
|
||||||
@@ -50,7 +36,14 @@ export const rewardQueryRouter = router({
|
|||||||
all: contentStackBaseWithServiceProcedure
|
all: contentStackBaseWithServiceProcedure
|
||||||
.input(rewardsAllInput)
|
.input(rewardsAllInput)
|
||||||
.query(async function ({ input, ctx }) {
|
.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)
|
const allApiRewards = await getCachedAllTierRewards(ctx.serviceToken)
|
||||||
|
|
||||||
@@ -75,16 +68,23 @@ export const rewardQueryRouter = router({
|
|||||||
const levelsWithRewards = Object.entries(allApiRewards).map(
|
const levelsWithRewards = Object.entries(allApiRewards).map(
|
||||||
([level, rewards]) => {
|
([level, rewards]) => {
|
||||||
const combinedRewards = rewards
|
const combinedRewards = rewards
|
||||||
.filter((r) => (input.unique ? r?.rewardTierLevel === level : true))
|
.filter((reward) =>
|
||||||
|
input.unique ? reward.rewardTierLevel === level : true
|
||||||
|
)
|
||||||
.map((reward) => {
|
.map((reward) => {
|
||||||
const contentStackReward = contentStackRewards.find((r) => {
|
const contentStackReward = contentStackRewards.find((r) => {
|
||||||
return r.reward_id === reward?.rewardId
|
return r.reward_id === reward.rewardId
|
||||||
})
|
})
|
||||||
|
|
||||||
if (contentStackReward) {
|
if (contentStackReward) {
|
||||||
return contentStackReward
|
return contentStackReward
|
||||||
} else {
|
} 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))
|
.filter((reward): reward is CMSReward => Boolean(reward))
|
||||||
@@ -94,9 +94,9 @@ export const rewardQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!levelConfig) {
|
if (!levelConfig) {
|
||||||
getAllRewardFailCounter.add(1)
|
metricsGetContentstackRewardAll.dataError(
|
||||||
|
`Failed to matched loyalty level between API and CMS for level ${level}`
|
||||||
console.error("contentstack.loyaltyLevels level not found")
|
)
|
||||||
throw notFound()
|
throw notFound()
|
||||||
}
|
}
|
||||||
const result: LevelWithRewards = {
|
const result: LevelWithRewards = {
|
||||||
@@ -107,21 +107,31 @@ export const rewardQueryRouter = router({
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
getAllRewardSuccessCounter.add(1)
|
metricsGetContentstackRewardAll.success()
|
||||||
|
|
||||||
return levelsWithRewards
|
return levelsWithRewards
|
||||||
}),
|
}),
|
||||||
byLevel: contentStackBaseWithServiceProcedure
|
byLevel: contentStackBaseWithServiceProcedure
|
||||||
.input(rewardsByLevelInput)
|
.input(rewardsByLevelInput)
|
||||||
.query(async function ({ input, ctx }) {
|
.query(async function ({ input, ctx }) {
|
||||||
getByLevelRewardCounter.add(1)
|
|
||||||
const { level_id } = input
|
const { level_id } = input
|
||||||
|
|
||||||
|
const getRewardByLevelCounter = createCounter(
|
||||||
|
"trpc.contentstack",
|
||||||
|
"reward.byLevel"
|
||||||
|
)
|
||||||
|
const metricsGetRewardByLevel = getRewardByLevelCounter.init({
|
||||||
|
level_id,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetRewardByLevel.start()
|
||||||
|
|
||||||
const allUpcomingApiRewards = await getCachedAllTierRewards(
|
const allUpcomingApiRewards = await getCachedAllTierRewards(
|
||||||
ctx.serviceToken
|
ctx.serviceToken
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!allUpcomingApiRewards || !allUpcomingApiRewards[level_id]) {
|
if (!allUpcomingApiRewards || !allUpcomingApiRewards[level_id]) {
|
||||||
getByLevelRewardFailCounter.add(1)
|
metricsGetRewardByLevel.noDataError()
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -150,24 +160,36 @@ export const rewardQueryRouter = router({
|
|||||||
const levelsWithRewards = apiRewards
|
const levelsWithRewards = apiRewards
|
||||||
.map((reward) => {
|
.map((reward) => {
|
||||||
const contentStackReward = contentStackRewards.find((r) => {
|
const contentStackReward = contentStackRewards.find((r) => {
|
||||||
return r.reward_id === reward?.rewardId
|
return r.reward_id === reward.rewardId
|
||||||
})
|
})
|
||||||
|
|
||||||
if (contentStackReward) {
|
if (contentStackReward) {
|
||||||
return contentStackReward
|
return contentStackReward
|
||||||
} else {
|
} 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))
|
.filter((reward): reward is CMSReward => Boolean(reward))
|
||||||
|
|
||||||
getByLevelRewardSuccessCounter.add(1)
|
metricsGetRewardByLevel.success()
|
||||||
|
|
||||||
return { level: loyaltyLevelsConfig, rewards: levelsWithRewards }
|
return { level: loyaltyLevelsConfig, rewards: levelsWithRewards }
|
||||||
}),
|
}),
|
||||||
current: contentStackBaseWithProtectedProcedure
|
current: contentStackBaseWithProtectedProcedure
|
||||||
.input(langInput.optional()) // lang is required for client, but not for server
|
.input(langInput.optional()) // lang is required for client, but not for server
|
||||||
.query(async function ({ ctx }) {
|
.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(
|
const apiResponse = await api.get(
|
||||||
api.endpoints.v1.Profile.Reward.reward,
|
api.endpoints.v1.Profile.Reward.reward,
|
||||||
@@ -179,25 +201,7 @@ export const rewardQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsGetCurrentReward.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,19 +211,7 @@ export const rewardQueryRouter = router({
|
|||||||
validateCategorizedRewardsSchema.safeParse(data)
|
validateCategorizedRewardsSchema.safeParse(data)
|
||||||
|
|
||||||
if (!validatedApiRewards.success) {
|
if (!validatedApiRewards.success) {
|
||||||
getCurrentRewardFailCounter.add(1, {
|
metricsGetCurrentReward.validationError(validatedApiRewards.error)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,14 +235,20 @@ export const rewardQueryRouter = router({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
getCurrentRewardSuccessCounter.add(1)
|
metricsGetCurrentReward.success()
|
||||||
|
|
||||||
return { rewards }
|
return { rewards }
|
||||||
}),
|
}),
|
||||||
surprises: contentStackBaseWithProtectedProcedure
|
surprises: contentStackBaseWithProtectedProcedure
|
||||||
.input(langInput.optional()) // lang is required for client, but not for server
|
.input(langInput.optional()) // lang is required for client, but not for server
|
||||||
.query(async ({ ctx }) => {
|
.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
|
const endpoint = api.endpoints.v1.Profile.Reward.reward
|
||||||
|
|
||||||
@@ -262,25 +260,7 @@ export const rewardQueryRouter = router({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsGetSurprises.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,19 +269,7 @@ export const rewardQueryRouter = router({
|
|||||||
validateCategorizedRewardsSchema.safeParse(data)
|
validateCategorizedRewardsSchema.safeParse(data)
|
||||||
|
|
||||||
if (!validatedApiRewards.success) {
|
if (!validatedApiRewards.success) {
|
||||||
getCurrentRewardFailCounter.add(1, {
|
metricsGetSurprises.validationError(validatedApiRewards.error)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,8 +284,6 @@ export const rewardQueryRouter = router({
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentRewardSuccessCounter.add(1)
|
|
||||||
|
|
||||||
const surprises: Surprise[] = cmsRewards
|
const surprises: Surprise[] = cmsRewards
|
||||||
.map((cmsReward) => {
|
.map((cmsReward) => {
|
||||||
// Non-null assertion is used here because we know our reward exist
|
// Non-null assertion is used here because we know our reward exist
|
||||||
@@ -336,15 +302,32 @@ export const rewardQueryRouter = router({
|
|||||||
})
|
})
|
||||||
.flatMap((surprises) => (surprises ? [surprises] : []))
|
.flatMap((surprises) => (surprises ? [surprises] : []))
|
||||||
|
|
||||||
|
metricsGetSurprises.success()
|
||||||
|
|
||||||
return surprises
|
return surprises
|
||||||
}),
|
}),
|
||||||
unwrap: protectedProcedure
|
unwrap: protectedProcedure
|
||||||
.input(rewardsUpdateInput)
|
.input(rewardsUpdateInput)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.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 }) => {
|
const metricsGetUnwrapSurprise = getUnwrapSurpriseCounter.init({
|
||||||
return api.post(api.endpoints.v1.Profile.Reward.unwrap, {
|
rewardId,
|
||||||
|
couponCode,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetUnwrapSurprise.start()
|
||||||
|
|
||||||
|
const apiResponse = await api.post(
|
||||||
|
api.endpoints.v1.Profile.Reward.unwrap,
|
||||||
|
{
|
||||||
body: {
|
body: {
|
||||||
rewardId,
|
rewardId,
|
||||||
couponCode,
|
couponCode,
|
||||||
@@ -352,57 +335,47 @@ export const rewardQueryRouter = router({
|
|||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
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,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
|
|
||||||
console.error(
|
|
||||||
"contentstack.unwrap API error",
|
|
||||||
JSON.stringify({
|
|
||||||
error: {
|
|
||||||
status: apiResponse.status,
|
|
||||||
statusText: apiResponse.statusText,
|
|
||||||
text,
|
|
||||||
},
|
|
||||||
query: {},
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (!apiResponse.ok) {
|
||||||
|
metricsGetUnwrapSurprise.httpError(apiResponse)
|
||||||
return false
|
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
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getUnwrapSurpriseSuccessCounter.add(1)
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}),
|
}),
|
||||||
redeem: protectedProcedure
|
redeem: protectedProcedure
|
||||||
.input(rewardsRedeemInput)
|
.input(rewardsRedeemInput)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
getRedeemCounter.add(1)
|
|
||||||
|
|
||||||
const { rewardId, couponCode } = input
|
const { rewardId, couponCode } = input
|
||||||
|
|
||||||
|
const getRedeemCounter = createCounter(
|
||||||
|
"trpc.contentstack",
|
||||||
|
"reward.redeem"
|
||||||
|
)
|
||||||
|
|
||||||
|
const metricGetRedeem = getRedeemCounter.init({ rewardId, couponCode })
|
||||||
|
|
||||||
|
metricGetRedeem.start()
|
||||||
|
|
||||||
const apiResponse = await api.post(
|
const apiResponse = await api.post(
|
||||||
api.endpoints.v1.Profile.Reward.redeem,
|
api.endpoints.v1.Profile.Reward.redeem,
|
||||||
{
|
{
|
||||||
@@ -417,29 +390,11 @@ export const rewardQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
metricGetRedeem.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getRedeemSuccessCounter.add(1)
|
metricGetRedeem.success()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { metrics } from "@opentelemetry/api"
|
|
||||||
|
|
||||||
import * as api from "@/lib/api"
|
import * as api from "@/lib/api"
|
||||||
import {
|
import {
|
||||||
GetRewards as GetRewards,
|
GetRewards as GetRewards,
|
||||||
@@ -7,6 +5,7 @@ import {
|
|||||||
} from "@/lib/graphql/Query/RewardsWithRedeem.graphql"
|
} from "@/lib/graphql/Query/RewardsWithRedeem.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
|
|
||||||
import { getCacheClient } from "@/services/dataCache"
|
import { getCacheClient } from "@/services/dataCache"
|
||||||
import {
|
import {
|
||||||
@@ -26,63 +25,6 @@ import type {
|
|||||||
} from "@/types/trpc/routers/contentstack/reward"
|
} from "@/types/trpc/routers/contentstack/reward"
|
||||||
import type { Lang } from "@/constants/languages"
|
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[]) {
|
export function getUniqueRewardIds(rewardIds: string[]) {
|
||||||
const uniqueRewardIds = new Set(rewardIds)
|
const uniqueRewardIds = new Set(rewardIds)
|
||||||
return Array.from(uniqueRewardIds)
|
return Array.from(uniqueRewardIds)
|
||||||
@@ -97,6 +39,14 @@ export async function getCachedAllTierRewards(token: string) {
|
|||||||
return await cacheClient.cacheOrGet(
|
return await cacheClient.cacheOrGet(
|
||||||
"getAllTierRewards",
|
"getAllTierRewards",
|
||||||
async () => {
|
async () => {
|
||||||
|
const getApiRewardAllTiersCounter = createCounter(
|
||||||
|
"trpc.api",
|
||||||
|
"reward.allTiers"
|
||||||
|
)
|
||||||
|
const metricsGetApiRewardAllTiers = getApiRewardAllTiersCounter.init()
|
||||||
|
|
||||||
|
metricsGetApiRewardAllTiers.start()
|
||||||
|
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
api.endpoints.v1.Profile.Reward.allTiers,
|
api.endpoints.v1.Profile.Reward.allTiers,
|
||||||
{
|
{
|
||||||
@@ -107,26 +57,7 @@ export async function getCachedAllTierRewards(token: string) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
metricsGetApiRewardAllTiers.httpError(apiResponse)
|
||||||
getAllRewardFailCounter.add(1, {
|
|
||||||
error_type: "http_error",
|
|
||||||
error: JSON.stringify({
|
|
||||||
status: apiResponse.status,
|
|
||||||
statusText: apiResponse.statusText,
|
|
||||||
text,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"api.rewards.allTiers error ",
|
|
||||||
JSON.stringify({
|
|
||||||
error: {
|
|
||||||
status: apiResponse.status,
|
|
||||||
statusText: apiResponse.statusText,
|
|
||||||
text,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
throw apiResponse
|
throw apiResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,20 +66,14 @@ export async function getCachedAllTierRewards(token: string) {
|
|||||||
validateApiAllTiersSchema.safeParse(data)
|
validateApiAllTiersSchema.safeParse(data)
|
||||||
|
|
||||||
if (!validatedApiAllTierRewards.success) {
|
if (!validatedApiAllTierRewards.success) {
|
||||||
getAllRewardFailCounter.add(1, {
|
metricsGetApiRewardAllTiers.validationError(
|
||||||
error_type: "validation_error",
|
validatedApiAllTierRewards.error
|
||||||
error: JSON.stringify(validatedApiAllTierRewards.error),
|
|
||||||
})
|
|
||||||
console.error(validatedApiAllTierRewards.error)
|
|
||||||
console.error(
|
|
||||||
"api.rewards validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
error: validatedApiAllTierRewards.error,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
throw validatedApiAllTierRewards.error
|
throw validatedApiAllTierRewards.error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
metricsGetApiRewardAllTiers.success()
|
||||||
|
|
||||||
return validatedApiAllTierRewards.data
|
return validatedApiAllTierRewards.data
|
||||||
},
|
},
|
||||||
"1h"
|
"1h"
|
||||||
@@ -164,13 +89,15 @@ export async function getCmsRewards(lang: Lang, rewardIds: string[]) {
|
|||||||
generateLoyaltyConfigTag(lang, "reward", id)
|
generateLoyaltyConfigTag(lang, "reward", id)
|
||||||
)
|
)
|
||||||
|
|
||||||
getAllCMSRewardRefsCounter.add(1, { lang, rewardIds })
|
const getContentstackRewardAllRefsCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.reward.refs start",
|
"reward.all.refs"
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, rewardIds },
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetContentstackRewardAllRefs =
|
||||||
|
getContentstackRewardAllRefsCounter.init({ lang, rewardIds })
|
||||||
|
|
||||||
|
metricsGetContentstackRewardAllRefs.start()
|
||||||
|
|
||||||
const refsResponse = await request<GetRewardRefsSchema>(
|
const refsResponse = await request<GetRewardRefsSchema>(
|
||||||
GetRewardsRef,
|
GetRewardsRef,
|
||||||
{
|
{
|
||||||
@@ -182,50 +109,30 @@ export async function getCmsRewards(lang: Lang, rewardIds: string[]) {
|
|||||||
ttl: "max",
|
ttl: "max",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!refsResponse.data) {
|
if (!refsResponse.data) {
|
||||||
const notFoundError = notFound(refsResponse)
|
const notFoundError = notFound(refsResponse)
|
||||||
getAllCMSRewardRefsFailCounter.add(1, {
|
metricsGetContentstackRewardAllRefs.noDataError()
|
||||||
lang,
|
|
||||||
rewardIds,
|
|
||||||
error_type: "not_found",
|
|
||||||
error: JSON.stringify({ code: notFoundError.code }),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.reward.refs not found error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, rewardIds },
|
|
||||||
error: { code: notFoundError.code },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
const validatedRefsData = rewardRefsSchema.safeParse(refsResponse)
|
const validatedRefsData = rewardRefsSchema.safeParse(refsResponse)
|
||||||
|
|
||||||
if (!validatedRefsData.success) {
|
if (!validatedRefsData.success) {
|
||||||
getAllCMSRewardRefsFailCounter.add(1, {
|
metricsGetContentstackRewardAllRefs.validationError(validatedRefsData.error)
|
||||||
lang,
|
|
||||||
rewardIds,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedRefsData.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"contentstack.reward.refs validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, rewardIds },
|
|
||||||
error: validatedRefsData.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllCMSRewardRefsSuccessCounter.add(1, { lang, rewardIds })
|
metricsGetContentstackRewardAllRefs.success()
|
||||||
console.info(
|
|
||||||
"contentstack.startPage.refs success",
|
const getContentstackRewardAllCounter = createCounter(
|
||||||
JSON.stringify({
|
"trpc.contentstack",
|
||||||
query: { lang, rewardIds },
|
"reward.all"
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetContentstackRewardAll = getContentstackRewardAllCounter.init({
|
||||||
|
lang,
|
||||||
|
rewardIds,
|
||||||
|
})
|
||||||
|
|
||||||
const cmsRewardsResponse = await request<CMSRewardsResponse>(
|
const cmsRewardsResponse = await request<CMSRewardsResponse>(
|
||||||
GetRewards,
|
GetRewards,
|
||||||
@@ -240,22 +147,8 @@ export async function getCmsRewards(lang: Lang, rewardIds: string[]) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!cmsRewardsResponse.data) {
|
if (!cmsRewardsResponse.data) {
|
||||||
getAllRewardFailCounter.add(1, {
|
|
||||||
lang,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(cmsRewardsResponse.data),
|
|
||||||
})
|
|
||||||
const notFoundError = notFound(cmsRewardsResponse)
|
const notFoundError = notFound(cmsRewardsResponse)
|
||||||
console.error(
|
metricsGetContentstackRewardAll.noDataError()
|
||||||
"contentstack.rewards not found error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: {
|
|
||||||
locale: lang,
|
|
||||||
rewardIds,
|
|
||||||
},
|
|
||||||
error: { code: notFoundError.code },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,22 +156,11 @@ export async function getCmsRewards(lang: Lang, rewardIds: string[]) {
|
|||||||
validateCmsRewardsSchema.safeParse(cmsRewardsResponse)
|
validateCmsRewardsSchema.safeParse(cmsRewardsResponse)
|
||||||
|
|
||||||
if (!validatedCmsRewards.success) {
|
if (!validatedCmsRewards.success) {
|
||||||
getAllRewardFailCounter.add(1, {
|
metricsGetContentstackRewardAll.validationError(validatedCmsRewards.error)
|
||||||
locale: lang,
|
|
||||||
rewardIds,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedCmsRewards.error),
|
|
||||||
})
|
|
||||||
console.error(validatedCmsRewards.error)
|
|
||||||
console.error(
|
|
||||||
"contentstack.rewards validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { locale: lang, rewardIds },
|
|
||||||
error: validatedCmsRewards.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
metricsGetContentstackRewardAll.success()
|
||||||
|
|
||||||
return validatedCmsRewards.data
|
return validatedCmsRewards.data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
} from "@/lib/graphql/Query/StartPage/StartPage.graphql"
|
} from "@/lib/graphql/Query/StartPage/StartPage.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -13,14 +14,6 @@ import {
|
|||||||
} from "@/utils/generateTag"
|
} from "@/utils/generateTag"
|
||||||
|
|
||||||
import { startPageRefsSchema, startPageSchema } from "./output"
|
import { startPageRefsSchema, startPageSchema } from "./output"
|
||||||
import {
|
|
||||||
getStartPageCounter,
|
|
||||||
getStartPageFailCounter,
|
|
||||||
getStartPageRefsCounter,
|
|
||||||
getStartPageRefsFailCounter,
|
|
||||||
getStartPageRefsSuccessCounter,
|
|
||||||
getStartPageSuccessCounter,
|
|
||||||
} from "./telemetry"
|
|
||||||
import { getConnections } from "./utils"
|
import { getConnections } from "./utils"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -36,13 +29,14 @@ export const startPageQueryRouter = router({
|
|||||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
||||||
const { lang, uid } = ctx
|
const { lang, uid } = ctx
|
||||||
|
|
||||||
getStartPageRefsCounter.add(1, { lang, uid: `${uid}` })
|
const getStartPageRefsCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.startPage.refs start",
|
"startPage.get.refs"
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetStartPageRefs = getStartPageRefsCounter.init({ lang, uid })
|
||||||
|
|
||||||
|
metricsGetStartPageRefs.start()
|
||||||
|
|
||||||
const refsResponse = await request<GetStartPageRefsSchema>(
|
const refsResponse = await request<GetStartPageRefsSchema>(
|
||||||
GetStartPageRefs,
|
GetStartPageRefs,
|
||||||
{
|
{
|
||||||
@@ -56,56 +50,26 @@ export const startPageQueryRouter = router({
|
|||||||
)
|
)
|
||||||
if (!refsResponse.data) {
|
if (!refsResponse.data) {
|
||||||
const notFoundError = notFound(refsResponse)
|
const notFoundError = notFound(refsResponse)
|
||||||
getStartPageRefsFailCounter.add(1, {
|
metricsGetStartPageRefs.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
const validatedRefsData = startPageRefsSchema.safeParse(refsResponse.data)
|
const validatedRefsData = startPageRefsSchema.safeParse(refsResponse.data)
|
||||||
|
|
||||||
if (!validatedRefsData.success) {
|
if (!validatedRefsData.success) {
|
||||||
getStartPageRefsFailCounter.add(1, {
|
metricsGetStartPageRefs.validationError(validatedRefsData.error)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getStartPageRefsSuccessCounter.add(1, { lang, uid: `${uid}` })
|
metricsGetStartPageRefs.success()
|
||||||
console.info(
|
|
||||||
"contentstack.startPage.refs success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
getStartPageCounter.add(1, { lang, uid: `${uid}` })
|
const getStartPageCounter = createCounter(
|
||||||
console.info(
|
"trpc.contentstack",
|
||||||
"contentstack.startPage start",
|
"startPage.get"
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetStartPage = getStartPageCounter.init({ lang, uid })
|
||||||
|
|
||||||
|
metricsGetStartPage.start()
|
||||||
|
|
||||||
const connections = getConnections(validatedRefsData.data)
|
const connections = getConnections(validatedRefsData.data)
|
||||||
|
|
||||||
@@ -113,6 +77,7 @@ export const startPageQueryRouter = router({
|
|||||||
generateTagsFromSystem(lang, connections),
|
generateTagsFromSystem(lang, connections),
|
||||||
generateTag(lang, validatedRefsData.data.start_page.system.uid),
|
generateTag(lang, validatedRefsData.data.start_page.system.uid),
|
||||||
].flat()
|
].flat()
|
||||||
|
|
||||||
const response = await request<GetStartPageData>(
|
const response = await request<GetStartPageData>(
|
||||||
GetStartPage,
|
GetStartPage,
|
||||||
{
|
{
|
||||||
@@ -127,48 +92,18 @@ export const startPageQueryRouter = router({
|
|||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
const notFoundError = notFound(response)
|
const notFoundError = notFound(response)
|
||||||
getStartPageFailCounter.add(1, {
|
metricsGetStartPage.noDataError()
|
||||||
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 },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw notFoundError
|
throw notFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
const startPage = startPageSchema.safeParse(response.data)
|
const startPage = startPageSchema.safeParse(response.data)
|
||||||
|
|
||||||
if (!startPage.success) {
|
if (!startPage.success) {
|
||||||
getStartPageFailCounter.add(1, {
|
metricsGetStartPage.validationError(startPage.error)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getStartPageSuccessCounter.add(1, { lang, uid: `${uid}` })
|
metricsGetStartPage.success()
|
||||||
console.info(
|
|
||||||
"contentstack.startPage success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { lang, uid },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const system = startPage.data.start_page.system
|
const system = startPage.data.start_page.system
|
||||||
const tracking: TrackingSDKPageData = {
|
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 { dt } from "@/lib/dt"
|
||||||
import { badRequestError, unauthorizedError } from "@/server/errors/trpc"
|
import { badRequestError, unauthorizedError } from "@/server/errors/trpc"
|
||||||
import { getCityPageUrls } from "@/server/routers/contentstack/destinationCityPage/utils"
|
import { getCityPageUrls } from "@/server/routers/contentstack/destinationCityPage/utils"
|
||||||
|
import { getVerifiedUser } from "@/server/routers/user/utils"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import {
|
import {
|
||||||
contentStackBaseWithServiceProcedure,
|
contentStackBaseWithServiceProcedure,
|
||||||
publicProcedure,
|
publicProcedure,
|
||||||
@@ -17,7 +19,6 @@ import { toApiLang } from "@/server/utils"
|
|||||||
import { getCacheClient } from "@/services/dataCache"
|
import { getCacheClient } from "@/services/dataCache"
|
||||||
|
|
||||||
import { getHotelPageUrls } from "../contentstack/hotelPage/utils"
|
import { getHotelPageUrls } from "../contentstack/hotelPage/utils"
|
||||||
import { getVerifiedUser } from "../user/query"
|
|
||||||
import { additionalDataSchema } from "./schemas/hotel/include/additionalData"
|
import { additionalDataSchema } from "./schemas/hotel/include/additionalData"
|
||||||
import { meetingRoomsSchema } from "./schemas/meetingRoom"
|
import { meetingRoomsSchema } from "./schemas/meetingRoom"
|
||||||
import {
|
import {
|
||||||
@@ -42,17 +43,11 @@ import {
|
|||||||
selectRateRoomAvailabilityInputSchema,
|
selectRateRoomAvailabilityInputSchema,
|
||||||
selectRateRoomsAvailabilityInputSchema,
|
selectRateRoomsAvailabilityInputSchema,
|
||||||
} from "./input"
|
} from "./input"
|
||||||
import { metrics } from "./metrics"
|
|
||||||
import {
|
import {
|
||||||
ancillaryPackagesSchema,
|
ancillaryPackagesSchema,
|
||||||
breakfastPackagesSchema,
|
breakfastPackagesSchema,
|
||||||
getNearbyHotelIdsSchema,
|
getNearbyHotelIdsSchema,
|
||||||
} from "./output"
|
} from "./output"
|
||||||
import {
|
|
||||||
locationsUrlsCounter,
|
|
||||||
locationsUrlsFailCounter,
|
|
||||||
locationsUrlsSuccessCounter,
|
|
||||||
} from "./telemetry"
|
|
||||||
import {
|
import {
|
||||||
getBedTypes,
|
getBedTypes,
|
||||||
getCitiesByCountry,
|
getCitiesByCountry,
|
||||||
@@ -431,24 +426,11 @@ export const hotelQueryRouter = router({
|
|||||||
const { lang } = ctx
|
const { lang } = ctx
|
||||||
const apiLang = toApiLang(lang)
|
const apiLang = toApiLang(lang)
|
||||||
|
|
||||||
metrics.hotelsAvailabilityBookingCode.counter.add(1, {
|
|
||||||
...input,
|
|
||||||
})
|
|
||||||
const bookingCodeAvailabilityResponse =
|
const bookingCodeAvailabilityResponse =
|
||||||
await getHotelsAvailabilityByCity(input, apiLang, ctx.serviceToken)
|
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.
|
// Get regular availability of hotels which don't have availability with booking code.
|
||||||
const unavailableHotelIds =
|
const unavailableHotelIds = bookingCodeAvailabilityResponse.availability
|
||||||
bookingCodeAvailabilityResponse?.availability
|
|
||||||
.filter((hotel) => {
|
.filter((hotel) => {
|
||||||
return hotel.status === "NotAvailable"
|
return hotel.status === "NotAvailable"
|
||||||
})
|
})
|
||||||
@@ -461,6 +443,7 @@ export const hotelQueryRouter = router({
|
|||||||
if (!unavailableHotelIds || !unavailableHotelIds.length) {
|
if (!unavailableHotelIds || !unavailableHotelIds.length) {
|
||||||
return bookingCodeAvailabilityResponse
|
return bookingCodeAvailabilityResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
const unavailableHotelsInput = {
|
const unavailableHotelsInput = {
|
||||||
...input,
|
...input,
|
||||||
bookingCode: "",
|
bookingCode: "",
|
||||||
@@ -472,11 +455,6 @@ export const hotelQueryRouter = router({
|
|||||||
ctx.serviceToken
|
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).
|
// No regular rates available due to network or API failure (no need to filter & merge).
|
||||||
if (!unavailableHotels) {
|
if (!unavailableHotels) {
|
||||||
return bookingCodeAvailabilityResponse
|
return bookingCodeAvailabilityResponse
|
||||||
@@ -537,19 +515,16 @@ export const hotelQueryRouter = router({
|
|||||||
const language = ctx.lang
|
const language = ctx.lang
|
||||||
let hotelsToFetch: string[] = []
|
let hotelsToFetch: string[] = []
|
||||||
|
|
||||||
metrics.hotels.counter.add(1, {
|
const getHotelsByCSFilterCounter = createCounter(
|
||||||
input: JSON.stringify(input),
|
"trpc.hotel.hotels",
|
||||||
language,
|
"byCSFilter"
|
||||||
})
|
|
||||||
console.info(
|
|
||||||
"api.hotel.hotels start",
|
|
||||||
JSON.stringify({
|
|
||||||
query: {
|
|
||||||
...input,
|
|
||||||
language,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
const metricsGetHotelsByCSFilter = getHotelsByCSFilterCounter.init({
|
||||||
|
input,
|
||||||
|
language,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetHotelsByCSFilter.start()
|
||||||
|
|
||||||
if (hotelsToInclude.length) {
|
if (hotelsToInclude.length) {
|
||||||
hotelsToFetch = hotelsToInclude
|
hotelsToFetch = hotelsToInclude
|
||||||
@@ -571,20 +546,13 @@ export const hotelQueryRouter = router({
|
|||||||
.find((loc) => loc.cityIdentifier === locationFilter.city)?.id
|
.find((loc) => loc.cityIdentifier === locationFilter.city)?.id
|
||||||
|
|
||||||
if (!cityId) {
|
if (!cityId) {
|
||||||
metrics.hotels.fail.add(1, {
|
metricsGetHotelsByCSFilter.dataError(
|
||||||
input: JSON.stringify(input),
|
`CityId not found for cityIdentifier: ${locationFilter.city}`,
|
||||||
language,
|
{
|
||||||
error_type: "not_found",
|
cityIdentifier: locationFilter.city,
|
||||||
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}`,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -594,20 +562,13 @@ export const hotelQueryRouter = router({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!hotelIds?.length) {
|
if (!hotelIds?.length) {
|
||||||
metrics.hotels.fail.add(1, {
|
metricsGetHotelsByCSFilter.dataError(
|
||||||
|
`No hotelIds found for cityId: ${cityId}`,
|
||||||
|
{
|
||||||
cityId,
|
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}`,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -623,20 +584,13 @@ export const hotelQueryRouter = router({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!hotelIds?.length) {
|
if (!hotelIds?.length) {
|
||||||
metrics.hotels.fail.add(1, {
|
metricsGetHotelsByCSFilter.dataError(
|
||||||
|
`No hotelIds found for country: ${locationFilter.country}`,
|
||||||
|
{
|
||||||
country: locationFilter.country,
|
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}`,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -648,20 +602,11 @@ export const hotelQueryRouter = router({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!hotelsToFetch.length) {
|
if (!hotelsToFetch.length) {
|
||||||
metrics.hotels.fail.add(1, {
|
metricsGetHotelsByCSFilter.dataError(
|
||||||
input: JSON.stringify(input),
|
`Couldn't find any hotels for given input: ${JSON.stringify(input)}`,
|
||||||
language,
|
input
|
||||||
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)}`,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
const hotelPages = await getHotelPageUrls(language)
|
const hotelPages = await getHotelPageUrls(language)
|
||||||
@@ -684,20 +629,7 @@ export const hotelQueryRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
metrics.hotels.success.add(1, {
|
metricsGetHotelsByCSFilter.success()
|
||||||
input: JSON.stringify(input),
|
|
||||||
language,
|
|
||||||
})
|
|
||||||
|
|
||||||
console.info(
|
|
||||||
"api.hotels success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: {
|
|
||||||
input: JSON.stringify(input),
|
|
||||||
language,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return hotels.filter((hotel): hotel is HotelDataWithUrl => !!hotel)
|
return hotels.filter((hotel): hotel is HotelDataWithUrl => !!hotel)
|
||||||
}),
|
}),
|
||||||
@@ -769,13 +701,17 @@ export const hotelQueryRouter = router({
|
|||||||
return cacheClient.cacheOrGet(
|
return cacheClient.cacheOrGet(
|
||||||
`${apiLang}:nearbyHotels:${hotelId}`,
|
`${apiLang}:nearbyHotels:${hotelId}`,
|
||||||
async () => {
|
async () => {
|
||||||
metrics.nearbyHotelIds.counter.add(1, {
|
const nearbyHotelsCounter = createCounter(
|
||||||
|
"trpc.hotel",
|
||||||
|
"nearbyHotelIds"
|
||||||
|
)
|
||||||
|
const metricsNearbyHotels = nearbyHotelsCounter.init({
|
||||||
|
params,
|
||||||
hotelId,
|
hotelId,
|
||||||
})
|
})
|
||||||
console.info(
|
|
||||||
"api.hotels.nearbyHotelIds start",
|
metricsNearbyHotels.start()
|
||||||
JSON.stringify({ query: { hotelId, params } })
|
|
||||||
)
|
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
api.endpoints.v1.Hotel.Hotels.nearbyHotels(hotelId),
|
api.endpoints.v1.Hotel.Hotels.nearbyHotels(hotelId),
|
||||||
{
|
{
|
||||||
@@ -786,55 +722,18 @@ export const hotelQueryRouter = router({
|
|||||||
params
|
params
|
||||||
)
|
)
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsNearbyHotels.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiJson = await apiResponse.json()
|
const apiJson = await apiResponse.json()
|
||||||
const validateHotelData = getNearbyHotelIdsSchema.safeParse(apiJson)
|
const validateHotelData = getNearbyHotelIdsSchema.safeParse(apiJson)
|
||||||
if (!validateHotelData.success) {
|
if (!validateHotelData.success) {
|
||||||
metrics.nearbyHotelIds.fail.add(1, {
|
metricsNearbyHotels.validationError(validateHotelData.error)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw badRequestError()
|
throw badRequestError()
|
||||||
}
|
}
|
||||||
metrics.nearbyHotelIds.success.add(1, {
|
|
||||||
hotelId,
|
metricsNearbyHotels.success()
|
||||||
})
|
|
||||||
console.info(
|
|
||||||
"api.hotels.nearbyHotelIds success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { hotelId, params },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return validateHotelData.data.map((id: string) => parseInt(id, 10))
|
return validateHotelData.data.map((id: string) => parseInt(id, 10))
|
||||||
},
|
},
|
||||||
@@ -884,16 +783,17 @@ export const hotelQueryRouter = router({
|
|||||||
urls: publicProcedure
|
urls: publicProcedure
|
||||||
.input(getLocationsUrlsInput)
|
.input(getLocationsUrlsInput)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
const procedureName = "hotels.locations.urls"
|
|
||||||
|
|
||||||
const { lang } = input
|
const { lang } = input
|
||||||
|
|
||||||
locationsUrlsCounter.add(1, { lang })
|
const locationsUrlsCounter = createCounter(
|
||||||
|
"trpc.hotel.locations",
|
||||||
console.info(
|
"urls"
|
||||||
`${procedureName}: start`,
|
|
||||||
JSON.stringify({ query: { lang } })
|
|
||||||
)
|
)
|
||||||
|
const metricsLocationsUrls = locationsUrlsCounter.init({
|
||||||
|
lang,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsLocationsUrls.start()
|
||||||
|
|
||||||
const [hotelPageUrlsResult, cityPageUrlsResult] =
|
const [hotelPageUrlsResult, cityPageUrlsResult] =
|
||||||
await Promise.allSettled([
|
await Promise.allSettled([
|
||||||
@@ -905,32 +805,15 @@ export const hotelQueryRouter = router({
|
|||||||
hotelPageUrlsResult.status === "rejected" ||
|
hotelPageUrlsResult.status === "rejected" ||
|
||||||
cityPageUrlsResult.status === "rejected"
|
cityPageUrlsResult.status === "rejected"
|
||||||
) {
|
) {
|
||||||
locationsUrlsFailCounter.add(1, {
|
metricsLocationsUrls.dataError(`Failed to get data for page URLs`, {
|
||||||
lang,
|
|
||||||
error_type: "no_data",
|
|
||||||
response: JSON.stringify({
|
|
||||||
hotelPageUrlsResult,
|
hotelPageUrlsResult,
|
||||||
cityPageUrlsResult,
|
cityPageUrlsResult,
|
||||||
}),
|
|
||||||
})
|
|
||||||
|
|
||||||
console.error(`${procedureName}: no data`, {
|
|
||||||
variables: { lang },
|
|
||||||
error_type: "no_data",
|
|
||||||
response: {
|
|
||||||
hotelPageUrlsResult,
|
|
||||||
cityPageUrlsResult,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
locationsUrlsSuccessCounter.add(1, { lang })
|
metricsLocationsUrls.success()
|
||||||
|
|
||||||
console.info(`${procedureName}: success`, {
|
|
||||||
variables: { lang },
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hotels: hotelPageUrlsResult.value,
|
hotels: hotelPageUrlsResult.value,
|
||||||
@@ -992,12 +875,12 @@ export const hotelQueryRouter = router({
|
|||||||
hotelId,
|
hotelId,
|
||||||
language,
|
language,
|
||||||
}
|
}
|
||||||
const metricsData = { ...params, hotelId: input.hotelId }
|
const meetingRoomsCounter = createCounter("trpc.hotel", "meetingRooms")
|
||||||
metrics.meetingRooms.counter.add(1, metricsData)
|
const metricsMeetingRooms = meetingRoomsCounter.init({
|
||||||
console.info(
|
params,
|
||||||
"api.hotels.meetingRooms start",
|
})
|
||||||
JSON.stringify({ query: { hotelId, params } })
|
|
||||||
)
|
metricsMeetingRooms.start()
|
||||||
|
|
||||||
const cacheClient = await getCacheClient()
|
const cacheClient = await getCacheClient()
|
||||||
return cacheClient.cacheOrGet(
|
return cacheClient.cacheOrGet(
|
||||||
@@ -1014,28 +897,7 @@ export const hotelQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsMeetingRooms.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
throw new Error("Failed to fetch meeting rooms")
|
throw new Error("Failed to fetch meeting rooms")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1043,22 +905,10 @@ export const hotelQueryRouter = router({
|
|||||||
const validatedMeetingRooms = meetingRoomsSchema.safeParse(apiJson)
|
const validatedMeetingRooms = meetingRoomsSchema.safeParse(apiJson)
|
||||||
|
|
||||||
if (!validatedMeetingRooms.success) {
|
if (!validatedMeetingRooms.success) {
|
||||||
console.error(
|
metricsMeetingRooms.validationError(validatedMeetingRooms.error)
|
||||||
"api.hotels.meetingRooms validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { params },
|
|
||||||
error: validatedMeetingRooms.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw badRequestError()
|
throw badRequestError()
|
||||||
}
|
}
|
||||||
metrics.meetingRooms.success.add(1, {
|
metricsMeetingRooms.success()
|
||||||
hotelId,
|
|
||||||
})
|
|
||||||
console.info(
|
|
||||||
"api.hotels.meetingRooms success",
|
|
||||||
JSON.stringify({ query: { params } })
|
|
||||||
)
|
|
||||||
|
|
||||||
return validatedMeetingRooms.data.data
|
return validatedMeetingRooms.data.data
|
||||||
},
|
},
|
||||||
@@ -1074,12 +924,16 @@ export const hotelQueryRouter = router({
|
|||||||
hotelId,
|
hotelId,
|
||||||
language,
|
language,
|
||||||
}
|
}
|
||||||
const metricsData = { ...params, hotelId: input.hotelId }
|
|
||||||
metrics.additionalData.counter.add(1, metricsData)
|
const additionalDataCounter = createCounter(
|
||||||
console.info(
|
"trpc.hotel",
|
||||||
"api.hotels.additionalData start",
|
"additionalData"
|
||||||
JSON.stringify({ query: { hotelId, params } })
|
|
||||||
)
|
)
|
||||||
|
const metricsAdditionalData = additionalDataCounter.init({
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsAdditionalData.start()
|
||||||
|
|
||||||
const cacheClient = await getCacheClient()
|
const cacheClient = await getCacheClient()
|
||||||
return cacheClient.cacheOrGet(
|
return cacheClient.cacheOrGet(
|
||||||
@@ -1096,28 +950,7 @@ export const hotelQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsAdditionalData.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
throw new Error("Unable to fetch additional data for hotel")
|
throw new Error("Unable to fetch additional data for hotel")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1126,22 +959,11 @@ export const hotelQueryRouter = router({
|
|||||||
additionalDataSchema.safeParse(apiJson)
|
additionalDataSchema.safeParse(apiJson)
|
||||||
|
|
||||||
if (!validatedAdditionalData.success) {
|
if (!validatedAdditionalData.success) {
|
||||||
console.error(
|
metricsAdditionalData.validationError(validatedAdditionalData.error)
|
||||||
"api.hotels.additionalData validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { params },
|
|
||||||
error: validatedAdditionalData.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw badRequestError()
|
throw badRequestError()
|
||||||
}
|
}
|
||||||
metrics.additionalData.success.add(1, {
|
|
||||||
hotelId,
|
metricsAdditionalData.success()
|
||||||
})
|
|
||||||
console.info(
|
|
||||||
"api.hotels.additionalData success",
|
|
||||||
JSON.stringify({ query: { params } })
|
|
||||||
)
|
|
||||||
|
|
||||||
return validatedAdditionalData.data
|
return validatedAdditionalData.data
|
||||||
},
|
},
|
||||||
@@ -1167,12 +989,16 @@ export const hotelQueryRouter = router({
|
|||||||
language: apiLang,
|
language: apiLang,
|
||||||
}
|
}
|
||||||
|
|
||||||
const metricsData = { ...params, hotelId: input.hotelId }
|
const breakfastCounter = createCounter(
|
||||||
metrics.breakfastPackage.counter.add(1, metricsData)
|
"trpc.hotel.packages",
|
||||||
console.info(
|
"breakfast"
|
||||||
"api.package.breakfast start",
|
|
||||||
JSON.stringify({ query: metricsData })
|
|
||||||
)
|
)
|
||||||
|
const metricsBreakfast = breakfastCounter.init({
|
||||||
|
params,
|
||||||
|
hotelId: input.hotelId,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsBreakfast.start()
|
||||||
|
|
||||||
const cacheClient = await getCacheClient()
|
const cacheClient = await getCacheClient()
|
||||||
const breakfastPackages = await cacheClient.cacheOrGet(
|
const breakfastPackages = await cacheClient.cacheOrGet(
|
||||||
@@ -1189,57 +1015,17 @@ export const hotelQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsBreakfast.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw new Error("Unable to fetch breakfast packages")
|
throw new Error("Unable to fetch breakfast packages")
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiJson = await apiResponse.json()
|
const apiJson = await apiResponse.json()
|
||||||
const breakfastPackages = breakfastPackagesSchema.safeParse(apiJson)
|
const breakfastPackages = breakfastPackagesSchema.safeParse(apiJson)
|
||||||
if (!breakfastPackages.success) {
|
if (!breakfastPackages.success) {
|
||||||
metrics.breakfastPackage.fail.add(1, {
|
metricsBreakfast.validationError(breakfastPackages.error)
|
||||||
...metricsData,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(breakfastPackages.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"api.package.breakfast validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: metricsData,
|
|
||||||
error: breakfastPackages.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
throw new Error("Unable to parse breakfast packages")
|
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
|
return breakfastPackages.data
|
||||||
},
|
},
|
||||||
"1h"
|
"1h"
|
||||||
@@ -1265,6 +1051,8 @@ export const hotelQueryRouter = router({
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
metricsBreakfast.success()
|
||||||
|
|
||||||
return breakfastPackages.filter(
|
return breakfastPackages.filter(
|
||||||
(pkg) => pkg.code !== BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
|
(pkg) => pkg.code !== BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
|
||||||
)
|
)
|
||||||
@@ -1281,16 +1069,21 @@ export const hotelQueryRouter = router({
|
|||||||
language: apiLang,
|
language: apiLang,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ancillaryCounter = createCounter(
|
||||||
|
"trpc.hotel.packages",
|
||||||
|
"ancillary"
|
||||||
|
)
|
||||||
|
const metricsAncillary = ancillaryCounter.init({
|
||||||
|
params,
|
||||||
|
hotelId: input.hotelId,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsAncillary.start()
|
||||||
|
|
||||||
const cacheClient = await getCacheClient()
|
const cacheClient = await getCacheClient()
|
||||||
return await cacheClient.cacheOrGet(
|
const result = await cacheClient.cacheOrGet(
|
||||||
`${apiLang}:hotel:${input.hotelId}:ancillaries:startDate:${params.StartDate}:endDate:${params.EndDate}`,
|
`${apiLang}:hotel:${input.hotelId}:ancillaries:startDate:${params.StartDate}:endDate:${params.EndDate}`,
|
||||||
async () => {
|
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(
|
const apiResponse = await api.get(
|
||||||
api.endpoints.v1.Package.Ancillary.hotel(input.hotelId),
|
api.endpoints.v1.Package.Ancillary.hotel(input.hotelId),
|
||||||
{
|
{
|
||||||
@@ -1302,59 +1095,25 @@ export const hotelQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsAncillary.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiJson = await apiResponse.json()
|
const apiJson = await apiResponse.json()
|
||||||
const ancillaryPackages = ancillaryPackagesSchema.safeParse(apiJson)
|
const ancillaryPackages = ancillaryPackagesSchema.safeParse(apiJson)
|
||||||
if (!ancillaryPackages.success) {
|
if (!ancillaryPackages.success) {
|
||||||
metrics.ancillaryPackage.fail.add(1, {
|
metricsAncillary.validationError(ancillaryPackages.error)
|
||||||
...metricsData,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(ancillaryPackages.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"api.package.ancillary validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: metricsData,
|
|
||||||
error: ancillaryPackages.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
metrics.ancillaryPackage.success.add(1, metricsData)
|
|
||||||
console.info(
|
|
||||||
"api.package.ancillary success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: metricsData,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return ancillaryPackages.data
|
return ancillaryPackages.data
|
||||||
},
|
},
|
||||||
"1h"
|
"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 { env } from "@/env/server"
|
||||||
import * as api from "@/lib/api"
|
import * as api from "@/lib/api"
|
||||||
import { badRequestError } from "@/server/errors/trpc"
|
import { badRequestError } from "@/server/errors/trpc"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import { toApiLang } from "@/server/utils"
|
import { toApiLang } from "@/server/utils"
|
||||||
|
|
||||||
import { generateChildrenString } from "@/components/HotelReservation/utils"
|
import { generateChildrenString } from "@/components/HotelReservation/utils"
|
||||||
@@ -13,8 +14,7 @@ import { getCacheClient } from "@/services/dataCache"
|
|||||||
import { cache } from "@/utils/cache"
|
import { cache } from "@/utils/cache"
|
||||||
|
|
||||||
import { getHotelPageUrls } from "../contentstack/hotelPage/utils"
|
import { getHotelPageUrls } from "../contentstack/hotelPage/utils"
|
||||||
import { type RoomFeaturesInput, roomPackagesInputSchema } from "./input"
|
import { type RoomFeaturesInput } from "./input"
|
||||||
import { metrics } from "./metrics"
|
|
||||||
import {
|
import {
|
||||||
type Cities,
|
type Cities,
|
||||||
citiesByCountrySchema,
|
citiesByCountrySchema,
|
||||||
@@ -38,12 +38,12 @@ import type {
|
|||||||
DestinationPagesHotelData,
|
DestinationPagesHotelData,
|
||||||
Room as RoomCategory,
|
Room as RoomCategory,
|
||||||
} from "@/types/hotel"
|
} from "@/types/hotel"
|
||||||
import type { PackagesInput } from "@/types/requests/packages"
|
import type { PackagesOutput } from "@/types/requests/packages"
|
||||||
import type {
|
import type {
|
||||||
HotelsAvailabilityInputSchema,
|
HotelsAvailabilityInputSchema,
|
||||||
HotelsByHotelIdsAvailabilityInputSchema,
|
HotelsByHotelIdsAvailabilityInputSchema,
|
||||||
RoomsAvailabilityInputRoom,
|
RoomsAvailabilityInputRoom,
|
||||||
RoomsAvailabilityInputSchema,
|
RoomsAvailabilityOutputSchema,
|
||||||
} from "@/types/trpc/routers/hotel/availability"
|
} from "@/types/trpc/routers/hotel/availability"
|
||||||
import type { HotelInput } from "@/types/trpc/routers/hotel/hotel"
|
import type { HotelInput } from "@/types/trpc/routers/hotel/hotel"
|
||||||
import type {
|
import type {
|
||||||
@@ -332,18 +332,23 @@ export async function getHotelIdsByCityId({
|
|||||||
cityId: string
|
cityId: string
|
||||||
serviceToken: string
|
serviceToken: string
|
||||||
}) {
|
}) {
|
||||||
|
const getHotelIdsByCityIdCounter = createCounter(
|
||||||
|
"hotel",
|
||||||
|
"getHotelIdsByCityId"
|
||||||
|
)
|
||||||
|
const metricsGetHotelIdsByCityId = getHotelIdsByCityIdCounter.init({
|
||||||
|
cityId,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetHotelIdsByCityId.start()
|
||||||
|
|
||||||
const cacheClient = await getCacheClient()
|
const cacheClient = await getCacheClient()
|
||||||
return await cacheClient.cacheOrGet(
|
const result = await cacheClient.cacheOrGet(
|
||||||
`${cityId}:hotelsByCityId`,
|
`${cityId}:hotelsByCityId`,
|
||||||
async () => {
|
async () => {
|
||||||
const searchParams = new URLSearchParams({
|
const searchParams = new URLSearchParams({
|
||||||
city: cityId,
|
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(
|
const apiResponse = await api.get(
|
||||||
api.endpoints.v1.Hotel.hotels,
|
api.endpoints.v1.Hotel.hotels,
|
||||||
@@ -356,59 +361,25 @@ export async function getHotelIdsByCityId({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const responseMessage = await apiResponse.text()
|
await metricsGetHotelIdsByCityId.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
throw new Error("Unable to fetch hotelIds by cityId")
|
throw new Error("Unable to fetch hotelIds by cityId")
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiJson = await apiResponse.json()
|
const apiJson = await apiResponse.json()
|
||||||
const validatedHotelIds = getHotelIdsSchema.safeParse(apiJson)
|
const validatedHotelIds = getHotelIdsSchema.safeParse(apiJson)
|
||||||
if (!validatedHotelIds.success) {
|
if (!validatedHotelIds.success) {
|
||||||
metrics.hotelIds.fail.add(1, {
|
metricsGetHotelIdsByCityId.validationError(validatedHotelIds.error)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
throw new Error("Unable to parse data for hotelIds by cityId")
|
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
|
return validatedHotelIds.data
|
||||||
},
|
},
|
||||||
env.CACHE_TIME_HOTELS
|
env.CACHE_TIME_HOTELS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
metricsGetHotelIdsByCityId.success()
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getHotelIdsByCountry({
|
export async function getHotelIdsByCountry({
|
||||||
@@ -418,17 +389,22 @@ export async function getHotelIdsByCountry({
|
|||||||
country: string
|
country: string
|
||||||
serviceToken: string
|
serviceToken: string
|
||||||
}) {
|
}) {
|
||||||
const cacheClient = await getCacheClient()
|
const getHotelIdsByCountryCounter = createCounter(
|
||||||
|
"hotel",
|
||||||
return await cacheClient.cacheOrGet(
|
"getHotelIdsByCountry"
|
||||||
`${country}:hotelsByCountry`,
|
|
||||||
async () => {
|
|
||||||
metrics.hotelIds.counter.add(1, { country })
|
|
||||||
console.info(
|
|
||||||
"api.hotel.hotel-ids start",
|
|
||||||
JSON.stringify({ query: { country } })
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const metricsGetHotelIdsByCountry = getHotelIdsByCountryCounter.init({
|
||||||
|
country,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetHotelIdsByCountry.start()
|
||||||
|
|
||||||
|
const cacheClient = await getCacheClient()
|
||||||
|
|
||||||
|
const result = await cacheClient.cacheOrGet(
|
||||||
|
`${country}:hotelsByCountry`,
|
||||||
|
async () => {
|
||||||
const hotelIdsParams = new URLSearchParams({
|
const hotelIdsParams = new URLSearchParams({
|
||||||
country,
|
country,
|
||||||
})
|
})
|
||||||
@@ -444,55 +420,25 @@ export async function getHotelIdsByCountry({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const responseMessage = await apiResponse.text()
|
await metricsGetHotelIdsByCountry.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
throw new Error("Unable to fetch hotelIds by country")
|
throw new Error("Unable to fetch hotelIds by country")
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiJson = await apiResponse.json()
|
const apiJson = await apiResponse.json()
|
||||||
const validatedHotelIds = getHotelIdsSchema.safeParse(apiJson)
|
const validatedHotelIds = getHotelIdsSchema.safeParse(apiJson)
|
||||||
if (!validatedHotelIds.success) {
|
if (!validatedHotelIds.success) {
|
||||||
metrics.hotelIds.fail.add(1, {
|
metricsGetHotelIdsByCountry.validationError(validatedHotelIds.error)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
throw new Error("Unable to parse hotelIds by country")
|
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
|
return validatedHotelIds.data
|
||||||
},
|
},
|
||||||
env.CACHE_TIME_HOTELS
|
env.CACHE_TIME_HOTELS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
metricsGetHotelIdsByCountry.success()
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getHotelIdsByCityIdentifier(
|
export async function getHotelIdsByCityIdentifier(
|
||||||
@@ -631,11 +577,22 @@ function findProduct(product: Products, rateDefinition: RateDefinition) {
|
|||||||
|
|
||||||
export const getHotel = cache(
|
export const getHotel = cache(
|
||||||
async (input: HotelInput, serviceToken: string) => {
|
async (input: HotelInput, serviceToken: string) => {
|
||||||
const callable = async function (
|
const { hotelId, language, isCardOnlyPayment } = input
|
||||||
hotelId: HotelInput["hotelId"],
|
|
||||||
language: HotelInput["language"],
|
const getHotelCounter = createCounter("hotel", "getHotel")
|
||||||
isCardOnlyPayment?: HotelInput["isCardOnlyPayment"]
|
const metricsGetHotel = getHotelCounter.init({
|
||||||
) {
|
hotelId,
|
||||||
|
language,
|
||||||
|
isCardOnlyPayment,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetHotel.start()
|
||||||
|
|
||||||
|
const cacheClient = await getCacheClient()
|
||||||
|
|
||||||
|
const result = await cacheClient.cacheOrGet(
|
||||||
|
`${input.language}:hotel:${input.hotelId}:${!!input.isCardOnlyPayment}`,
|
||||||
|
async () => {
|
||||||
/**
|
/**
|
||||||
* Since API expects the params appended and not just
|
* Since API expects the params appended and not just
|
||||||
* a comma separated string we need to initialize the
|
* a comma separated string we need to initialize the
|
||||||
@@ -650,14 +607,6 @@ export const getHotel = cache(
|
|||||||
["include", "RoomCategories"],
|
["include", "RoomCategories"],
|
||||||
["language", toApiLang(language)],
|
["language", toApiLang(language)],
|
||||||
])
|
])
|
||||||
metrics.hotel.counter.add(1, {
|
|
||||||
hotelId,
|
|
||||||
language,
|
|
||||||
})
|
|
||||||
console.info(
|
|
||||||
"api.hotels.hotelData start",
|
|
||||||
JSON.stringify({ query: { hotelId, params: params.toString() } })
|
|
||||||
)
|
|
||||||
|
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
api.endpoints.v1.Hotel.Hotels.hotel(hotelId),
|
api.endpoints.v1.Hotel.Hotels.hotel(hotelId),
|
||||||
@@ -670,28 +619,7 @@ export const getHotel = cache(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsGetHotel.httpError(apiResponse)
|
||||||
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
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -699,33 +627,10 @@ export const getHotel = cache(
|
|||||||
const validateHotelData = hotelSchema.safeParse(apiJson)
|
const validateHotelData = hotelSchema.safeParse(apiJson)
|
||||||
|
|
||||||
if (!validateHotelData.success) {
|
if (!validateHotelData.success) {
|
||||||
metrics.hotel.fail.add(1, {
|
metricsGetHotel.validationError(validateHotelData.error)
|
||||||
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()
|
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
|
const hotelData = validateHotelData.data
|
||||||
|
|
||||||
if (isCardOnlyPayment) {
|
if (isCardOnlyPayment) {
|
||||||
@@ -743,16 +648,13 @@ export const getHotel = cache(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return hotelData
|
return hotelData
|
||||||
}
|
|
||||||
|
|
||||||
const cacheClient = await getCacheClient()
|
|
||||||
return await cacheClient.cacheOrGet(
|
|
||||||
`${input.language}:hotel:${input.hotelId}:${!!input.isCardOnlyPayment}`,
|
|
||||||
async () => {
|
|
||||||
return callable(input.hotelId, input.language, input.isCardOnlyPayment)
|
|
||||||
},
|
},
|
||||||
env.CACHE_TIME_HOTELS
|
env.CACHE_TIME_HOTELS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
metricsGetHotel.success()
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -781,7 +683,14 @@ export async function getHotelsAvailabilityByCity(
|
|||||||
...(redemption ? { isRedemption: "true" } : {}),
|
...(redemption ? { isRedemption: "true" } : {}),
|
||||||
language: apiLang,
|
language: apiLang,
|
||||||
}
|
}
|
||||||
metrics.hotelsAvailability.counter.add(1, {
|
|
||||||
|
const getHotelsAvailabilityByCityCounter = createCounter(
|
||||||
|
"hotel",
|
||||||
|
"getHotelsAvailabilityByCity"
|
||||||
|
)
|
||||||
|
const metricsGetHotelsAvailabilityByCity =
|
||||||
|
getHotelsAvailabilityByCityCounter.init({
|
||||||
|
apiLang,
|
||||||
cityId,
|
cityId,
|
||||||
roomStayStartDate,
|
roomStayStartDate,
|
||||||
roomStayEndDate,
|
roomStayEndDate,
|
||||||
@@ -790,10 +699,9 @@ export async function getHotelsAvailabilityByCity(
|
|||||||
bookingCode,
|
bookingCode,
|
||||||
redemption,
|
redemption,
|
||||||
})
|
})
|
||||||
console.info(
|
|
||||||
"api.hotels.hotelsAvailability start",
|
metricsGetHotelsAvailabilityByCity.start()
|
||||||
JSON.stringify({ query: { cityId, params } })
|
|
||||||
)
|
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
api.endpoints.v1.Availability.city(cityId),
|
api.endpoints.v1.Availability.city(cityId),
|
||||||
{
|
{
|
||||||
@@ -804,74 +712,19 @@ export async function getHotelsAvailabilityByCity(
|
|||||||
params
|
params
|
||||||
)
|
)
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsGetHotelsAvailabilityByCity.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
throw new Error("Failed to fetch hotels availability by city")
|
throw new Error("Failed to fetch hotels availability by city")
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiJson = await apiResponse.json()
|
const apiJson = await apiResponse.json()
|
||||||
const validateAvailabilityData = hotelsAvailabilitySchema.safeParse(apiJson)
|
const validateAvailabilityData = hotelsAvailabilitySchema.safeParse(apiJson)
|
||||||
if (!validateAvailabilityData.success) {
|
if (!validateAvailabilityData.success) {
|
||||||
metrics.hotelsAvailability.fail.add(1, {
|
metricsGetHotelsAvailabilityByCity.validationError(
|
||||||
cityId,
|
validateAvailabilityData.error
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
throw badRequestError()
|
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) {
|
if (redemption) {
|
||||||
validateAvailabilityData.data.data.forEach((data) => {
|
validateAvailabilityData.data.data.forEach((data) => {
|
||||||
data.attributes.productType?.redemptions?.forEach((r) => {
|
data.attributes.productType?.redemptions?.forEach((r) => {
|
||||||
@@ -880,11 +733,15 @@ export async function getHotelsAvailabilityByCity(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const result = {
|
||||||
availability: validateAvailabilityData.data.data.flatMap(
|
availability: validateAvailabilityData.data.data.flatMap(
|
||||||
(hotels) => hotels.attributes
|
(hotels) => hotels.attributes
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
metricsGetHotelsAvailabilityByCity.success()
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getHotelsAvailabilityByHotelIds(
|
export async function getHotelsAvailabilityByHotelIds(
|
||||||
@@ -910,8 +767,26 @@ export async function getHotelsAvailabilityByHotelIds(
|
|||||||
["language", apiLang],
|
["language", apiLang],
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const getHotelsAvailabilityByHotelIdsCounter = createCounter(
|
||||||
|
"hotel",
|
||||||
|
"getHotelsAvailabilityByHotelIds"
|
||||||
|
)
|
||||||
|
const metricsGetHotelsAvailabilityByHotelIds =
|
||||||
|
getHotelsAvailabilityByHotelIdsCounter.init({
|
||||||
|
apiLang,
|
||||||
|
hotelIds,
|
||||||
|
roomStayStartDate,
|
||||||
|
roomStayEndDate,
|
||||||
|
adults,
|
||||||
|
children,
|
||||||
|
bookingCode,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetHotelsAvailabilityByHotelIds.start()
|
||||||
|
|
||||||
const cacheClient = await getCacheClient()
|
const cacheClient = await getCacheClient()
|
||||||
return cacheClient.cacheOrGet(
|
|
||||||
|
const result = cacheClient.cacheOrGet(
|
||||||
`${apiLang}:hotels:availability:${hotelIds.join(",")}:${roomStayStartDate}:${roomStayEndDate}:${adults}:${children}:${bookingCode}`,
|
`${apiLang}:hotels:availability:${hotelIds.join(",")}:${roomStayStartDate}:${roomStayEndDate}:${adults}:${children}:${bookingCode}`,
|
||||||
async () => {
|
async () => {
|
||||||
/**
|
/**
|
||||||
@@ -924,18 +799,7 @@ export async function getHotelsAvailabilityByHotelIds(
|
|||||||
hotelIds.forEach((hotelId) =>
|
hotelIds.forEach((hotelId) =>
|
||||||
params.append("hotelIds", hotelId.toString())
|
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(
|
const apiResponse = await api.get(
|
||||||
api.endpoints.v1.Availability.hotels(),
|
api.endpoints.v1.Availability.hotels(),
|
||||||
{
|
{
|
||||||
@@ -946,72 +810,20 @@ export async function getHotelsAvailabilityByHotelIds(
|
|||||||
params
|
params
|
||||||
)
|
)
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsGetHotelsAvailabilityByHotelIds.httpError(apiResponse)
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
throw new Error("Failed to fetch hotels availability by hotelIds")
|
throw new Error("Failed to fetch hotels availability by hotelIds")
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiJson = await apiResponse.json()
|
const apiJson = await apiResponse.json()
|
||||||
const validateAvailabilityData =
|
const validateAvailabilityData =
|
||||||
hotelsAvailabilitySchema.safeParse(apiJson)
|
hotelsAvailabilitySchema.safeParse(apiJson)
|
||||||
if (!validateAvailabilityData.success) {
|
if (!validateAvailabilityData.success) {
|
||||||
metrics.hotelsByHotelIdAvailability.fail.add(1, {
|
metricsGetHotelsAvailabilityByHotelIds.validationError(
|
||||||
hotelIds,
|
validateAvailabilityData.error
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
throw badRequestError()
|
throw badRequestError()
|
||||||
}
|
}
|
||||||
metrics.hotelsByHotelIdAvailability.success.add(1, {
|
|
||||||
hotelIds,
|
|
||||||
roomStayStartDate,
|
|
||||||
roomStayEndDate,
|
|
||||||
adults,
|
|
||||||
children,
|
|
||||||
bookingCode,
|
|
||||||
})
|
|
||||||
console.info(
|
|
||||||
"api.hotels.hotelsByHotelIdAvailability success",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { params },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return {
|
return {
|
||||||
availability: validateAvailabilityData.data.data.flatMap(
|
availability: validateAvailabilityData.data.data.flatMap(
|
||||||
(hotels) => hotels.attributes
|
(hotels) => hotels.attributes
|
||||||
@@ -1020,6 +832,10 @@ export async function getHotelsAvailabilityByHotelIds(
|
|||||||
},
|
},
|
||||||
env.CACHE_TIME_CITY_SEARCH
|
env.CACHE_TIME_CITY_SEARCH
|
||||||
)
|
)
|
||||||
|
|
||||||
|
metricsGetHotelsAvailabilityByHotelIds.success()
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getRoomFeaturesInventory(
|
async function getRoomFeaturesInventory(
|
||||||
@@ -1034,10 +850,7 @@ async function getRoomFeaturesInventory(
|
|||||||
roomFeatureCodes,
|
roomFeatureCodes,
|
||||||
startDate,
|
startDate,
|
||||||
} = input
|
} = input
|
||||||
const cacheClient = await getCacheClient()
|
|
||||||
return cacheClient.cacheOrGet(
|
|
||||||
stringify(input),
|
|
||||||
async function () {
|
|
||||||
const params = {
|
const params = {
|
||||||
adults,
|
adults,
|
||||||
hotelId,
|
hotelId,
|
||||||
@@ -1049,8 +862,20 @@ async function getRoomFeaturesInventory(
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
metrics.roomFeatures.counter.add(1, params)
|
const getRoomFeaturesInventoryCounter = createCounter(
|
||||||
|
"hotel",
|
||||||
|
"getRoomFeaturesInventory"
|
||||||
|
)
|
||||||
|
const metricsGetRoomFeaturesInventory =
|
||||||
|
getRoomFeaturesInventoryCounter.init(params)
|
||||||
|
|
||||||
|
metricsGetRoomFeaturesInventory.start()
|
||||||
|
|
||||||
|
const cacheClient = await getCacheClient()
|
||||||
|
|
||||||
|
const result = cacheClient.cacheOrGet(
|
||||||
|
stringify(input),
|
||||||
|
async function () {
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
api.endpoints.v1.Availability.roomFeatures(hotelId),
|
api.endpoints.v1.Availability.roomFeatures(hotelId),
|
||||||
{
|
{
|
||||||
@@ -1062,67 +887,45 @@ async function getRoomFeaturesInventory(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = apiResponse.text()
|
await metricsGetRoomFeaturesInventory.httpError(apiResponse)
|
||||||
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)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await apiResponse.json()
|
const data = await apiResponse.json()
|
||||||
const validatedRoomFeaturesData = roomFeaturesSchema.safeParse(data)
|
const validatedRoomFeaturesData = roomFeaturesSchema.safeParse(data)
|
||||||
if (!validatedRoomFeaturesData.success) {
|
if (!validatedRoomFeaturesData.success) {
|
||||||
console.error(
|
metricsGetRoomFeaturesInventory.validationError(
|
||||||
"api.availability.roomfeature error",
|
validatedRoomFeaturesData.error
|
||||||
JSON.stringify({
|
|
||||||
query: { hotelId, params },
|
|
||||||
error: validatedRoomFeaturesData.error,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
metrics.roomFeatures.success.add(1, params)
|
|
||||||
|
|
||||||
return validatedRoomFeaturesData.data
|
return validatedRoomFeaturesData.data
|
||||||
},
|
},
|
||||||
"5m"
|
"5m"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
metricsGetRoomFeaturesInventory.success()
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPackages(
|
export async function getPackages(input: PackagesOutput, serviceToken: string) {
|
||||||
rawInput: PackagesInput,
|
const { adults, children, endDate, hotelId, lang, packageCodes, startDate } =
|
||||||
serviceToken: string
|
input
|
||||||
) {
|
|
||||||
const parsedInput = roomPackagesInputSchema.safeParse(rawInput)
|
const getPackagesCounter = createCounter("hotel", "getPackages")
|
||||||
if (!parsedInput.success) {
|
const metricsGetPackages = getPackagesCounter.init({
|
||||||
console.info(`Failed to parse input for Get Packages`)
|
input,
|
||||||
console.error(parsedInput.error)
|
})
|
||||||
return null
|
|
||||||
}
|
metricsGetPackages.start()
|
||||||
const input = parsedInput.data
|
|
||||||
const cacheClient = await getCacheClient()
|
const cacheClient = await getCacheClient()
|
||||||
return cacheClient.cacheOrGet(
|
|
||||||
|
const result = cacheClient.cacheOrGet(
|
||||||
stringify(input),
|
stringify(input),
|
||||||
async function () {
|
async function () {
|
||||||
const {
|
|
||||||
adults,
|
|
||||||
children,
|
|
||||||
endDate,
|
|
||||||
hotelId,
|
|
||||||
lang,
|
|
||||||
packageCodes,
|
|
||||||
startDate,
|
|
||||||
} = input
|
|
||||||
const apiLang = toApiLang(lang)
|
const apiLang = toApiLang(lang)
|
||||||
|
|
||||||
const searchParams = new URLSearchParams({
|
const searchParams = new URLSearchParams({
|
||||||
@@ -1137,16 +940,6 @@ export async function getPackages(
|
|||||||
searchParams.append("packageCodes", code)
|
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(
|
const apiResponse = await api.get(
|
||||||
api.endpoints.v1.Package.Packages.hotel(hotelId),
|
api.endpoints.v1.Package.Packages.hotel(hotelId),
|
||||||
{
|
{
|
||||||
@@ -1158,56 +951,29 @@ export async function getPackages(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
metrics.packages.fail.add(1, {
|
await metricsGetPackages.httpError(apiResponse)
|
||||||
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 } })
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiJson = await apiResponse.json()
|
const apiJson = await apiResponse.json()
|
||||||
const validatedPackagesData = packagesSchema.safeParse(apiJson)
|
const validatedPackagesData = packagesSchema.safeParse(apiJson)
|
||||||
if (!validatedPackagesData.success) {
|
if (!validatedPackagesData.success) {
|
||||||
metrics.packages.fail.add(1, {
|
metricsGetPackages.validationError(validatedPackagesData.error)
|
||||||
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,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
metrics.packages.success.add(1, {
|
|
||||||
hotelId,
|
|
||||||
})
|
|
||||||
console.info(
|
|
||||||
"api.hotels.packages success",
|
|
||||||
JSON.stringify({ query: { hotelId, params: params } })
|
|
||||||
)
|
|
||||||
|
|
||||||
return validatedPackagesData.data
|
return validatedPackagesData.data
|
||||||
},
|
},
|
||||||
"3h"
|
"3h"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
metricsGetPackages.success()
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRoomsAvailability(
|
export async function getRoomsAvailability(
|
||||||
input: RoomsAvailabilityInputSchema,
|
input: RoomsAvailabilityOutputSchema,
|
||||||
token: string,
|
token: string,
|
||||||
serviceToken: string,
|
serviceToken: string,
|
||||||
userPoints: number | undefined
|
userPoints: number | undefined
|
||||||
@@ -1219,27 +985,18 @@ export async function getRoomsAvailability(
|
|||||||
|
|
||||||
const redemption = searchType === REDEMPTION
|
const redemption = searchType === REDEMPTION
|
||||||
|
|
||||||
const apiLang = toApiLang(lang)
|
const getRoomsAvailabilityCounter = createCounter(
|
||||||
|
"hotel",
|
||||||
const kids = rooms
|
"getRoomsAvailability"
|
||||||
.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 metricsGetRoomsAvailability = getRoomsAvailabilityCounter.init({
|
||||||
|
input,
|
||||||
|
redemption,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsGetRoomsAvailability.start()
|
||||||
|
|
||||||
|
const apiLang = toApiLang(lang)
|
||||||
|
|
||||||
const baseCacheKey = {
|
const baseCacheKey = {
|
||||||
bookingCode,
|
bookingCode,
|
||||||
@@ -1257,7 +1014,7 @@ export async function getRoomsAvailability(
|
|||||||
...baseCacheKey,
|
...baseCacheKey,
|
||||||
room,
|
room,
|
||||||
}
|
}
|
||||||
return cacheClient.cacheOrGet(
|
const result = cacheClient.cacheOrGet(
|
||||||
stringify(cacheKey),
|
stringify(cacheKey),
|
||||||
async function () {
|
async function () {
|
||||||
{
|
{
|
||||||
@@ -1287,9 +1044,8 @@ export async function getRoomsAvailability(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
|
await metricsGetRoomsAvailability.httpError(apiResponse)
|
||||||
const text = await apiResponse.text()
|
const text = await apiResponse.text()
|
||||||
metrics.roomsAvailability.fail.add(1, metricsData)
|
|
||||||
console.error("Failed API call", { params, text })
|
|
||||||
return { error: "http_error", details: text }
|
return { error: "http_error", details: text }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1297,11 +1053,10 @@ export async function getRoomsAvailability(
|
|||||||
const validateAvailabilityData =
|
const validateAvailabilityData =
|
||||||
roomsAvailabilitySchema.safeParse(apiJson)
|
roomsAvailabilitySchema.safeParse(apiJson)
|
||||||
if (!validateAvailabilityData.success) {
|
if (!validateAvailabilityData.success) {
|
||||||
console.error("Validation error", {
|
metricsGetRoomsAvailability.validationError(
|
||||||
params,
|
validateAvailabilityData.error
|
||||||
error: validateAvailabilityData.error,
|
)
|
||||||
})
|
|
||||||
metrics.roomsAvailability.fail.add(1, metricsData)
|
|
||||||
return {
|
return {
|
||||||
error: "validation_error",
|
error: "validation_error",
|
||||||
details: validateAvailabilityData.error,
|
details: validateAvailabilityData.error,
|
||||||
@@ -1323,7 +1078,7 @@ export async function getRoomsAvailability(
|
|||||||
const roomFeatures = await getPackages(
|
const roomFeatures = await getPackages(
|
||||||
{
|
{
|
||||||
adults: room.adults,
|
adults: room.adults,
|
||||||
children: room.childrenInRoom?.length,
|
children: room.childrenInRoom?.length || 0,
|
||||||
endDate: input.booking.toDate,
|
endDate: input.booking.toDate,
|
||||||
hotelId: input.booking.hotelId,
|
hotelId: input.booking.hotelId,
|
||||||
lang,
|
lang,
|
||||||
@@ -1387,9 +1142,10 @@ export async function getRoomsAvailability(
|
|||||||
},
|
},
|
||||||
"1m"
|
"1m"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
metrics.roomsAvailability.success.add(1, metricsData)
|
|
||||||
|
|
||||||
const data = availabilityResponses.map((availability) => {
|
const data = availabilityResponses.map((availability) => {
|
||||||
if (availability.status === "fulfilled") {
|
if (availability.status === "fulfilled") {
|
||||||
@@ -1401,6 +1157,8 @@ export async function getRoomsAvailability(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
metricsGetRoomsAvailability.success()
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import { TRPCError } from "@trpc/server"
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { Lang } from "@/constants/languages"
|
import { Lang } from "@/constants/languages"
|
||||||
|
import { getVerifiedUser } from "@/server/routers/user/utils"
|
||||||
import { safeProtectedProcedure } from "@/server/trpc"
|
import { safeProtectedProcedure } from "@/server/trpc"
|
||||||
|
|
||||||
import { isValidSession } from "@/utils/session"
|
import { isValidSession } from "@/utils/session"
|
||||||
|
|
||||||
import { getVerifiedUser } from "../../user/query"
|
|
||||||
import { getPrimaryLinks } from "./getPrimaryLinks"
|
import { getPrimaryLinks } from "./getPrimaryLinks"
|
||||||
import { getSecondaryLinks } from "./getSecondaryLinks"
|
import { getSecondaryLinks } from "./getSecondaryLinks"
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import { publicProcedure, router } from "@/server/trpc"
|
import { publicProcedure, router } from "@/server/trpc"
|
||||||
|
|
||||||
import { getCacheClient } from "@/services/dataCache"
|
import { getCacheClient } from "@/services/dataCache"
|
||||||
|
|
||||||
import { jobylonFeedSchema } from "./output"
|
import { jobylonFeedSchema } from "./output"
|
||||||
import {
|
|
||||||
getJobylonFeedCounter,
|
|
||||||
getJobylonFeedFailCounter,
|
|
||||||
getJobylonFeedSuccessCounter,
|
|
||||||
} from "./telemetry"
|
|
||||||
|
|
||||||
export const TWENTYFOUR_HOURS = 60 * 60 * 24
|
export const TWENTYFOUR_HOURS = 60 * 60 * 24
|
||||||
|
|
||||||
@@ -19,20 +15,19 @@ const feedUrl =
|
|||||||
export const jobylonQueryRouter = router({
|
export const jobylonQueryRouter = router({
|
||||||
feed: router({
|
feed: router({
|
||||||
get: publicProcedure.query(async function () {
|
get: publicProcedure.query(async function () {
|
||||||
|
const jobylonFeedGetCounter = createCounter("trpc.jobylon.feed", "get")
|
||||||
|
const metricsJobylonFeedGet = jobylonFeedGetCounter.init()
|
||||||
|
|
||||||
|
metricsJobylonFeedGet.start()
|
||||||
|
|
||||||
const url = new URL(feedUrl)
|
const url = new URL(feedUrl)
|
||||||
url.search = new URLSearchParams({
|
url.search = new URLSearchParams({
|
||||||
format: "json",
|
format: "json",
|
||||||
}).toString()
|
}).toString()
|
||||||
const urlString = url.toString()
|
|
||||||
|
|
||||||
getJobylonFeedCounter.add(1, { url: urlString })
|
|
||||||
console.info(
|
|
||||||
"jobylon.feed start",
|
|
||||||
JSON.stringify({ query: { url: urlString } })
|
|
||||||
)
|
|
||||||
|
|
||||||
const cacheClient = await getCacheClient()
|
const cacheClient = await getCacheClient()
|
||||||
return await cacheClient.cacheOrGet(
|
|
||||||
|
const result = cacheClient.cacheOrGet(
|
||||||
"jobylon:feed",
|
"jobylon:feed",
|
||||||
async () => {
|
async () => {
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
@@ -40,27 +35,14 @@ export const jobylonQueryRouter = router({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
await metricsJobylonFeedGet.httpError(response)
|
||||||
const text = await response.text()
|
const text = await response.text()
|
||||||
const error = {
|
throw new Error(
|
||||||
|
`Failed to fetch Jobylon feed: ${JSON.stringify({
|
||||||
status: response.status,
|
status: response.status,
|
||||||
statusText: response.statusText,
|
statusText: response.statusText,
|
||||||
text,
|
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)}`
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,37 +50,20 @@ export const jobylonQueryRouter = router({
|
|||||||
const validatedResponse = jobylonFeedSchema.safeParse(responseJson)
|
const validatedResponse = jobylonFeedSchema.safeParse(responseJson)
|
||||||
|
|
||||||
if (!validatedResponse.success) {
|
if (!validatedResponse.success) {
|
||||||
getJobylonFeedFailCounter.add(1, {
|
metricsJobylonFeedGet.validationError(validatedResponse.error)
|
||||||
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)
|
|
||||||
throw new 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
|
return validatedResponse.data
|
||||||
},
|
},
|
||||||
"1d"
|
"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()
|
.number()
|
||||||
.optional()
|
.optional()
|
||||||
.transform((num) => (num ? String(num) : undefined)),
|
.transform((num) => (num ? String(num) : undefined)),
|
||||||
limit: z
|
limit: z.number().min(0).default(6),
|
||||||
.number()
|
|
||||||
.min(0)
|
|
||||||
.default(6)
|
|
||||||
.transform((num) => String(num)),
|
|
||||||
lang: z.nativeEnum(Lang).optional(),
|
lang: z.nativeEnum(Lang).optional(),
|
||||||
})
|
})
|
||||||
.default({})
|
.default({})
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { metrics } from "@opentelemetry/api"
|
|
||||||
|
|
||||||
import { signupVerify } from "@/constants/routes/signup"
|
import { signupVerify } from "@/constants/routes/signup"
|
||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
import * as api from "@/lib/api"
|
import * as api from "@/lib/api"
|
||||||
@@ -8,6 +6,7 @@ import {
|
|||||||
initiateSaveCardSchema,
|
initiateSaveCardSchema,
|
||||||
subscriberIdSchema,
|
subscriberIdSchema,
|
||||||
} from "@/server/routers/user/output"
|
} from "@/server/routers/user/output"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
import { protectedProcedure, router, serviceProcedure } from "@/server/trpc"
|
import { protectedProcedure, router, serviceProcedure } from "@/server/trpc"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -17,20 +16,6 @@ import {
|
|||||||
signupInput,
|
signupInput,
|
||||||
} from "./input"
|
} 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({
|
export const userMutationRouter = router({
|
||||||
creditCard: router({
|
creditCard: router({
|
||||||
add: protectedProcedure.input(addCreditCardInput).mutation(async function ({
|
add: protectedProcedure.input(addCreditCardInput).mutation(async function ({
|
||||||
@@ -159,7 +144,15 @@ export const userMutationRouter = router({
|
|||||||
generatePreferencesLink: protectedProcedure.mutation(async function ({
|
generatePreferencesLink: protectedProcedure.mutation(async function ({
|
||||||
ctx,
|
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, {
|
const apiResponse = await api.get(api.endpoints.v1.Profile.subscriberId, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
@@ -167,25 +160,7 @@ export const userMutationRouter = router({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
const text = await apiResponse.text()
|
await metricsGeneratePreferencesLink.httpError(apiResponse)
|
||||||
generatePreferencesLinkFailCounter.add(1, {
|
|
||||||
error_type: "http_error",
|
|
||||||
error: JSON.stringify({
|
|
||||||
status: apiResponse.status,
|
|
||||||
statusText: apiResponse.statusText,
|
|
||||||
text,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"api.user.subscriberId error ",
|
|
||||||
JSON.stringify({
|
|
||||||
error: {
|
|
||||||
status: apiResponse.status,
|
|
||||||
statusText: apiResponse.statusText,
|
|
||||||
text,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,31 +169,23 @@ export const userMutationRouter = router({
|
|||||||
const validatedData = subscriberIdSchema.safeParse(data)
|
const validatedData = subscriberIdSchema.safeParse(data)
|
||||||
|
|
||||||
if (!validatedData.success) {
|
if (!validatedData.success) {
|
||||||
generatePreferencesLinkSuccessCounter.add(1, {
|
metricsGeneratePreferencesLink.validationError(validatedData.error)
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedData.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"api.user.generatePreferencesLink validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
error: validatedData.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
console.error(validatedData.error.format())
|
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const preferencesLink = new URL(env.SALESFORCE_PREFERENCE_BASE_URL)
|
const preferencesLink = new URL(env.SALESFORCE_PREFERENCE_BASE_URL)
|
||||||
preferencesLink.searchParams.set("subKey", validatedData.data.subscriberId)
|
preferencesLink.searchParams.set("subKey", validatedData.data.subscriberId)
|
||||||
|
|
||||||
generatePreferencesLinkSuccessCounter.add(1)
|
metricsGeneratePreferencesLink.success()
|
||||||
|
|
||||||
return preferencesLink.toString()
|
return preferencesLink.toString()
|
||||||
}),
|
}),
|
||||||
signup: serviceProcedure.input(signupInput).mutation(async function ({
|
signup: serviceProcedure.input(signupInput).mutation(async function ({
|
||||||
ctx,
|
ctx,
|
||||||
input,
|
input,
|
||||||
}) {
|
}) {
|
||||||
signupCounter.add(1)
|
const signupCounter = createCounter("trpc.user", "signup")
|
||||||
|
const metricsSignup = signupCounter.init()
|
||||||
|
|
||||||
const apiResponse = await api.post(api.endpoints.v1.Profile.profile, {
|
const apiResponse = await api.post(api.endpoints.v1.Profile.profile, {
|
||||||
body: input,
|
body: input,
|
||||||
@@ -228,29 +195,13 @@ export const userMutationRouter = router({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
|
await metricsSignup.httpError(apiResponse)
|
||||||
const text = await apiResponse.text()
|
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)
|
throw serverErrorByStatus(apiResponse.status, text)
|
||||||
}
|
}
|
||||||
signupSuccessCounter.add(1)
|
|
||||||
console.info("api.user.signup success")
|
metricsSignup.success()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
redirectUrl: signupVerify[input.language],
|
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 * as api from "@/lib/api"
|
||||||
import { dt } from "@/lib/dt"
|
import { createCounter } from "@/server/telemetry"
|
||||||
import {
|
import {
|
||||||
languageProtectedProcedure,
|
languageProtectedProcedure,
|
||||||
protectedProcedure,
|
protectedProcedure,
|
||||||
@@ -10,8 +7,6 @@ import {
|
|||||||
safeProtectedProcedure,
|
safeProtectedProcedure,
|
||||||
} from "@/server/trpc"
|
} from "@/server/trpc"
|
||||||
|
|
||||||
import { cache } from "@/utils/cache"
|
|
||||||
import * as maskValue from "@/utils/maskValue"
|
|
||||||
import { isValidSession } from "@/utils/session"
|
import { isValidSession } from "@/utils/session"
|
||||||
import { getFriendsMembership, getMembershipCards } from "@/utils/user"
|
import { getFriendsMembership, getMembershipCards } from "@/utils/user"
|
||||||
|
|
||||||
@@ -20,277 +15,21 @@ import {
|
|||||||
getSavedPaymentCardsInput,
|
getSavedPaymentCardsInput,
|
||||||
staysInput,
|
staysInput,
|
||||||
} from "./input"
|
} from "./input"
|
||||||
|
import { getFriendTransactionsSchema } from "./output"
|
||||||
import {
|
import {
|
||||||
creditCardsSchema,
|
getCreditCards,
|
||||||
getFriendTransactionsSchema,
|
getPreviousStays,
|
||||||
getStaysSchema,
|
getUpcomingStays,
|
||||||
getUserSchema,
|
getVerifiedUser,
|
||||||
} from "./output"
|
parsedUser,
|
||||||
import { updateStaysBookingUrl } from "./utils"
|
updateStaysBookingUrl,
|
||||||
|
} from "./utils"
|
||||||
import type { Session } from "next-auth"
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
LoginType,
|
LoginType,
|
||||||
TrackingSDKUserData,
|
TrackingSDKUserData,
|
||||||
} from "@/types/components/tracking"
|
} from "@/types/components/tracking"
|
||||||
import { Transactions } from "@/types/enums/transactions"
|
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({
|
export const userQueryRouter = router({
|
||||||
get: protectedProcedure
|
get: protectedProcedure
|
||||||
@@ -385,11 +124,20 @@ export const userQueryRouter = router({
|
|||||||
return membershipLevel
|
return membershipLevel
|
||||||
}),
|
}),
|
||||||
userTrackingInfo: safeProtectedProcedure.query(async function ({ ctx }) {
|
userTrackingInfo: safeProtectedProcedure.query(async function ({ ctx }) {
|
||||||
|
const userTrackingInfoCounter = createCounter("user", "userTrackingInfo")
|
||||||
|
const metricsUserTrackingInfo = userTrackingInfoCounter.init()
|
||||||
|
|
||||||
|
metricsUserTrackingInfo.start()
|
||||||
|
|
||||||
const notLoggedInUserTrackingData: TrackingSDKUserData = {
|
const notLoggedInUserTrackingData: TrackingSDKUserData = {
|
||||||
loginStatus: "Non-logged in",
|
loginStatus: "Non-logged in",
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValidSession(ctx.session)) {
|
if (!isValidSession(ctx.session)) {
|
||||||
|
metricsUserTrackingInfo.success({
|
||||||
|
reason: "invalid session",
|
||||||
|
data: notLoggedInUserTrackingData,
|
||||||
|
})
|
||||||
return notLoggedInUserTrackingData
|
return notLoggedInUserTrackingData
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,63 +145,25 @@ export const userQueryRouter = router({
|
|||||||
const verifiedUserData = await getVerifiedUser({ session: ctx.session })
|
const verifiedUserData = await getVerifiedUser({ session: ctx.session })
|
||||||
|
|
||||||
if (!verifiedUserData || "error" in verifiedUserData) {
|
if (!verifiedUserData || "error" in verifiedUserData) {
|
||||||
|
metricsUserTrackingInfo.success({
|
||||||
|
reason: "invalid user data",
|
||||||
|
data: notLoggedInUserTrackingData,
|
||||||
|
})
|
||||||
return notLoggedInUserTrackingData
|
return notLoggedInUserTrackingData
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = new URLSearchParams()
|
const previousStaysData = await getPreviousStays(
|
||||||
params.set("limit", "1")
|
ctx.session.token.access_token,
|
||||||
getPreviousStaysCounter.add(1, { query: JSON.stringify({ params }) })
|
1
|
||||||
console.info(
|
|
||||||
"api.booking.stays.past start",
|
|
||||||
JSON.stringify({ query: { params } })
|
|
||||||
)
|
)
|
||||||
const previousStaysResponse = await api.get(
|
if (!previousStaysData) {
|
||||||
api.endpoints.v1.Booking.Stays.past,
|
metricsUserTrackingInfo.success({
|
||||||
{
|
reason: "no previous stays data",
|
||||||
headers: {
|
data: notLoggedInUserTrackingData,
|
||||||
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,
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
console.error(
|
|
||||||
"api.booking.stays.past error",
|
|
||||||
JSON.stringify({
|
|
||||||
error: {
|
|
||||||
status: previousStaysResponse.status,
|
|
||||||
statusText: previousStaysResponse.statusText,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return notLoggedInUserTrackingData
|
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),
|
|
||||||
})
|
|
||||||
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)
|
const membership = getFriendsMembership(verifiedUserData.data.loyalty)
|
||||||
|
|
||||||
const loggedInUserTrackingData: TrackingSDKUserData = {
|
const loggedInUserTrackingData: TrackingSDKUserData = {
|
||||||
@@ -462,13 +172,19 @@ export const userQueryRouter = router({
|
|||||||
memberId: verifiedUserData.data.profileId,
|
memberId: verifiedUserData.data.profileId,
|
||||||
membershipNumber: membership?.membershipNumber,
|
membershipNumber: membership?.membershipNumber,
|
||||||
memberLevel: membership?.membershipLevel,
|
memberLevel: membership?.membershipLevel,
|
||||||
noOfNightsStayed: verifiedPreviousStaysData.data.links?.totalCount ?? 0,
|
noOfNightsStayed: previousStaysData.links?.totalCount ?? 0,
|
||||||
totalPointsAvailableToSpend: membership?.currentPoints,
|
totalPointsAvailableToSpend: membership?.currentPoints,
|
||||||
loginAction: "login success",
|
loginAction: "login success",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
metricsUserTrackingInfo.success({
|
||||||
|
reason: "valid logged in",
|
||||||
|
data: loggedInUserTrackingData,
|
||||||
|
})
|
||||||
|
|
||||||
return loggedInUserTrackingData
|
return loggedInUserTrackingData
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error in userTrackingInfo:", error)
|
metricsUserTrackingInfo.fail(error)
|
||||||
return notLoggedInUserTrackingData
|
return notLoggedInUserTrackingData
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -479,86 +195,22 @@ export const userQueryRouter = router({
|
|||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const { limit, cursor, lang } = input
|
const { limit, cursor, lang } = input
|
||||||
const language = lang || ctx.lang
|
const language = lang || ctx.lang
|
||||||
const params: Record<string, string> = { limit }
|
|
||||||
if (cursor) {
|
const data = await getPreviousStays(
|
||||||
params.offset = cursor
|
ctx.session.token.access_token,
|
||||||
}
|
limit,
|
||||||
getPreviousStaysCounter.add(1, { query: JSON.stringify({ params }) })
|
cursor
|
||||||
console.info(
|
|
||||||
"api.booking.stays.past start",
|
|
||||||
JSON.stringify({ query: { params } })
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const apiResponse = await api.get(
|
if (data) {
|
||||||
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 =
|
const nextCursor =
|
||||||
verifiedData.data.links &&
|
data.links && data.links.offset < data.links.totalCount
|
||||||
verifiedData.data.links.offset < verifiedData.data.links.totalCount
|
? data.links.offset
|
||||||
? verifiedData.data.links.offset
|
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
const updatedData = await updateStaysBookingUrl(
|
const updatedData = await updateStaysBookingUrl(
|
||||||
verifiedData.data.data,
|
data.data,
|
||||||
ctx.session.token.access_token,
|
ctx.session,
|
||||||
language
|
language
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -566,6 +218,8 @@ export const userQueryRouter = router({
|
|||||||
data: updatedData,
|
data: updatedData,
|
||||||
nextCursor,
|
nextCursor,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
}),
|
}),
|
||||||
|
|
||||||
upcoming: languageProtectedProcedure
|
upcoming: languageProtectedProcedure
|
||||||
@@ -573,85 +227,22 @@ export const userQueryRouter = router({
|
|||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const { limit, cursor, lang } = input
|
const { limit, cursor, lang } = input
|
||||||
const language = lang || ctx.lang
|
const language = lang || ctx.lang
|
||||||
const params: Record<string, string> = { limit }
|
|
||||||
if (cursor) {
|
const data = await getUpcomingStays(
|
||||||
params.offset = cursor
|
ctx.session.token.access_token,
|
||||||
}
|
limit,
|
||||||
getUpcomingStaysCounter.add(1, {
|
cursor
|
||||||
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) {
|
if (data) {
|
||||||
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 =
|
const nextCursor =
|
||||||
verifiedData.data.links &&
|
data.links && data.links.offset < data.links.totalCount
|
||||||
verifiedData.data.links.offset < verifiedData.data.links.totalCount
|
? data.links.offset
|
||||||
? verifiedData.data.links.offset
|
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
const updatedData = await updateStaysBookingUrl(
|
const updatedData = await updateStaysBookingUrl(
|
||||||
verifiedData.data.data,
|
data.data,
|
||||||
ctx.session.token.access_token,
|
ctx.session,
|
||||||
language
|
language
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -659,6 +250,8 @@ export const userQueryRouter = router({
|
|||||||
data: updatedData,
|
data: updatedData,
|
||||||
nextCursor,
|
nextCursor,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
transaction: router({
|
transaction: router({
|
||||||
@@ -667,11 +260,18 @@ export const userQueryRouter = router({
|
|||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const { limit, page } = input
|
const { limit, page } = input
|
||||||
|
|
||||||
getFriendTransactionsCounter.add(1)
|
const friendTransactionsCounter = createCounter(
|
||||||
console.info(
|
"trpc.user.transactions",
|
||||||
"api.transaction.friendTransactions start",
|
"friendTransactions"
|
||||||
JSON.stringify({})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const metricsFriendTransactions = friendTransactionsCounter.init({
|
||||||
|
limit,
|
||||||
|
page,
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsFriendTransactions.start()
|
||||||
|
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
api.endpoints.v1.Profile.Transaction.friendTransactions,
|
api.endpoints.v1.Profile.Transaction.friendTransactions,
|
||||||
{
|
{
|
||||||
@@ -682,61 +282,20 @@ export const userQueryRouter = router({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
// switch (apiResponse.status) {
|
await metricsFriendTransactions.httpError(apiResponse)
|
||||||
// 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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiJson = await apiResponse.json()
|
const apiJson = await apiResponse.json()
|
||||||
const verifiedData = getFriendTransactionsSchema.safeParse(apiJson)
|
const verifiedData = getFriendTransactionsSchema.safeParse(apiJson)
|
||||||
if (!verifiedData.success) {
|
if (!verifiedData.success) {
|
||||||
getFriendTransactionsFailCounter.add(1, {
|
metricsFriendTransactions.validationError(verifiedData.error)
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(verifiedData.error),
|
|
||||||
})
|
|
||||||
console.error(
|
|
||||||
"api.transaction.friendTransactions validation error ",
|
|
||||||
JSON.stringify({ error: verifiedData.error })
|
|
||||||
)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getFriendTransactionsSuccessCounter.add(1)
|
|
||||||
console.info(
|
|
||||||
"api.transaction.friendTransactions success",
|
|
||||||
JSON.stringify({})
|
|
||||||
)
|
|
||||||
|
|
||||||
const updatedData = await updateStaysBookingUrl(
|
const updatedData = await updateStaysBookingUrl(
|
||||||
verifiedData.data.data,
|
verifiedData.data.data,
|
||||||
ctx.session.token.access_token,
|
ctx.session,
|
||||||
ctx.lang
|
ctx.lang
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -763,7 +322,7 @@ export const userQueryRouter = router({
|
|||||||
|
|
||||||
const slicedData = pageData.slice(limit * (page - 1), limit * page)
|
const slicedData = pageData.slice(limit * (page - 1), limit * page)
|
||||||
|
|
||||||
return {
|
const result = {
|
||||||
data: {
|
data: {
|
||||||
transactions: slicedData.map(({ type, attributes }) => {
|
transactions: slicedData.map(({ type, attributes }) => {
|
||||||
return {
|
return {
|
||||||
@@ -786,6 +345,10 @@ export const userQueryRouter = router({
|
|||||||
totalPages: Math.ceil(pageData.length / limit),
|
totalPages: Math.ceil(pageData.length / limit),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
metricsFriendTransactions.success()
|
||||||
|
|
||||||
|
return result
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,73 +1,327 @@
|
|||||||
import { metrics } from "@opentelemetry/api"
|
import { countries } from "@/constants/countries"
|
||||||
|
|
||||||
import { myBookingPath } from "@/constants/myBooking"
|
import { myBookingPath } from "@/constants/myBooking"
|
||||||
import { myStay } from "@/constants/routes/myStay"
|
import { myStay } from "@/constants/routes/myStay"
|
||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
import * as api from "@/lib/api"
|
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 { 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 { Lang } from "@/constants/languages"
|
||||||
import type { FriendTransaction, Stay } from "./output"
|
|
||||||
|
|
||||||
const meter = metrics.getMeter("trpc.user")
|
export const getVerifiedUser = cache(
|
||||||
const getProfileCounter = meter.createCounter("trpc.user.profile")
|
async ({
|
||||||
const getProfileSuccessCounter = meter.createCounter(
|
session,
|
||||||
"trpc.user.profile-success"
|
includeExtendedPartnerData,
|
||||||
)
|
}: {
|
||||||
const getProfileFailCounter = meter.createCounter("trpc.user.profile-fail")
|
session: Session
|
||||||
|
includeExtendedPartnerData?: boolean
|
||||||
|
}) => {
|
||||||
|
const getVerifiedUserCounter = createCounter("user", "getVerifiedUser")
|
||||||
|
const metricsGetVerifiedUser = getVerifiedUserCounter.init()
|
||||||
|
|
||||||
async function updateStaysBookingUrl(
|
metricsGetVerifiedUser.start()
|
||||||
data: Stay[],
|
|
||||||
token: string,
|
|
||||||
lang: Lang
|
|
||||||
): Promise<Stay[]>
|
|
||||||
|
|
||||||
async function updateStaysBookingUrl(
|
const now = Date.now()
|
||||||
data: FriendTransaction[],
|
if (session.token.expires_at && session.token.expires_at < now) {
|
||||||
token: string,
|
metricsGetVerifiedUser.dataError(`Token expired`)
|
||||||
lang: Lang
|
return { error: true, cause: "token_expired" } as const
|
||||||
): Promise<FriendTransaction[]>
|
}
|
||||||
|
|
||||||
async function updateStaysBookingUrl(
|
const apiResponse = await api.get(
|
||||||
data: Stay[] | FriendTransaction[],
|
api.endpoints.v2.Profile.profile,
|
||||||
token: string,
|
{
|
||||||
lang: Lang
|
|
||||||
) {
|
|
||||||
// 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: {
|
headers: {
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${session.token.access_token}`,
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
|
includeExtendedPartnerData
|
||||||
|
? { includes: "extendedPartnerInformation" }
|
||||||
|
: {}
|
||||||
|
)
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
getProfileFailCounter.add(1, { error: JSON.stringify(apiResponse) })
|
await metricsGetVerifiedUser.httpError(apiResponse)
|
||||||
console.info(
|
|
||||||
"api.user.profile updatebookingurl error",
|
if (apiResponse.status === 401) {
|
||||||
JSON.stringify({ error: apiResponse })
|
return { error: true, cause: "unauthorized" } as const
|
||||||
)
|
} else if (apiResponse.status === 403) {
|
||||||
return data
|
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()
|
const apiJson = await apiResponse.json()
|
||||||
if (!apiJson.data?.attributes) {
|
if (!apiJson.data?.attributes) {
|
||||||
return data
|
metricsGetVerifiedUser.dataError(
|
||||||
|
`Missing data attributes in API response`,
|
||||||
|
{
|
||||||
|
data: apiJson,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getProfileSuccessCounter.add(1)
|
const verifiedData = getUserSchema.safeParse(apiJson)
|
||||||
console.info("api.user.profile updatebookingurl success", JSON.stringify({}))
|
if (!verifiedData.success) {
|
||||||
|
metricsGetVerifiedUser.validationError(verifiedData.error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
metricsGetVerifiedUser.success()
|
||||||
|
|
||||||
|
return verifiedData
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export 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 async function getPreviousStays(
|
||||||
|
accessToken: string,
|
||||||
|
limit: number = 10,
|
||||||
|
cursor?: string
|
||||||
|
) {
|
||||||
|
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) {
|
||||||
|
await metricsGetPreviousStays.httpError(apiResponse)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiJson = await apiResponse.json()
|
||||||
|
|
||||||
|
const verifiedData = getStaysSchema.safeParse(apiJson)
|
||||||
|
if (!verifiedData.success) {
|
||||||
|
metricsGetPreviousStays.validationError(verifiedData.error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
metricsGetPreviousStays.success()
|
||||||
|
|
||||||
|
return verifiedData.data
|
||||||
|
}
|
||||||
|
|
||||||
|
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) => {
|
return data.map((d) => {
|
||||||
const originalString =
|
const originalString =
|
||||||
d.attributes.confirmationNumber.toString() +
|
d.attributes.confirmationNumber.toString() + "," + user.data.lastName
|
||||||
"," +
|
|
||||||
apiJson.data.attributes.lastName
|
|
||||||
const encryptedBookingValue = encrypt(originalString)
|
const encryptedBookingValue = encrypt(originalString)
|
||||||
|
|
||||||
// Get base URL with fallback for ephemeral environments (like deploy previews).
|
// Get base URL with fallback for ephemeral environments (like deploy previews).
|
||||||
@@ -88,7 +342,7 @@ async function updateStaysBookingUrl(
|
|||||||
if (encryptedBookingValue) {
|
if (encryptedBookingValue) {
|
||||||
bookingUrl.searchParams.set("RefId", encryptedBookingValue)
|
bookingUrl.searchParams.set("RefId", encryptedBookingValue)
|
||||||
} else {
|
} else {
|
||||||
bookingUrl.searchParams.set("lastName", apiJson.data.attributes.lastName)
|
bookingUrl.searchParams.set("lastName", user.data.lastName)
|
||||||
bookingUrl.searchParams.set(
|
bookingUrl.searchParams.set(
|
||||||
"bookingId",
|
"bookingId",
|
||||||
d.attributes.confirmationNumber.toString()
|
d.attributes.confirmationNumber.toString()
|
||||||
@@ -103,6 +357,7 @@ async function updateStaysBookingUrl(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export { updateStaysBookingUrl }
|
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 { env } from "@/env/server"
|
||||||
|
import { createCounter } from "@/server/telemetry"
|
||||||
|
|
||||||
import { getCacheClient } from "@/services/dataCache"
|
import { getCacheClient } from "@/services/dataCache"
|
||||||
|
|
||||||
import type { ServiceTokenResponse } from "@/types/tokens"
|
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() {
|
export async function getServiceToken() {
|
||||||
const tracer = trace.getTracer("getServiceToken")
|
const tracer = trace.getTracer("getServiceToken")
|
||||||
|
|
||||||
@@ -62,15 +53,32 @@ async function getOrSetServiceTokenFromCache(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getJwt(scopes: string[]) {
|
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 jwt = await fetchServiceToken(scopes)
|
||||||
|
|
||||||
const expiresAt = Date.now() + jwt.expires_in * 1000
|
const expiresAt = Date.now() + jwt.expires_in * 1000
|
||||||
|
|
||||||
|
metricsGetJwt.success()
|
||||||
|
|
||||||
return { expiresAt, jwt }
|
return { expiresAt, jwt }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchServiceToken(scopes: string[]) {
|
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`, {
|
const response = await fetch(`${env.CURITY_ISSUER_USER}/oauth/v2/token`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -87,36 +95,23 @@ async function fetchServiceToken(scopes: string[]) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
await metricsFetchServiceToken.httpError(response)
|
||||||
|
|
||||||
const text = await response.text()
|
const text = await response.text()
|
||||||
const error = {
|
throw new Error(
|
||||||
|
`[fetchServiceToken] Failed to obtain service token: ${JSON.stringify({
|
||||||
status: response.status,
|
status: response.status,
|
||||||
statusText: response.statusText,
|
statusText: response.statusText,
|
||||||
text,
|
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)}`
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.json() as Promise<ServiceTokenResponse>
|
const result = response.json() as Promise<ServiceTokenResponse>
|
||||||
|
|
||||||
|
metricsFetchServiceToken.success()
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
function getServiceTokenCacheKey(scopes: string[]): string {
|
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
|
export interface PackagesInput
|
||||||
extends z.input<typeof roomPackagesInputSchema> {}
|
extends z.input<typeof roomPackagesInputSchema> {}
|
||||||
|
|
||||||
|
export interface PackagesOutput
|
||||||
|
extends z.output<typeof roomPackagesInputSchema> {}
|
||||||
|
|
||||||
export type Packages = z.output<typeof packagesSchema>
|
export type Packages = z.output<typeof packagesSchema>
|
||||||
export type Package = NonNullable<Packages>[number]
|
export type Package = NonNullable<Packages>[number]
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ export type HotelsByHotelIdsAvailabilityInputSchema = z.output<
|
|||||||
export type RoomsAvailabilityInputSchema = z.input<
|
export type RoomsAvailabilityInputSchema = z.input<
|
||||||
typeof selectRateRoomsAvailabilityInputSchema
|
typeof selectRateRoomsAvailabilityInputSchema
|
||||||
>
|
>
|
||||||
|
export type RoomsAvailabilityOutputSchema = z.output<
|
||||||
|
typeof selectRateRoomsAvailabilityInputSchema
|
||||||
|
>
|
||||||
export type RoomsAvailabilityInputRoom =
|
export type RoomsAvailabilityInputRoom =
|
||||||
RoomsAvailabilityInputSchema["booking"]["rooms"][number]
|
RoomsAvailabilityInputSchema["booking"]["rooms"][number]
|
||||||
export type RoomsAvailabilityExtendedInputSchema = z.input<
|
export type RoomsAvailabilityExtendedInputSchema = z.input<
|
||||||
|
|||||||
153
yarn.lock
153
yarn.lock
@@ -6402,6 +6402,7 @@ __metadata:
|
|||||||
"@types/geojson": "npm:^7946.0.16"
|
"@types/geojson": "npm:^7946.0.16"
|
||||||
"@types/jest": "npm:^29.5.12"
|
"@types/jest": "npm:^29.5.12"
|
||||||
"@types/json-stable-stringify-without-jsonify": "npm:^1.0.2"
|
"@types/json-stable-stringify-without-jsonify": "npm:^1.0.2"
|
||||||
|
"@types/lodash-es": "npm:^4"
|
||||||
"@types/node": "npm:^20"
|
"@types/node": "npm:^20"
|
||||||
"@types/react": "npm:^18"
|
"@types/react": "npm:^18"
|
||||||
"@types/react-dom": "npm:^18"
|
"@types/react-dom": "npm:^18"
|
||||||
@@ -6429,6 +6430,7 @@ __metadata:
|
|||||||
eslint-plugin-simple-import-sort: "npm:^12.1.0"
|
eslint-plugin-simple-import-sort: "npm:^12.1.0"
|
||||||
fast-deep-equal: "npm:^3.1.3"
|
fast-deep-equal: "npm:^3.1.3"
|
||||||
fetch-retry: "npm:^6.0.0"
|
fetch-retry: "npm:^6.0.0"
|
||||||
|
flat: "npm:^6.0.1"
|
||||||
framer-motion: "npm:^11.3.28"
|
framer-motion: "npm:^11.3.28"
|
||||||
fuse.js: "npm:^7.1.0"
|
fuse.js: "npm:^7.1.0"
|
||||||
graphql: "npm:^16.8.1"
|
graphql: "npm:^16.8.1"
|
||||||
@@ -6446,6 +6448,7 @@ __metadata:
|
|||||||
json-stable-stringify-without-jsonify: "npm:^1.0.1"
|
json-stable-stringify-without-jsonify: "npm:^1.0.1"
|
||||||
libphonenumber-js: "npm:^1.10.60"
|
libphonenumber-js: "npm:^1.10.60"
|
||||||
lint-staged: "npm:^15.2.2"
|
lint-staged: "npm:^15.2.2"
|
||||||
|
lodash-es: "npm:^4.17.21"
|
||||||
material-symbols: "npm:^0.29.0"
|
material-symbols: "npm:^0.29.0"
|
||||||
nanoid: "npm:^5.0.9"
|
nanoid: "npm:^5.0.9"
|
||||||
netlify-plugin-cypress: "npm:^2.2.1"
|
netlify-plugin-cypress: "npm:^2.2.1"
|
||||||
@@ -6464,13 +6467,13 @@ __metadata:
|
|||||||
react-material-symbols: "npm:^4.4.0"
|
react-material-symbols: "npm:^4.4.0"
|
||||||
react-to-print: "npm:^3.0.2"
|
react-to-print: "npm:^3.0.2"
|
||||||
schema-dts: "npm:^1.1.2"
|
schema-dts: "npm:^1.1.2"
|
||||||
secure-json-parse: "npm:^4.0.0"
|
|
||||||
server-only: "npm:^0.0.1"
|
server-only: "npm:^0.0.1"
|
||||||
slugify: "npm:^1.6.6"
|
slugify: "npm:^1.6.6"
|
||||||
sonner: "npm:^1.7.0"
|
sonner: "npm:^1.7.0"
|
||||||
start-server-and-test: "npm:^2.0.3"
|
start-server-and-test: "npm:^2.0.3"
|
||||||
supercluster: "npm:^8.0.1"
|
supercluster: "npm:^8.0.1"
|
||||||
superjson: "npm:^2.2.1"
|
superjson: "npm:^2.2.1"
|
||||||
|
ts-jest: "npm:^29.3.2"
|
||||||
ts-morph: "npm:^25.0.1"
|
ts-morph: "npm:^25.0.1"
|
||||||
ts-node: "npm:^10.9.2"
|
ts-node: "npm:^10.9.2"
|
||||||
typescript: "npm:5.4.5"
|
typescript: "npm:5.4.5"
|
||||||
@@ -8227,6 +8230,22 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"@types/mdx@npm:^2.0.0":
|
||||||
version: 2.0.13
|
version: 2.0.13
|
||||||
resolution: "@types/mdx@npm:2.0.13"
|
resolution: "@types/mdx@npm:2.0.13"
|
||||||
@@ -9608,7 +9627,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"async@npm:^3.2.0":
|
"async@npm:^3.2.0, async@npm:^3.2.3":
|
||||||
version: 3.2.6
|
version: 3.2.6
|
||||||
resolution: "async@npm:3.2.6"
|
resolution: "async@npm:3.2.6"
|
||||||
checksum: 10c0/36484bb15ceddf07078688d95e27076379cc2f87b10c03b6dd8a83e89475a3c8df5848859dd06a4c95af1e4c16fc973de0171a77f18ea00be899aca2a4f85e70
|
checksum: 10c0/36484bb15ceddf07078688d95e27076379cc2f87b10c03b6dd8a83e89475a3c8df5848859dd06a4c95af1e4c16fc973de0171a77f18ea00be899aca2a4f85e70
|
||||||
@@ -9961,6 +9980,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"bser@npm:2.1.1":
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
resolution: "bser@npm:2.1.1"
|
resolution: "bser@npm:2.1.1"
|
||||||
@@ -10193,7 +10221,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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
|
version: 4.1.2
|
||||||
resolution: "chalk@npm:4.1.2"
|
resolution: "chalk@npm:4.1.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -11588,6 +11616,17 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"electron-to-chromium@npm:^1.5.73":
|
||||||
version: 1.5.103
|
version: 1.5.103
|
||||||
resolution: "electron-to-chromium@npm:1.5.103"
|
resolution: "electron-to-chromium@npm:1.5.103"
|
||||||
@@ -12646,7 +12685,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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
|
version: 2.1.0
|
||||||
resolution: "fast-json-stable-stringify@npm:2.1.0"
|
resolution: "fast-json-stable-stringify@npm:2.1.0"
|
||||||
checksum: 10c0/7f081eb0b8a64e0057b3bb03f974b3ef00135fbf36c1c710895cd9300f13c94ba809bb3a81cf4e1b03f6e5285610a61abbd7602d0652de423144dfee5a389c9b
|
checksum: 10c0/7f081eb0b8a64e0057b3bb03f974b3ef00135fbf36c1c710895cd9300f13c94ba809bb3a81cf4e1b03f6e5285610a61abbd7602d0652de423144dfee5a389c9b
|
||||||
@@ -12758,6 +12797,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"fill-range@npm:^7.1.1":
|
||||||
version: 7.1.1
|
version: 7.1.1
|
||||||
resolution: "fill-range@npm:7.1.1"
|
resolution: "fill-range@npm:7.1.1"
|
||||||
@@ -12807,6 +12855,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"flatted@npm:^3.2.9":
|
||||||
version: 3.3.3
|
version: 3.3.3
|
||||||
resolution: "flatted@npm:3.3.3"
|
resolution: "flatted@npm:3.3.3"
|
||||||
@@ -14587,6 +14644,20 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"jest-changed-files@npm:^29.7.0":
|
||||||
version: 29.7.0
|
version: 29.7.0
|
||||||
resolution: "jest-changed-files@npm:29.7.0"
|
resolution: "jest-changed-files@npm:29.7.0"
|
||||||
@@ -14972,7 +15043,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"jest-util@npm:^29.7.0":
|
"jest-util@npm:^29.0.0, jest-util@npm:^29.7.0":
|
||||||
version: 29.7.0
|
version: 29.7.0
|
||||||
resolution: "jest-util@npm:29.7.0"
|
resolution: "jest-util@npm:29.7.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -15849,6 +15920,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"lodash.merge@npm:^4.6.2":
|
||||||
version: 4.6.2
|
version: 4.6.2
|
||||||
resolution: "lodash.merge@npm:4.6.2"
|
resolution: "lodash.merge@npm:4.6.2"
|
||||||
@@ -16207,7 +16285,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"make-error@npm:^1.1.1":
|
"make-error@npm:^1.1.1, make-error@npm:^1.3.6":
|
||||||
version: 1.3.6
|
version: 1.3.6
|
||||||
resolution: "make-error@npm:1.3.6"
|
resolution: "make-error@npm:1.3.6"
|
||||||
checksum: 10c0/171e458d86854c6b3fc46610cfacf0b45149ba043782558c6875d9f42f222124384ad0b468c92e996d815a8a2003817a710c0a160e49c1c394626f76fa45396f
|
checksum: 10c0/171e458d86854c6b3fc46610cfacf0b45149ba043782558c6875d9f42f222124384ad0b468c92e996d815a8a2003817a710c0a160e49c1c394626f76fa45396f
|
||||||
@@ -16437,6 +16515,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"minimatch@npm:^8.0.2":
|
||||||
version: 8.0.4
|
version: 8.0.4
|
||||||
resolution: "minimatch@npm:8.0.4"
|
resolution: "minimatch@npm:8.0.4"
|
||||||
@@ -19418,13 +19505,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"semver@npm:^5.6.0":
|
||||||
version: 5.7.2
|
version: 5.7.2
|
||||||
resolution: "semver@npm:5.7.2"
|
resolution: "semver@npm:5.7.2"
|
||||||
@@ -19443,7 +19523,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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
|
version: 7.7.1
|
||||||
resolution: "semver@npm:7.7.1"
|
resolution: "semver@npm:7.7.1"
|
||||||
bin:
|
bin:
|
||||||
@@ -20799,6 +20879,44 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"ts-morph@npm:^25.0.1":
|
||||||
version: 25.0.1
|
version: 25.0.1
|
||||||
resolution: "ts-morph@npm:25.0.1"
|
resolution: "ts-morph@npm:25.0.1"
|
||||||
@@ -21029,6 +21147,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"type-is@npm:^1.6.16, type-is@npm:^1.6.18":
|
||||||
version: 1.6.18
|
version: 1.6.18
|
||||||
resolution: "type-is@npm:1.6.18"
|
resolution: "type-is@npm:1.6.18"
|
||||||
|
|||||||
Reference in New Issue
Block a user