Feature/turbopack * . * . * pin import-in-the-middle * update marker * revert back to using *.graphql.ts Approved-by: Linus Flood
122 lines
3.6 KiB
TypeScript
122 lines
3.6 KiB
TypeScript
import "server-only"
|
|
|
|
import { ClientError, type GraphQLClient } from "graphql-request"
|
|
|
|
import { Lang } from "@scandic-hotels/common/constants/language"
|
|
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
|
|
|
|
import { env } from "../../env/server"
|
|
|
|
import type { DocumentNode } from "graphql"
|
|
|
|
import type { Data } from "../types/requestData"
|
|
|
|
const requestLogger = createLogger("graphql-request")
|
|
export async function request<T>(
|
|
client: GraphQLClient,
|
|
query: string | DocumentNode,
|
|
variables?: {},
|
|
params?: RequestInit
|
|
): Promise<Data<T>> {
|
|
try {
|
|
client.setHeaders({
|
|
access_token: env.CMS_ACCESS_TOKEN,
|
|
branch: env.CMS_BRANCH,
|
|
"Content-Type": "application/json",
|
|
...params?.headers,
|
|
})
|
|
|
|
client.requestConfig.cache = params?.cache
|
|
client.requestConfig.next = params?.next
|
|
|
|
try {
|
|
// @ts-expect-error: query can be undefined (?)
|
|
const operationName = (query as DocumentNode).definitions.find(
|
|
(d) => d.kind === "OperationDefinition" && d.operation === "query"
|
|
// @ts-expect-error: name does not exist (?)
|
|
).name.value
|
|
|
|
requestLogger.debug(`[gql] Sending graphql request to ${env.CMS_URL}`, {
|
|
operationName,
|
|
variables,
|
|
})
|
|
} catch (e) {
|
|
requestLogger.error(`[gql] Unable to extract operation name from query`, {
|
|
query,
|
|
error: e,
|
|
})
|
|
|
|
requestLogger.debug(`[gql] Sending graphql request to ${env.CMS_URL}`, {
|
|
operationName: "<Unable to extract>",
|
|
variables,
|
|
})
|
|
}
|
|
|
|
const response = await client.request<T>({
|
|
document: query,
|
|
variables,
|
|
requestHeaders: {
|
|
"User-Agent": `scandic-web-${env.SENTRY_ENVIRONMENT}`,
|
|
},
|
|
})
|
|
|
|
try {
|
|
// @ts-expect-error: query can be undefined (?)
|
|
const operationName = (query as DocumentNode).definitions.find(
|
|
(d) => d.kind === "OperationDefinition" && d.operation === "query"
|
|
// @ts-expect-error: name does not exist (?)
|
|
).name.value
|
|
|
|
requestLogger.debug(`[gql] Response for ${env.CMS_URL}`, {
|
|
response,
|
|
operationName,
|
|
variables,
|
|
})
|
|
} catch (e) {
|
|
requestLogger.error(`[gql] Unable to extract operation name from query`, {
|
|
query,
|
|
error: e,
|
|
})
|
|
|
|
requestLogger.debug(`[gql] Response for ${env.CMS_URL}`, {
|
|
response,
|
|
operationName: "<Unable to extract>",
|
|
variables,
|
|
})
|
|
}
|
|
|
|
return { data: response }
|
|
} catch (error) {
|
|
if (error instanceof ClientError) {
|
|
if (error.response.errors?.length) {
|
|
const failedToFetchItem = error.response.errors.find(
|
|
(err) => err.message === "Failed to fetch item"
|
|
)
|
|
if (
|
|
failedToFetchItem &&
|
|
failedToFetchItem.extensions?.errors === "Object not found!" &&
|
|
failedToFetchItem.path?.find((p) => Lang[p as Lang])
|
|
) {
|
|
/**
|
|
* Because of Contentstacks totally obscure way of implementing
|
|
* GraphQL where they throw an error when you are querying for
|
|
* a single item that is nullable and doesn't exist. This leads
|
|
* to the issue where when we have a page that is missing a published
|
|
* version for one language, it throws an error which we have to recover
|
|
* from here since it isn't an error.
|
|
*/
|
|
|
|
return { data: error.response.data as T }
|
|
}
|
|
}
|
|
}
|
|
|
|
requestLogger.error(
|
|
`[gql] Error sending graphql request to ${env.CMS_URL}`,
|
|
error
|
|
)
|
|
|
|
throw new Error("Failed to fetch data from CMS", { cause: { error } })
|
|
}
|
|
}
|