mirror of
https://github.com/sasjs/adapter.git
synced 2026-01-07 12:30:06 +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**.
|
* @param errorCallBack - function that should be triggered on every HTTP response with the status different from 2**.
|
||||||
*/
|
*/
|
||||||
public enableVerboseMode(
|
public enableVerboseMode(
|
||||||
successCallBack?: (response: AxiosResponse | AxiosError) => AxiosResponse,
|
successCallBack?: (response: AxiosResponse) => AxiosResponse,
|
||||||
errorCallBack?: (response: AxiosResponse | AxiosError) => AxiosResponse
|
errorCallBack?: (response: AxiosError) => AxiosError
|
||||||
) {
|
) {
|
||||||
this.requestClient?.enableVerboseMode(successCallBack, errorCallBack)
|
this.requestClient?.enableVerboseMode(successCallBack, errorCallBack)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
AxiosError,
|
AxiosError,
|
||||||
|
AxiosHeaders,
|
||||||
AxiosInstance,
|
AxiosInstance,
|
||||||
AxiosRequestConfig,
|
AxiosRequestConfig,
|
||||||
AxiosResponse
|
AxiosResponse
|
||||||
@@ -413,95 +414,17 @@ export class RequestClient implements HttpClient {
|
|||||||
return bodyLines.join('\n')
|
return bodyLines.join('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
private defaultInterceptionCallBack = (
|
private handleAxiosResponse = (response: AxiosResponse) => {
|
||||||
axiosResponse: AxiosResponse | AxiosError
|
const { status, config, request, data } = response
|
||||||
) => {
|
|
||||||
// Message indicating absent value.
|
|
||||||
const noValueMessage = 'Not provided'
|
|
||||||
|
|
||||||
// Fallback request object that can be safely used to form request summary.
|
const reqHeaders = request?._header ?? 'Not provided\n'
|
||||||
type FallbackRequest = { _header?: string; res: { rawHeaders: string[] } }
|
const rawHeaders = request?.res?.rawHeaders ?? ['Not provided']
|
||||||
// _header is not present in responses with status 1**
|
|
||||||
// rawHeaders are not present in responses with status 1**
|
|
||||||
let fallbackRequest: FallbackRequest = {
|
|
||||||
_header: `${noValueMessage}\n`,
|
|
||||||
res: { rawHeaders: [noValueMessage] }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback response object that can be safely used to form response summary.
|
const resHeaders = this.formatHeaders(rawHeaders)
|
||||||
type FallbackResponse = {
|
const parsedResBody = this.parseInterceptedBody(data)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { status, config, request, data: resData } = fallbackResponse
|
|
||||||
const { data: reqData } = config
|
|
||||||
const { _header: reqHeaders, res } = request || fallbackRequest
|
|
||||||
const { rawHeaders } = res
|
|
||||||
|
|
||||||
// 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 parsedResBody = this.parseInterceptedBody(resData)
|
|
||||||
|
|
||||||
// HTTP response summary.
|
|
||||||
process.logger?.info(`HTTP Request (first 50 lines):
|
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 Code: ${this.prettifyString(status)}
|
||||||
|
|
||||||
@@ -509,7 +432,67 @@ HTTP Response (first 50 lines):
|
|||||||
${resHeaders}${parsedResBody ? `\n\n${parsedResBody}` : ''}
|
${resHeaders}${parsedResBody ? `\n\n${parsedResBody}` : ''}
|
||||||
`)
|
`)
|
||||||
|
|
||||||
return axiosResponse
|
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.
|
||||||
|
// _header is not present in responses with status 1**
|
||||||
|
// rawHeaders are not present in responses with status 1**
|
||||||
|
let fallbackRequest = {
|
||||||
|
_header: noValueMessage,
|
||||||
|
res: { rawHeaders: [noValueMessage] }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request) {
|
||||||
|
fallbackRequest = {
|
||||||
|
_header:
|
||||||
|
request._header ?? request._currentRequest?._header ?? noValueMessage,
|
||||||
|
res: { rawHeaders: request.res?.rawHeaders ?? [noValueMessage] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
const { status, request: req, data: resData } = fallbackResponse
|
||||||
|
const { _header: reqHeaders, res } = req
|
||||||
|
|
||||||
|
const resHeaders = this.formatHeaders(res.rawHeaders)
|
||||||
|
const parsedResBody = this.parseInterceptedBody(resData)
|
||||||
|
|
||||||
|
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 error
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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**.
|
* @param errorCallBack - function that should be triggered on every HTTP response with the status different from 2**.
|
||||||
*/
|
*/
|
||||||
public enableVerboseMode = (
|
public enableVerboseMode = (
|
||||||
successCallBack = this.defaultInterceptionCallBack,
|
successCallBack = this.handleAxiosResponse,
|
||||||
errorCallBack = this.defaultInterceptionCallBack
|
errorCallBack = this.handleAxiosError
|
||||||
) => {
|
) => {
|
||||||
this.httpInterceptor = this.httpClient.interceptors.response.use(
|
this.httpInterceptor = this.httpClient.interceptors.response.use(
|
||||||
successCallBack,
|
successCallBack,
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ describe('RequestClient', () => {
|
|||||||
expect(rejectionErrorMessage).toEqual(expectedError.message)
|
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
|
const reqHeaders = `POST https://sas.server.com/compute/sessions/session_id/jobs HTTP/1.1
|
||||||
Accept: application/json
|
Accept: application/json
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
@@ -181,7 +181,7 @@ Connection: close
|
|||||||
} as AxiosError
|
} as AxiosError
|
||||||
|
|
||||||
const requestClient = new RequestClient('')
|
const requestClient = new RequestClient('')
|
||||||
requestClient['defaultInterceptionCallBack'](mockedAxiosError)
|
requestClient['handleAxiosError'](mockedAxiosError)
|
||||||
|
|
||||||
const noValueMessage = 'Not provided'
|
const noValueMessage = 'Not provided'
|
||||||
const expectedLog = `HTTP Request (first 50 lines):
|
const expectedLog = `HTTP Request (first 50 lines):
|
||||||
@@ -214,7 +214,7 @@ ${noValueMessage}
|
|||||||
}
|
}
|
||||||
|
|
||||||
const requestClient = new RequestClient('')
|
const requestClient = new RequestClient('')
|
||||||
requestClient['defaultInterceptionCallBack'](mockedResponse)
|
requestClient['handleAxiosResponse'](mockedResponse)
|
||||||
|
|
||||||
const expectedLog = `HTTP Request (first 50 lines):
|
const expectedLog = `HTTP Request (first 50 lines):
|
||||||
${reqHeaders}${requestClient['parseInterceptedBody'](reqData)}
|
${reqHeaders}${requestClient['parseInterceptedBody'](reqData)}
|
||||||
@@ -235,29 +235,29 @@ ${resHeaders[0]}: ${resHeaders[1]}${
|
|||||||
it('should log parsed response with status 3**', () => {
|
it('should log parsed response with status 3**', () => {
|
||||||
const status = getRandomStatus([300, 301, 302, 303, 304, 307, 308])
|
const status = getRandomStatus([300, 301, 302, 303, 304, 307, 308])
|
||||||
|
|
||||||
const mockedResponse: AxiosResponse = {
|
const mockedAxiosError = {
|
||||||
data: resData,
|
config: {
|
||||||
status,
|
data: reqData
|
||||||
statusText: '',
|
},
|
||||||
headers: {},
|
request: {
|
||||||
config: { data: reqData },
|
_currentRequest: {
|
||||||
request: { _header: reqHeaders, res: { rawHeaders: resHeaders } }
|
_header: reqHeaders
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} as AxiosError
|
||||||
|
|
||||||
const requestClient = new RequestClient('')
|
const requestClient = new RequestClient('')
|
||||||
requestClient['defaultInterceptionCallBack'](mockedResponse)
|
requestClient['handleAxiosError'](mockedAxiosError)
|
||||||
|
|
||||||
|
const noValueMessage = 'Not provided'
|
||||||
const expectedLog = `HTTP Request (first 50 lines):
|
const expectedLog = `HTTP Request (first 50 lines):
|
||||||
${reqHeaders}${requestClient['parseInterceptedBody'](reqData)}
|
${reqHeaders}${requestClient['parseInterceptedBody'](reqData)}
|
||||||
|
|
||||||
HTTP Response Code: ${requestClient['prettifyString'](status)}
|
HTTP Response Code: ${requestClient['prettifyString'](noValueMessage)}
|
||||||
|
|
||||||
HTTP Response (first 50 lines):
|
HTTP Response (first 50 lines):
|
||||||
${resHeaders[0]}: ${resHeaders[1]}${
|
${noValueMessage}
|
||||||
requestClient['parseInterceptedBody'](resData)
|
\n${requestClient['parseInterceptedBody'](noValueMessage)}
|
||||||
? `\n\n${requestClient['parseInterceptedBody'](resData)}`
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
`
|
`
|
||||||
|
|
||||||
expect((process as any).logger.info).toHaveBeenCalledWith(expectedLog)
|
expect((process as any).logger.info).toHaveBeenCalledWith(expectedLog)
|
||||||
@@ -294,7 +294,7 @@ ${resHeaders[0]}: ${resHeaders[1]}${
|
|||||||
} as AxiosError
|
} as AxiosError
|
||||||
|
|
||||||
const requestClient = new RequestClient('')
|
const requestClient = new RequestClient('')
|
||||||
requestClient['defaultInterceptionCallBack'](mockedAxiosError)
|
requestClient['handleAxiosError'](mockedAxiosError)
|
||||||
|
|
||||||
const expectedLog = `HTTP Request (first 50 lines):
|
const expectedLog = `HTTP Request (first 50 lines):
|
||||||
${reqHeaders}${requestClient['parseInterceptedBody'](reqData)}
|
${reqHeaders}${requestClient['parseInterceptedBody'](reqData)}
|
||||||
@@ -344,7 +344,7 @@ ${resHeaders[0]}: ${resHeaders[1]}${
|
|||||||
} as AxiosError
|
} as AxiosError
|
||||||
|
|
||||||
const requestClient = new RequestClient('')
|
const requestClient = new RequestClient('')
|
||||||
requestClient['defaultInterceptionCallBack'](mockedAxiosError)
|
requestClient['handleAxiosError'](mockedAxiosError)
|
||||||
|
|
||||||
const expectedLog = `HTTP Request (first 50 lines):
|
const expectedLog = `HTTP Request (first 50 lines):
|
||||||
${reqHeaders}${requestClient['parseInterceptedBody'](reqData)}
|
${reqHeaders}${requestClient['parseInterceptedBody'](reqData)}
|
||||||
@@ -376,8 +376,8 @@ ${resHeaders[0]}: ${resHeaders[1]}${
|
|||||||
requestClient.enableVerboseMode()
|
requestClient.enableVerboseMode()
|
||||||
|
|
||||||
expect(interceptorSpy).toHaveBeenCalledWith(
|
expect(interceptorSpy).toHaveBeenCalledWith(
|
||||||
requestClient['defaultInterceptionCallBack'],
|
requestClient['handleAxiosResponse'],
|
||||||
requestClient['defaultInterceptionCallBack']
|
requestClient['handleAxiosError']
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -388,12 +388,12 @@ ${resHeaders[0]}: ${resHeaders[1]}${
|
|||||||
'use'
|
'use'
|
||||||
)
|
)
|
||||||
|
|
||||||
const successCallback = (response: AxiosResponse | AxiosError) => {
|
const successCallback = (response: AxiosResponse) => {
|
||||||
console.log('success')
|
console.log('success')
|
||||||
|
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
const failureCallback = (response: AxiosResponse | AxiosError) => {
|
const failureCallback = (response: AxiosError) => {
|
||||||
console.log('failure')
|
console.log('failure')
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|||||||
Reference in New Issue
Block a user