fix: improve error handling

- Do not throw in onError callback for server client. This lets the originating error bubble to Next.js.
- Do not log errors inside the service token procedure, server client onError callback will log all procedure errors.
- Log errors for fetching service token failure.
This commit is contained in:
Michael Zetterberg
2024-10-11 10:42:40 +02:00
committed by Pontus Dreij
parent f63cecc488
commit 194ca601b5
3 changed files with 65 additions and 49 deletions

View File

@@ -50,8 +50,6 @@ export function serverClient() {
redirect(redirectUrl)
}
}
throw internalServerError()
},
})
}

View File

@@ -21,6 +21,7 @@ const fetchServiceTokenFailCounter = meter.createCounter(
async function fetchServiceToken(scopes: string[]) {
fetchServiceTokenCounter.add(1)
const response = await fetch(`${env.CURITY_ISSUER_USER}/oauth/v2/token`, {
method: "POST",
headers: {
@@ -36,52 +37,67 @@ async function fetchServiceToken(scopes: string[]) {
})
if (!response.ok) {
fetchServiceTokenFailCounter.add(1, {
error_type: "http_error",
error: JSON.stringify({
status: response.status,
statusText: response.statusText,
}),
})
throw new Error("Failed to obtain service token")
}
return response.json()
}
export async function getServiceToken(): Promise<ServiceTokenResponse> {
try {
const scopes = ["profile", "hotel", "booking"]
const tag = generateServiceTokenTag(scopes)
const getCachedJwt = unstable_cache(
async (scopes) => {
const jwt = await fetchServiceToken(scopes)
const expiresAt = Date.now() + jwt.expires_in * 1000
return { expiresAt, jwt }
},
[tag],
{ tags: [tag] }
)
const cachedJwt = await getCachedJwt(scopes)
if (cachedJwt.expiresAt < Date.now()) {
console.log(
"trpc.context.serviceToken: Service token expired, revalidating tag"
)
revalidateTag(tag)
console.log(
"trpc.context.serviceToken: Fetching new temporary service token."
)
fetchTempServiceTokenCounter.add(1)
const newToken = await fetchServiceToken(scopes)
return newToken
const text = await response.text()
const error = {
status: response.status,
statusText: response.statusText,
text,
}
return cachedJwt.jwt
} catch (error) {
console.error("Error fetching service token:", error)
throw error
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>
}
export async function getServiceToken() {
const scopes = ["profile", "hotel", "booking"]
const tag = generateServiceTokenTag(scopes)
const getCachedJwt = unstable_cache(
async (scopes) => {
const jwt = await fetchServiceToken(scopes)
const expiresAt = Date.now() + jwt.expires_in * 1000
return { expiresAt, jwt }
},
[tag],
{ tags: [tag] }
)
const cachedJwt = await getCachedJwt(scopes)
if (cachedJwt.expiresAt < Date.now()) {
console.log(
"trpc.context.serviceToken: Service token expired, revalidating tag"
)
revalidateTag(tag)
console.log(
"trpc.context.serviceToken: Fetching new temporary service token."
)
fetchTempServiceTokenCounter.add(1)
const newToken = await fetchServiceToken(scopes)
return newToken
}
return cachedJwt.jwt
}

View File

@@ -124,7 +124,7 @@ export const safeProtectedProcedure = t.procedure.use(async function (opts) {
export const serviceProcedure = t.procedure.use(async (opts) => {
const { access_token } = await getServiceToken()
if (!access_token) {
throw internalServerError(`Failed to obtain service token`)
throw internalServerError(`[serviceProcedure] No service token`)
}
return opts.next({
ctx: {
@@ -144,7 +144,9 @@ export const serviceServerActionProcedure = serverActionProcedure.use(
async (opts) => {
const { access_token } = await getServiceToken()
if (!access_token) {
throw internalServerError("Failed to obtain service token")
throw internalServerError(
"[serviceServerActionProcedure]: No service token"
)
}
return opts.next({
ctx: {