import { type DocumentNode, print } from "graphql" import { GraphQLClient } from "graphql-request" import stringify from "json-stable-stringify-without-jsonify" import { cache as reactCache } from "react" import { type CacheTime, getCacheClient, } from "@scandic-hotels/common/dataCache" import { createLogger } from "@scandic-hotels/common/logger/createLogger" import { env } from "../../env/server" import { getPreviewHash, isPreviewByUid } from "../previewContext" import { request as _request } from "./_request" import { getOperationName } from "./getOperationName" import type { Data } from "../types/requestData" export async function request( query: string | DocumentNode, variables?: Record, cacheOptions?: { key: string | string[] ttl: CacheTime } ): Promise> { const requestLogger = createLogger("graphql-request") const shouldUsePreview = variables?.uid ? isPreviewByUid(variables.uid) : false const doCall = () => internalRequest(query, shouldUsePreview, variables, getPreviewHash()) if (!cacheOptions) { requestLogger.warn("[NO CACHE] for query", query) return doCall() } if (shouldUsePreview) { requestLogger.debug("[NO CACHE] [PREVIEW] for query", query) return doCall() } const queryString = typeof query === "string" ? query : print(query) const variablesString = stringify(variables) const fullQuery = `${queryString}${variablesString}` const queryHash = await sha256(fullQuery) const operationName = getOperationName(query) const cacheKey: string = Array.isArray(cacheOptions.key) ? cacheOptions.key.join("_") : cacheOptions.key const extendedCacheKey = `${operationName}:${queryHash}:${cacheKey}` const _dataCache = await getCacheClient() return _dataCache.cacheOrGet(extendedCacheKey, doCall, cacheOptions.ttl, { includeGitHashInKey: false, }) } function internalRequest( query: string | DocumentNode, shouldUsePreview: boolean, variables?: Record, previewHash?: string ): Promise> { const cmsUrl = shouldUsePreview ? env.CMS_PREVIEW_URL : env.CMS_URL // Creating a new client for each request to avoid conflicting parameters const client = new GraphQLClient(cmsUrl, { fetch: reactCache(async function ( url: URL | RequestInfo, params?: RequestInit ) { return fetch(url, { ...params, signal: AbortSignal.timeout(15_000), }) }) as unknown as typeof fetch, }) const mergedParams = shouldUsePreview && previewHash ? { headers: { live_preview: previewHash, preview_token: env.CMS_PREVIEW_TOKEN, }, } : {} return _request(client, query, variables, mergedParams) } async function sha256(input: string) { const encoder = new TextEncoder() const data = encoder.encode(input) const hashBuffer = await crypto.subtle.digest("SHA-256", data) const hashArray = Array.from(new Uint8Array(hashBuffer)) const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("") return hashHex }