UNPKG

@camunda8/sdk

Version:

[![NPM](https://nodei.co/npm/@camunda8/sdk.png)](https://www.npmjs.com/package/@camunda8/sdk)

140 lines 6.76 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GotRetryConfig = exports.makeBeforeRetryHandlerFor401TokenRetry = exports.gotBeforeErrorHook = exports.gotBeforeRetryHook = exports.beforeCallHook = void 0; /* eslint-disable @typescript-eslint/no-explicit-any */ const debug_1 = require("debug"); const got_1 = require("got"); const AsyncTrace_1 = require("./AsyncTrace"); const CamundaSupportLogger_1 = require("./CamundaSupportLogger"); const GotErrors_1 = require("./GotErrors"); const trace = (0, debug_1.debug)('camunda:gotHooks'); const supportLogger = CamundaSupportLogger_1.CamundaSupportLogger.getInstance(); /** * Capturing useful async stack traces is challenging with got. * See here: https://github.com/sindresorhus/got/blob/main/documentation/async-stack-traces.md * This function stores the call point from the application of got requests. * This enables users to see where the error originated from. */ const beforeCallHook = (options, next) => { if (Object.isFrozen(options.context)) { options.context = { ...options.context, hasRetried: false }; } // If we stored the creation stack in the async context, we can use it to enhance the stack trace of the request. const creationStack = AsyncTrace_1.asyncOperationContext.getStore()?.creationStack; const obj = {}; Error.captureStackTrace(obj, exports.beforeCallHook); options.context.stack = creationStack ? `${creationStack}\n${obj.stack}` : obj.stack; supportLogger.log(`Rest call:`); supportLogger.log(options); return next(options); }; exports.beforeCallHook = beforeCallHook; /** * This function is used to handle 401 errors in got requests. * It will retry the request only once if the error code is 401. * Otherwise, for 429 and 503 errors, it will retry according to the GotRetryConfig. */ const gotBeforeRetryHook = (_, error, retryCount) => { trace('gotBeforeRetryHook called with error:', JSON.stringify(Object.keys(error))); if (error instanceof got_1.RequestError) { const is401 = error.response?.statusCode === 401; const hasRetried = retryCount && retryCount > 0; trace('gotBeforeRetryHook: HTTPError detected:', error.response?.statusCode); // If we have a 401 error, we handle it by retrying the request only once. if (is401 || error.code === '401') { // If we get a 401 error, we will retry the request only once. if (hasRetried) { // If we have already retried, we throw the error to stop retrying. throw error; } } } }; exports.gotBeforeRetryHook = gotBeforeRetryHook; /** * This function adds the call point to the error stack trace of got errors. * This enables users to see where the error originated from. * * It also logs the error to the Camunda Support log. * This is useful for debugging and support purposes. */ const gotBeforeErrorHook = (config) => (error) => { const { request } = error; let detail = ''; if (error instanceof got_1.HTTPError) { error = new GotErrors_1.HTTPError(error.response); try { const details = JSON.parse(error.response?.body || '{detail:""}'); error.statusCode = details.status; detail = details ?? ''; } catch (e) { error.statusCode = 0; } } const enhancedStack = error.options.context.stack?.split('\n'); error.source = enhancedStack ?? ['No enhanced stack trace available']; const method = request?.options.method; const url = request?.options.url.href; error.message += ` (${method} ${url}). ${JSON.stringify(detail)}`; if (enhancedStack) { error.message += `. Enhanced stack trace available as error.source.`; } /** Hinting for error messages. See https://github.com/camunda/camunda-8-js-sdk/issues/456 */ /** Here we reason over the error and the configuration to enrich the message with hints */ if (error.message.includes('Invalid header token')) { // This is a parse error, which means the response header was not valid JSON. // Debugging for https://github.com/camunda/camunda-8-js-sdk/issues/491 error.message += ` (response headers: ${error.response?.headers})`; } if (error.code === '401') { // the call was unauthorized if (config.CAMUNDA_AUTH_STRATEGY === 'OAUTH') { if (request?.options.headers?.authorization) { /** This is a 401 error, but the token is set in the header */ error.message += ' (this may be due to the client credentials not being authorized to access the resource)'; if (config.CAMUNDA_TENANT_ID) { /** We're *probably* making a multi-tenant call. It might have been overridden in the call, but we don't have access to the body */ error.message += `. Is the client credential authorized in the tenant?`; } } } } /** Log details of errors to the Camunda Support log */ supportLogger.log('**ERROR**: Got error during Rest call:'); supportLogger.log({ code: error.code, message: error.message, stack: error.stack, requestOptions: error.request?.options, source: error.source, }); return error; }; exports.gotBeforeErrorHook = gotBeforeErrorHook; /** * * This function is used on a 401 response to retry the request with a new token, one single time. * https://github.com/camunda/camunda-8-js-sdk/issues/125 */ const makeBeforeRetryHandlerFor401TokenRetry = (getHeadersFn) => async (context) => { context.headers.authorization = (await getHeadersFn()).authorization; }; exports.makeBeforeRetryHandlerFor401TokenRetry = makeBeforeRetryHandlerFor401TokenRetry; /** * Retry configuration for got requests. * This configuration is used to retry requests on certain status codes and methods. * We will retry on 429 (Too Many Requests) and 503 (Service Unavailable) status codes. * 503 is used for Camunda 8 to indicate server backpressure. See: https://github.com/camunda/camunda-8-js-sdk/issues/509 * 401 (Unauthorized) is used for OAuth token refreshes. * We will retry only once on 401 (see the BeforeRetryHook), because the worker polls continuously, and a worker that is misconfigured with an invalid secret will retry indefinitely. * This is not ideal, but it is the current behaviour. We need to ensure that such a worker does not flood the broker, so we cause a backoff. */ exports.GotRetryConfig = { methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], statusCodes: [401, 429, 503], }; //# sourceMappingURL=GotHooks.js.map