1
0
mirror of https://github.com/sasjs/adapter.git synced 2026-01-16 00:20: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(() => mockedAxios.post.mockImplementation(() =>
Promise.resolve({ data: mockLoginAuthoriseRequiredResponse }) Promise.resolve({
data: mockLoginAuthoriseRequiredResponse,
config: { url: 'https://test.com/SASLogon/login' }
})
) )
await authManager.logIn(userName, password) await authManager.logIn(userName, password)

View File

@@ -2,7 +2,6 @@ import { ServerType } from '@sasjs/utils/types'
import { ErrorResponse } from '..' import { ErrorResponse } from '..'
import { SASViyaApiClient } from '../SASViyaApiClient' import { SASViyaApiClient } from '../SASViyaApiClient'
import { ComputeJobExecutionError, LoginRequiredError } from '../types' import { ComputeJobExecutionError, LoginRequiredError } from '../types'
import { parseWeboutResponse } from '../utils'
import { BaseJobExecutor } from './JobExecutor' import { BaseJobExecutor } from './JobExecutor'
export class ComputeJobExecutor extends BaseJobExecutor { export class ComputeJobExecutor extends BaseJobExecutor {
@@ -35,15 +34,7 @@ export class ComputeJobExecutor extends BaseJobExecutor {
this.appendRequest(response, sasJob, config.debug) this.appendRequest(response, sasJob, config.debug)
let responseJson let responseJson
try { return response.result
if (typeof response!.result === 'string') {
responseJson = JSON.parse(response!.result)
} else {
responseJson = response!.result
}
} catch {
responseJson = JSON.parse(parseWeboutResponse(response!.result))
}
return responseJson return responseJson
}) })

View File

