1
0
mirror of https://github.com/sasjs/adapter.git synced 2025-12-11 01:14:36 +00:00

fix: refactor the default callback for axios interceptors

This commit is contained in:
2025-02-28 18:21:44 +05:00
parent 851b8fce2a
commit be3ce56b85
3 changed files with 89 additions and 106 deletions

View File

@@ -1170,8 +1170,8 @@ export default class SASjs {
* @param errorCallBack - function that should be triggered on every HTTP response with the status different from 2**.
*/
public enableVerboseMode(
successCallBack?: (response: AxiosResponse | AxiosError) => AxiosResponse,
errorCallBack?: (response: AxiosResponse | AxiosError) => AxiosResponse
successCallBack?: (response: AxiosResponse) => AxiosResponse,
errorCallBack?: (response: AxiosError) => AxiosError
) {
this.requestClient?.enableVerboseMode(successCallBack, errorCallBack)
}

View File

@@ -1,5 +1,6 @@
import {
AxiosError,
AxiosHeaders,
AxiosInstance,
AxiosRequestConfig,
AxiosResponse
@@ -413,103 +414,85 @@ export class RequestClient implements HttpClient {
return bodyLines.join('\n')
}
private defaultInterceptionCallBack = (
axiosResponse: AxiosResponse | AxiosError
) => {
private handleAxiosResponse = (response: AxiosResponse) => {
const { status, config, request, data } = response
const reqHeaders = request?._header ?? 'Not provided\n'
const rawHeaders = request?.res?.rawHeaders ?? ['Not provided']
const resHeaders = this.formatHeaders(rawHeaders)
const parsedResBody = this.parseInterceptedBody(data)
process.logger?.info(`HTTP Request (first 50 lines):
${reqHeaders}${this.parseInterceptedBody(config.data)}
HTTP Response Code: ${this.prettifyString(status)}
HTTP Response (first 50 lines):
${resHeaders}${parsedResBody ? `\n\n${parsedResBody}` : ''}
`)
return response
}
private handleAxiosError = (error: AxiosError) => {
// Message indicating absent value.
const noValueMessage = 'Not provided'
const { response, request, config } = error
// Fallback request object that can be safely used to form request summary.
type FallbackRequest = { _header?: string; res: { rawHeaders: string[] } }
// _header is not present in responses with status 1**
// rawHeaders are not present in responses with status 1**
let fallbackRequest: FallbackRequest = {
_header: `${noValueMessage}\n`,
let fallbackRequest = {
_header: noValueMessage,
res: { rawHeaders: [noValueMessage] }
}
// Fallback response object that can be safely used to form response summary.
type FallbackResponse = {
status?: number | string
request?: FallbackRequest
config: { data?: string }
data?: unknown
}
let fallbackResponse: FallbackResponse = axiosResponse
if (axios.isAxiosError(axiosResponse)) {
const { response, request, config } = axiosResponse
// Try to use axiosResponse.response to form response summary.
if (response) {
fallbackResponse = response
} else {
// Try to use axiosResponse.request to form request summary.
if (request) {
const { _header, _currentRequest } = request
// Try to use axiosResponse.request._header to form request summary.
if (_header) {
fallbackRequest._header = _header
}
// Try to use axiosResponse.request._currentRequest._header to form request summary.
else if (_currentRequest && _currentRequest._header) {
fallbackRequest._header = _currentRequest._header
}
const { res } = request
// Try to use axiosResponse.request.res.rawHeaders to form request summary.
if (res && res.rawHeaders) {
fallbackRequest.res.rawHeaders = res.rawHeaders
}
}
// Fallback config that can be safely used to form response summary.
const fallbackConfig = { data: noValueMessage }
fallbackResponse = {
status: noValueMessage,
request: fallbackRequest,
config: config || fallbackConfig,
data: noValueMessage
}
if (request) {
fallbackRequest = {
_header:
request._header ?? request._currentRequest?._header ?? noValueMessage,
res: { rawHeaders: request.res?.rawHeaders ?? [noValueMessage] }
}
}
const { status, config, request, data: resData } = fallbackResponse
const { data: reqData } = config
const { _header: reqHeaders, res } = request || fallbackRequest
const { rawHeaders } = res
// Fallback response object that can be safely used to form response summary.
let fallbackResponse = response || {
status: noValueMessage,
request: fallbackRequest,
config: config || { data: noValueMessage, headers: new AxiosHeaders() },
data: noValueMessage
}
// Converts an array of strings into a single string with the following format:
// <headerName>: <headerValue>
const resHeaders = rawHeaders.reduce(
(acc: string, value: string, i: number) => {
if (i % 2 === 0) {
acc += `${i === 0 ? '' : '\n'}${value}`
} else {
acc += `: ${value}`
}
return acc
},
''
)
const { status, request: req, data: resData } = fallbackResponse
const { _header: reqHeaders, res } = req
const resHeaders = this.formatHeaders(res.rawHeaders)
const parsedResBody = this.parseInterceptedBody(resData)
// HTTP response summary.
process.logger?.info(`HTTP Request (first 50 lines):
${reqHeaders}${this.parseInterceptedBody(reqData)}
${reqHeaders}${this.parseInterceptedBody(config?.data)}
HTTP Response Code: ${this.prettifyString(status)}
HTTP Response (first 50 lines):
${resHeaders}${parsedResBody ? `\n\n${parsedResBody}` : ''}
`)
HTTP Response Code: ${this.prettifyString(status)}
return error
}
HTTP Response (first 50 lines):
${resHeaders}${parsedResBody ? `\n\n${parsedResBody}` : ''}
`)
return axiosResponse
// Converts an array of strings into a single string with the following format:
// <headerName>: <headerValue>
private formatHeaders = (rawHeaders: string[]): string => {
return rawHeaders.reduce((acc, value, i) => {
if (i % 2 === 0) {
acc += `${i === 0 ? '' : '\n'}${value}`
} else {
acc += `: ${value}`
}
return acc
}, '')
}
/**
@@ -529,8 +512,8 @@ ${resHeaders}${parsedResBody ? `\n\n${parsedResBody}` : ''}
* @param errorCallBack - function that should be triggered on every HTTP response with the status different from 2**.
*/
public enableVerboseMode = (
successCallBack = this.defaultInterceptionCallBack,
errorCallBack = this.defaultInterceptionCallBack
successCallBack = this.handleAxiosResponse,
errorCallBack = this.handleAxiosError
) => {
this.httpInterceptor = this.httpClient.interceptors.response.use(
successCallBack,

View File

@@ -75,7 +75,7 @@ describe('RequestClient', () => {
expect(rejectionErrorMessage).toEqual(expectedError.message)
})
describe('defaultInterceptionCallBack', () => {
describe('defaultInterceptionCallBacks for successful requests and failed requests', () => {
const reqHeaders = `POST https://sas.server.com/compute/sessions/session_id/jobs HTTP/1.1
Accept: application/json
Content-Type: application/json
@@ -181,7 +181,7 @@ Connection: close
} as AxiosError
const requestClient = new RequestClient('')
requestClient['defaultInterceptionCallBack'](mockedAxiosError)
requestClient['handleAxiosError'](mockedAxiosError)
const noValueMessage = 'Not provided'
const expectedLog = `HTTP Request (first 50 lines):
@@ -214,7 +214,7 @@ ${noValueMessage}
}
const requestClient = new RequestClient('')
requestClient['defaultInterceptionCallBack'](mockedResponse)
requestClient['handleAxiosResponse'](mockedResponse)
const expectedLog = `HTTP Request (first 50 lines):
${reqHeaders}${requestClient['parseInterceptedBody'](reqData)}
@@ -235,29 +235,29 @@ ${resHeaders[0]}: ${resHeaders[1]}${
it('should log parsed response with status 3**', () => {
const status = getRandomStatus([300, 301, 302, 303, 304, 307, 308])
const mockedResponse: AxiosResponse = {
data: resData,
status,
statusText: '',
headers: {},
config: { data: reqData },
request: { _header: reqHeaders, res: { rawHeaders: resHeaders } }
}
const mockedAxiosError = {
config: {
data: reqData
},
request: {
_currentRequest: {
_header: reqHeaders
}
}
} as AxiosError
const requestClient = new RequestClient('')
requestClient['defaultInterceptionCallBack'](mockedResponse)
requestClient['handleAxiosError'](mockedAxiosError)
const noValueMessage = 'Not provided'
const expectedLog = `HTTP Request (first 50 lines):
${reqHeaders}${requestClient['parseInterceptedBody'](reqData)}
HTTP Response Code: ${requestClient['prettifyString'](status)}
HTTP Response Code: ${requestClient['prettifyString'](noValueMessage)}
HTTP Response (first 50 lines):
${resHeaders[0]}: ${resHeaders[1]}${
requestClient['parseInterceptedBody'](resData)
? `\n\n${requestClient['parseInterceptedBody'](resData)}`
: ''
}
${noValueMessage}
\n${requestClient['parseInterceptedBody'](noValueMessage)}
`
expect((process as any).logger.info).toHaveBeenCalledWith(expectedLog)
@@ -294,7 +294,7 @@ ${resHeaders[0]}: ${resHeaders[1]}${
} as AxiosError
const requestClient = new RequestClient('')
requestClient['defaultInterceptionCallBack'](mockedAxiosError)
requestClient['handleAxiosError'](mockedAxiosError)
const expectedLog = `HTTP Request (first 50 lines):
${reqHeaders}${requestClient['parseInterceptedBody'](reqData)}
@@ -344,7 +344,7 @@ ${resHeaders[0]}: ${resHeaders[1]}${
} as AxiosError
const requestClient = new RequestClient('')
requestClient['defaultInterceptionCallBack'](mockedAxiosError)
requestClient['handleAxiosError'](mockedAxiosError)
const expectedLog = `HTTP Request (first 50 lines):
${reqHeaders}${requestClient['parseInterceptedBody'](reqData)}
@@ -376,8 +376,8 @@ ${resHeaders[0]}: ${resHeaders[1]}${
requestClient.enableVerboseMode()
expect(interceptorSpy).toHaveBeenCalledWith(
requestClient['defaultInterceptionCallBack'],
requestClient['defaultInterceptionCallBack']
requestClient['handleAxiosResponse'],
requestClient['handleAxiosError']
)
})
@@ -388,12 +388,12 @@ ${resHeaders[0]}: ${resHeaders[1]}${
'use'
)
const successCallback = (response: AxiosResponse | AxiosError) => {
const successCallback = (response: AxiosResponse) => {
console.log('success')
return response
}
const failureCallback = (response: AxiosResponse | AxiosError) => {
const failureCallback = (response: AxiosError) => {
console.log('failure')
return response