1
0
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:
Krishna Acondy
2021-02-04 09:42:39 +00:00
parent 8edb00f869
commit 613cc6b9ef
6 changed files with 125 additions and 169 deletions

View File

@@ -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)

View File

@@ -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
})

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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 = ''

View File

@@ -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()
}