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:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user