@@ -2,7 +2,6 @@ import { ServerType } from '@sasjs/utils/types'
import { ErrorResponse } from '..' import { ErrorResponse } from '..'
import { SASViyaApiClient } from '../SASViyaApiClient' import { SASViyaApiClient } from '../SASViyaApiClient'
import { JobExecutionError, LoginRequiredError } from '../types' import { JobExecutionError, LoginRequiredError } from '../types'
import { parseWeboutResponse } from '../utils'
import { BaseJobExecutor } from './JobExecutor' import { BaseJobExecutor } from './JobExecutor'
export class JesJobExecutor extends BaseJobExecutor { export class JesJobExecutor extends BaseJobExecutor {
@@ -23,19 +22,7 @@ export class JesJobExecutor extends BaseJobExecutor {
.then((response) => { .then((response) => {
this.appendRequest(response, sasJob, config.debug) this.appendRequest(response, sasJob, config.debug)
let responseJson return response.result
try {
if (typeof response!.result === 'string') {
responseJson = JSON.parse(response!.result)
} else {
responseJson = response!.result
}
} catch {
responseJson = JSON.parse(parseWeboutResponse(response!.result))
}
return responseJson
}) })
.catch(async (e: Error) => { .catch(async (e: Error) => {
if (e instanceof JobExecutionError) { 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({ this.requests.push({
logFile: response?.log || response?.result || response, logFile: response?.log || stringifiedResult || response,
serviceLink: program, serviceLink: program,
timestamp: new Date(), timestamp: new Date(),
sourceCode, sourceCode,

View File

@@ -82,6 +82,13 @@ export class WebJobExecutor extends BaseJobExecutor {
return this.requestClient!.post(apiUrl, formData, undefined) return this.requestClient!.post(apiUrl, formData, undefined)
.then(async (res) => { .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) this.appendRequest(res, sasJob, config.debug)
return res.result 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) { private async getJobUri(sasJob: string) {
if (!this.sasViyaApiClient) return '' if (!this.sasViyaApiClient) return ''
let uri = '' let uri = ''

View File

@@ -4,6 +4,7 @@ import { isAuthorizeFormRequired, isLogInRequired } from '../auth'
import { LoginRequiredError } from '../types' import { LoginRequiredError } from '../types'
import { AuthorizeError } from '../types/AuthorizeError' import { AuthorizeError } from '../types/AuthorizeError'
import { NotFoundError } from '../types/NotFoundError' import { NotFoundError } from '../types/NotFoundError'
import { parseWeboutResponse } from '../utils/parseWeboutResponse'
export interface HttpClient { export interface HttpClient {
get<T>( get<T>(
@@ -42,7 +43,7 @@ export class RequestClient implements HttpClient {
private fileUploadCsrfToken: CsrfToken | undefined private fileUploadCsrfToken: CsrfToken | undefined
private httpClient: AxiosInstance private httpClient: AxiosInstance
constructor(baseUrl: string) { constructor(private baseUrl: string) {
this.httpClient = axios.create({ baseURL: baseUrl }) this.httpClient = axios.create({ baseURL: baseUrl })
} }
@@ -79,35 +80,12 @@ export class RequestClient implements HttpClient {
.get<T>(url, requestConfig) .get<T>(url, requestConfig)
.then((response) => { .then((response) => {
throwIfError(response) throwIfError(response)
const etag = response?.headers ? response.headers['etag'] : '' return this.parseResponse<T>(response)
return {
result: response.data as T,
etag
}
}) })
.catch(async (e) => { .catch(async (e) => {
const response = e.response as AxiosResponse return await this.handleError(e, () =>
if (e instanceof AuthorizeError) { this.get<T>(url, accessToken, contentType, overrideHeaders)
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
}) })
} }
@@ -127,39 +105,12 @@ export class RequestClient implements HttpClient {
.post<T>(url, data, { headers, withCredentials: true }) .post<T>(url, data, { headers, withCredentials: true })
.then((response) => { .then((response) => {
throwIfError(response) throwIfError(response)
const etag = response?.headers ? response.headers['etag'] : '' return this.parseResponse<T>(response)
return {
result: response.data as T,
etag
}
}) })
.catch(async (e) => { .catch(async (e) => {
const response = e.response as AxiosResponse return await this.handleError(e, () =>
if (e instanceof AuthorizeError) { this.post<T>(url, data, accessToken, contentType, overrideHeaders)
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
}) })
} }
@@ -174,36 +125,17 @@ export class RequestClient implements HttpClient {
...overrideHeaders ...overrideHeaders
} }
try { return this.httpClient
const response = await this.httpClient.put<T>(url, data, { .put<T>(url, data, { headers, withCredentials: true })
headers, .then((response) => {
withCredentials: true 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>( public async delete<T>(
@@ -212,33 +144,15 @@ export class RequestClient implements HttpClient {
): Promise<{ result: T; etag: string }> { ): Promise<{ result: T; etag: string }> {
const headers = this.getHeaders(accessToken, 'application/json') const headers = this.getHeaders(accessToken, 'application/json')
const requestConfig: AxiosRequestConfig = { return this.httpClient
headers .delete<T>(url, { headers, withCredentials: true })
} .then((response) => {
throwIfError(response)
try { return this.parseResponse<T>(response)
const response = await this.httpClient.delete<T>(url, requestConfig) })
const etag = response?.headers ? response.headers['etag'] : '' .catch(async (e) => {
return { return await this.handleError(e, () => this.delete<T>(url, accessToken))
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
}
} }
public async patch<T>( public async patch<T>(
@@ -248,31 +162,17 @@ export class RequestClient implements HttpClient {
): Promise<{ result: T; etag: string }> { ): Promise<{ result: T; etag: string }> {
const headers = this.getHeaders(accessToken, 'application/json') const headers = this.getHeaders(accessToken, 'application/json')
try { return this.httpClient
const response = await this.httpClient.patch<T>(url, data, { .patch<T>(url, data, { headers, withCredentials: true })
headers, .then((response) => {
withCredentials: true 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( public async uploadFile(
@@ -317,9 +217,7 @@ export class RequestClient implements HttpClient {
bodyElement.innerHTML = responseBody bodyElement.innerHTML = responseBody
const form = bodyElement.querySelector('#application_authorization') const form = bodyElement.querySelector('#application_authorization')
authUrl = form authUrl = form ? this.baseUrl + form.getAttribute('action') : null
? this.httpClient.defaults.baseURL! + form.getAttribute('action')
: null
const inputs: any = form?.querySelectorAll('input') const inputs: any = form?.querySelectorAll('input')
@@ -417,9 +315,60 @@ export class RequestClient implements HttpClient {
return csrfToken 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) => { const throwIfError = (response: AxiosResponse) => {
if (response.status === 401) {
throw new LoginRequiredError()
}
if (response.data?.entityID?.includes('login')) { if (response.data?.entityID?.includes('login')) {
throw new LoginRequiredError() throw new LoginRequiredError()
} }
@@ -427,7 +376,7 @@ const throwIfError = (response: AxiosResponse) => {
if ( if (
typeof response.data === 'string' && typeof response.data === 'string' &&
isLogInRequired(response.data) && isLogInRequired(response.data) &&
!response.config.url?.includes('/SASLogon/login') !response.config?.url?.includes('/SASLogon/login')
) { ) {
throw new LoginRequiredError() throw new LoginRequiredError()
} }