mirror of
https://github.com/sasjs/adapter.git
synced 2026-01-03 02:30:06 +00:00
chore(*): clean up, handle debug responses
This commit is contained in:
@@ -142,7 +142,10 @@ describe('AuthManager', () => {
|
||||
})
|
||||
)
|
||||
mockedAxios.post.mockImplementation(() =>
|
||||
Promise.resolve({ data: mockLoginAuthoriseRequiredResponse })
|
||||
Promise.resolve({
|
||||
data: mockLoginAuthoriseRequiredResponse,
|
||||
config: { url: 'https://test.com/SASLogon/login' }
|
||||
})
|
||||
)
|
||||
|
||||
await authManager.logIn(userName, password)
|
||||
|
||||
@@ -2,7 +2,6 @@ import { ServerType } from '@sasjs/utils/types'
|
||||
import { ErrorResponse } from '..'
|
||||
import { SASViyaApiClient } from '../SASViyaApiClient'
|
||||
import { ComputeJobExecutionError, LoginRequiredError } from '../types'
|
||||
import { parseWeboutResponse } from '../utils'
|
||||
import { BaseJobExecutor } from './JobExecutor'
|
||||
|
||||
export class ComputeJobExecutor extends BaseJobExecutor {
|
||||
@@ -35,15 +34,7 @@ export class ComputeJobExecutor extends BaseJobExecutor {
|
||||
this.appendRequest(response, sasJob, config.debug)
|
||||
let responseJson
|
||||
|
||||
try {
|
||||
if (typeof response!.result === 'string') {
|
||||
responseJson = JSON.parse(response!.result)
|
||||
} else {
|
||||
responseJson = response!.result
|
||||
}
|
||||
} catch {
|
||||
responseJson = JSON.parse(parseWeboutResponse(response!.result))
|
||||
}
|
||||
return response.result
|
||||
|
||||
return responseJson
|
||||
})
|
||||
|
||||
@@ -2,7 +2,6 @@ import { ServerType } from '@sasjs/utils/types'
|
||||
import { ErrorResponse } from '..'
|
||||
import { SASViyaApiClient } from '../SASViyaApiClient'
|
||||
import { JobExecutionError, LoginRequiredError } from '../types'
|
||||
import { parseWeboutResponse } from '../utils'
|
||||
import { BaseJobExecutor } from './JobExecutor'
|
||||
|
||||
export class JesJobExecutor extends BaseJobExecutor {
|
||||
@@ -23,19 +22,7 @@ export class JesJobExecutor extends BaseJobExecutor {
|
||||
.then((response) => {
|
||||
this.appendRequest(response, sasJob, config.debug)
|
||||
|
||||
let responseJson
|
||||
|
||||
try {
|
||||
if (typeof response!.result === 'string') {
|
||||
responseJson = JSON.parse(response!.result)
|
||||
} else {
|
||||
responseJson = response!.result
|
||||
}
|
||||
} catch {
|
||||
responseJson = JSON.parse(parseWeboutResponse(response!.result))
|
||||
}
|
||||
|
||||
return responseJson
|
||||
return response.result
|
||||
})
|
||||
.catch(async (e: Error) => {
|
||||
if (e instanceof JobExecutionError) {
|
||||
|
||||
@@ -90,8 +90,13 @@ export abstract class BaseJobExecutor implements JobExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
const stringifiedResult =
|
||||
typeof response?.result === 'string'
|
||||
? response?.result
|
||||
: JSON.stringify(response?.result, null, 2)
|
||||
|
||||
this.requests.push({
|
||||
logFile: response?.log || response?.result || response,
|
||||
logFile: response?.log || stringifiedResult || response,
|
||||
serviceLink: program,
|
||||
timestamp: new Date(),
|
||||
sourceCode,
|
||||
|
||||
@@ -82,6 +82,13 @@ export class WebJobExecutor extends BaseJobExecutor {
|
||||
|
||||
return this.requestClient!.post(apiUrl, formData, undefined)
|
||||
.then(async (res) => {
|
||||
if (this.serverType === ServerType.SasViya && config.debug) {
|
||||
const jsonResponse = await this.parseSasViyaDebugResponse(
|
||||
res.result as string
|
||||
)
|
||||
this.appendRequest(res, sasJob, config.debug)
|
||||
return jsonResponse
|
||||
}
|
||||
this.appendRequest(res, sasJob, config.debug)
|
||||
return res.result
|
||||
})
|
||||
@@ -99,6 +106,20 @@ export class WebJobExecutor extends BaseJobExecutor {
|
||||
})
|
||||
}
|
||||
|
||||
private parseSasViyaDebugResponse = async (response: string) => {
|
||||
const iframeStart = response.split(
|
||||
'<iframe style="width: 99%; height: 500px" src="'
|
||||
)[1]
|
||||
const jsonUrl = iframeStart ? iframeStart.split('"></iframe>')[0] : null
|
||||
if (!jsonUrl) {
|
||||
throw new Error('Unable to find webout file URL.')
|
||||
}
|
||||
|
||||
return this.requestClient
|
||||
.get(this.serverUrl + jsonUrl, undefined)
|
||||
.then((res) => res.result)
|
||||
}
|
||||
|
||||
private async getJobUri(sasJob: string) {
|
||||
if (!this.sasViyaApiClient) return ''
|
||||
let uri = ''
|
||||
|
||||
@@ -4,6 +4,7 @@ import { isAuthorizeFormRequired, isLogInRequired } from '../auth'
|
||||
import { LoginRequiredError } from '../types'
|
||||
import { AuthorizeError } from '../types/AuthorizeError'
|
||||
import { NotFoundError } from '../types/NotFoundError'
|
||||
import { parseWeboutResponse } from '../utils/parseWeboutResponse'
|
||||
|
||||
export interface HttpClient {
|
||||
get<T>(
|
||||
@@ -42,7 +43,7 @@ export class RequestClient implements HttpClient {
|
||||
private fileUploadCsrfToken: CsrfToken | undefined
|
||||
private httpClient: AxiosInstance
|
||||
|
||||
constructor(baseUrl: string) {
|
||||
constructor(private baseUrl: string) {
|
||||
this.httpClient = axios.create({ baseURL: baseUrl })
|
||||
}
|
||||
|
||||
@@ -79,35 +80,12 @@ export class RequestClient implements HttpClient {
|
||||
.get<T>(url, requestConfig)
|
||||
.then((response) => {
|
||||
throwIfError(response)
|
||||
const etag = response?.headers ? response.headers['etag'] : ''
|
||||
|
||||
return {
|
||||
result: response.data as T,
|
||||
etag
|
||||
}
|
||||
return this.parseResponse<T>(response)
|
||||
})
|
||||
.catch(async (e) => {
|
||||
const response = e.response as AxiosResponse
|
||||
if (e instanceof AuthorizeError) {
|
||||
const res = await this.get(e.confirmUrl, undefined, 'text/plain')
|
||||
if (isAuthorizeFormRequired(res.result as string)) {
|
||||
await this.authorize(res.result as string)
|
||||
}
|
||||
return this.get<T>(url, accessToken, contentType, overrideHeaders)
|
||||
}
|
||||
if (e instanceof LoginRequiredError) {
|
||||
this.clearCsrfTokens()
|
||||
}
|
||||
if (response?.status === 403 || response?.status === 449) {
|
||||
this.parseAndSetCsrfToken(response)
|
||||
if (this.csrfToken.headerName && this.csrfToken.value) {
|
||||
return this.get<T>(url, accessToken, contentType, overrideHeaders)
|
||||
}
|
||||
throw e
|
||||
} else if (response?.status === 404) {
|
||||
throw new NotFoundError(url)
|
||||
}
|
||||
throw e
|
||||
return await this.handleError(e, () =>
|
||||
this.get<T>(url, accessToken, contentType, overrideHeaders)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -127,39 +105,12 @@ export class RequestClient implements HttpClient {
|
||||
.post<T>(url, data, { headers, withCredentials: true })
|
||||
.then((response) => {
|
||||
throwIfError(response)
|
||||
const etag = response?.headers ? response.headers['etag'] : ''
|
||||
return {
|
||||
result: response.data as T,
|
||||
etag
|
||||
}
|
||||
return this.parseResponse<T>(response)
|
||||
})
|
||||
.catch(async (e) => {
|
||||
const response = e.response as AxiosResponse
|
||||
if (e instanceof AuthorizeError) {
|
||||
const res = await this.get(e.confirmUrl, undefined, 'text/plain')
|
||||
if (isAuthorizeFormRequired(res.result as string)) {
|
||||
await this.authorize(res.result as string)
|
||||
}
|
||||
return this.post<T>(
|
||||
url,
|
||||
data,
|
||||
accessToken,
|
||||
contentType,
|
||||
overrideHeaders
|
||||
)
|
||||
}
|
||||
if (e instanceof LoginRequiredError) {
|
||||
this.clearCsrfTokens()
|
||||
}
|
||||
if (response?.status === 403 || response?.status === 449) {
|
||||
this.parseAndSetCsrfToken(response)
|
||||
|
||||
if (this.csrfToken.headerName && this.csrfToken.value) {
|
||||
return this.post<T>(url, data, accessToken)
|
||||
}
|
||||
throw e
|
||||
}
|
||||
throw e
|
||||
return await this.handleError(e, () =>
|
||||
this.post<T>(url, data, accessToken, contentType, overrideHeaders)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -174,36 +125,17 @@ export class RequestClient implements HttpClient {
|
||||
...overrideHeaders
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await this.httpClient.put<T>(url, data, {
|
||||
headers,
|
||||
withCredentials: true
|
||||
return this.httpClient
|
||||
.put<T>(url, data, { headers, withCredentials: true })
|
||||
.then((response) => {
|
||||
throwIfError(response)
|
||||
return this.parseResponse<T>(response)
|
||||
})
|
||||
.catch(async (e) => {
|
||||
return await this.handleError(e, () =>
|
||||
this.put<T>(url, data, accessToken, overrideHeaders)
|
||||
)
|
||||
})
|
||||
const etag = response?.headers ? response.headers['etag'] : ''
|
||||
return {
|
||||
result: response.data as T,
|
||||
etag
|
||||
}
|
||||
} catch (e) {
|
||||
const response = e.response as AxiosResponse
|
||||
if (e instanceof LoginRequiredError) {
|
||||
this.clearCsrfTokens()
|
||||
}
|
||||
|
||||
if (response?.status === 403 || response?.status === 449) {
|
||||
this.parseAndSetCsrfToken(response)
|
||||
|
||||
if (this.csrfToken.headerName && this.csrfToken.value) {
|
||||
return this.put<T>(url, data, accessToken)
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
||||
if (response?.status === 401) {
|
||||
throw new LoginRequiredError()
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
public async delete<T>(
|
||||
@@ -212,33 +144,15 @@ export class RequestClient implements HttpClient {
|
||||
): Promise<{ result: T; etag: string }> {
|
||||
const headers = this.getHeaders(accessToken, 'application/json')
|
||||
|
||||
const requestConfig: AxiosRequestConfig = {
|
||||
headers
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await this.httpClient.delete<T>(url, requestConfig)
|
||||
const etag = response?.headers ? response.headers['etag'] : ''
|
||||
return {
|
||||
result: response.data as T,
|
||||
etag
|
||||
}
|
||||
} catch (e) {
|
||||
const response = e.response as AxiosResponse
|
||||
if (e instanceof LoginRequiredError) {
|
||||
this.clearCsrfTokens()
|
||||
}
|
||||
|
||||
if (response?.status === 403 || response?.status === 449) {
|
||||
this.parseAndSetCsrfToken(response)
|
||||
|
||||
if (this.csrfToken.headerName && this.csrfToken.value) {
|
||||
return this.delete<T>(url, accessToken)
|
||||
}
|
||||
throw e
|
||||
}
|
||||
throw e
|
||||
}
|
||||
return this.httpClient
|
||||
.delete<T>(url, { headers, withCredentials: true })
|
||||
.then((response) => {
|
||||
throwIfError(response)
|
||||
return this.parseResponse<T>(response)
|
||||
})
|
||||
.catch(async (e) => {
|
||||
return await this.handleError(e, () => this.delete<T>(url, accessToken))
|
||||
})
|
||||
}
|
||||
|
||||
public async patch<T>(
|
||||
@@ -248,31 +162,17 @@ export class RequestClient implements HttpClient {
|
||||
): Promise<{ result: T; etag: string }> {
|
||||
const headers = this.getHeaders(accessToken, 'application/json')
|
||||
|
||||
try {
|
||||
const response = await this.httpClient.patch<T>(url, data, {
|
||||
headers,
|
||||
withCredentials: true
|
||||
return this.httpClient
|
||||
.patch<T>(url, data, { headers, withCredentials: true })
|
||||
.then((response) => {
|
||||
throwIfError(response)
|
||||
return this.parseResponse<T>(response)
|
||||
})
|
||||
.catch(async (e) => {
|
||||
return await this.handleError(e, () =>
|
||||
this.patch<T>(url, data, accessToken)
|
||||
)
|
||||
})
|
||||
return {
|
||||
result: response.data as T,
|
||||
etag: response.headers['etag'] as string
|
||||
}
|
||||
} catch (e) {
|
||||
const response = e.response as AxiosResponse
|
||||
if (e instanceof LoginRequiredError) {
|
||||
this.clearCsrfTokens()
|
||||
}
|
||||
|
||||
if (response?.status === 403 || response?.status === 449) {
|
||||
this.parseAndSetCsrfToken(response)
|
||||
|
||||
if (this.csrfToken.headerName && this.csrfToken.value) {
|
||||
return this.patch<T>(url, accessToken)
|
||||
}
|
||||
throw e
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
public async uploadFile(
|
||||
@@ -317,9 +217,7 @@ export class RequestClient implements HttpClient {
|
||||
bodyElement.innerHTML = responseBody
|
||||
|
||||
const form = bodyElement.querySelector('#application_authorization')
|
||||
authUrl = form
|
||||
? this.httpClient.defaults.baseURL! + form.getAttribute('action')
|
||||
: null
|
||||
authUrl = form ? this.baseUrl + form.getAttribute('action') : null
|
||||
|
||||
const inputs: any = form?.querySelectorAll('input')
|
||||
|
||||
@@ -417,9 +315,60 @@ export class RequestClient implements HttpClient {
|
||||
return csrfToken
|
||||
}
|
||||
}
|
||||
|
||||
private handleError = async (e: any, callback: any) => {
|
||||
const response = e.response as AxiosResponse
|
||||
if (e instanceof AuthorizeError) {
|
||||
const res = await this.get(e.confirmUrl, undefined, 'text/plain')
|
||||
if (isAuthorizeFormRequired(res.result as string)) {
|
||||
await this.authorize(res.result as string)
|
||||
}
|
||||
return await callback()
|
||||
}
|
||||
if (e instanceof LoginRequiredError) {
|
||||
this.clearCsrfTokens()
|
||||
}
|
||||
if (response?.status === 403 || response?.status === 449) {
|
||||
this.parseAndSetCsrfToken(response)
|
||||
if (this.csrfToken.headerName && this.csrfToken.value) {
|
||||
return await callback()
|
||||
}
|
||||
throw e
|
||||
} else if (response?.status === 404) {
|
||||
throw new NotFoundError(response.config.url!)
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
||||
private async parseResponse<T>(response: AxiosResponse<any>) {
|
||||
const etag = response?.headers ? response.headers['etag'] : ''
|
||||
let parsedResponse
|
||||
|
||||
try {
|
||||
if (typeof response.data === 'string') {
|
||||
parsedResponse = JSON.parse(response.data)
|
||||
} else {
|
||||
parsedResponse = response.data
|
||||
}
|
||||
} catch {
|
||||
try {
|
||||
parsedResponse = JSON.parse(parseWeboutResponse(response.data))
|
||||
} catch {
|
||||
parsedResponse = response.data
|
||||
}
|
||||
}
|
||||
return {
|
||||
result: parsedResponse as T,
|
||||
etag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const throwIfError = (response: AxiosResponse) => {
|
||||
if (response.status === 401) {
|
||||
throw new LoginRequiredError()
|
||||
}
|
||||
|
||||
if (response.data?.entityID?.includes('login')) {
|
||||
throw new LoginRequiredError()
|
||||
}
|
||||
@@ -427,7 +376,7 @@ const throwIfError = (response: AxiosResponse) => {
|
||||
if (
|
||||
typeof response.data === 'string' &&
|
||||
isLogInRequired(response.data) &&
|
||||
!response.config.url?.includes('/SASLogon/login')
|
||||
!response.config?.url?.includes('/SASLogon/login')
|
||||
) {
|
||||
throw new LoginRequiredError()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user