mirror of
https://github.com/sasjs/adapter.git
synced 2026-01-19 10:00:06 +00:00
chore(*): refactor common functionality into JobExecutor, handle all auth-related scenarios
This commit is contained in:
@@ -18,7 +18,6 @@ import { ContextManager } from './ContextManager'
|
|||||||
import { timestampToYYYYMMDDHHMMSS } from '@sasjs/utils/time'
|
import { timestampToYYYYMMDDHHMMSS } from '@sasjs/utils/time'
|
||||||
import { Logger, LogLevel } from '@sasjs/utils/logger'
|
import { Logger, LogLevel } from '@sasjs/utils/logger'
|
||||||
import { isAuthorizeFormRequired } from './auth/isAuthorizeFormRequired'
|
import { isAuthorizeFormRequired } from './auth/isAuthorizeFormRequired'
|
||||||
import { parseAndSubmitAuthorizeForm } from './auth'
|
|
||||||
import { RequestClient } from './request/RequestClient'
|
import { RequestClient } from './request/RequestClient'
|
||||||
import { NotFoundError } from './types/NotFoundError'
|
import { NotFoundError } from './types/NotFoundError'
|
||||||
|
|
||||||
@@ -632,10 +631,7 @@ export class SASViyaApiClient {
|
|||||||
.then(async (response) => {
|
.then(async (response) => {
|
||||||
let code = ''
|
let code = ''
|
||||||
if (isAuthorizeFormRequired(response)) {
|
if (isAuthorizeFormRequired(response)) {
|
||||||
const formResponse: any = await parseAndSubmitAuthorizeForm(
|
const formResponse: any = await this.requestClient.authorize(response)
|
||||||
response,
|
|
||||||
this.serverUrl
|
|
||||||
)
|
|
||||||
|
|
||||||
const responseBody = formResponse
|
const responseBody = formResponse
|
||||||
.split('<body>')[1]
|
.split('<body>')[1]
|
||||||
|
|||||||
@@ -716,6 +716,7 @@ export default class SASjs {
|
|||||||
this.authManager = new AuthManager(
|
this.authManager = new AuthManager(
|
||||||
this.sasjsConfig.serverUrl,
|
this.sasjsConfig.serverUrl,
|
||||||
this.sasjsConfig.serverType!,
|
this.sasjsConfig.serverType!,
|
||||||
|
this.requestClient,
|
||||||
this.resendWaitingRequests
|
this.resendWaitingRequests
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
import { ServerType } from '@sasjs/utils/types'
|
import { ServerType } from '@sasjs/utils/types'
|
||||||
import axios, { AxiosInstance } from 'axios'
|
import { isAuthorizeFormRequired } from '.'
|
||||||
import { isAuthorizeFormRequired, parseAndSubmitAuthorizeForm } from '.'
|
import { RequestClient } from '../request/RequestClient'
|
||||||
import { serialize } from '../utils'
|
import { serialize } from '../utils'
|
||||||
|
|
||||||
export class AuthManager {
|
export class AuthManager {
|
||||||
public userName = ''
|
public userName = ''
|
||||||
private loginUrl: string
|
private loginUrl: string
|
||||||
private logoutUrl: string
|
private logoutUrl: string
|
||||||
private httpClient: AxiosInstance
|
|
||||||
constructor(
|
constructor(
|
||||||
private serverUrl: string,
|
private serverUrl: string,
|
||||||
private serverType: ServerType,
|
private serverType: ServerType,
|
||||||
|
private requestClient: RequestClient,
|
||||||
private loginCallback: () => Promise<void>
|
private loginCallback: () => Promise<void>
|
||||||
) {
|
) {
|
||||||
this.httpClient = axios.create({ baseURL: this.serverUrl })
|
|
||||||
this.loginUrl = `/SASLogon/login`
|
this.loginUrl = `/SASLogon/login`
|
||||||
this.logoutUrl =
|
this.logoutUrl =
|
||||||
this.serverType === ServerType.Sas9
|
this.serverType === ServerType.Sas9
|
||||||
@@ -50,21 +49,21 @@ export class AuthManager {
|
|||||||
}
|
}
|
||||||
const loginParamsStr = serialize(loginParams)
|
const loginParamsStr = serialize(loginParams)
|
||||||
|
|
||||||
const loginResponse = await this.httpClient
|
const { result: loginResponse } = await this.requestClient.post<string>(
|
||||||
.post<string>(this.loginUrl, loginParamsStr, {
|
this.loginUrl,
|
||||||
withCredentials: true,
|
loginParamsStr,
|
||||||
responseType: 'text',
|
undefined,
|
||||||
headers: {
|
'text/plain',
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
{
|
||||||
Accept: '*/*'
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
}
|
Accept: '*/*'
|
||||||
})
|
}
|
||||||
.then((response) => response.data)
|
)
|
||||||
|
|
||||||
let loggedIn
|
let loggedIn
|
||||||
|
|
||||||
if (isAuthorizeFormRequired(loginResponse)) {
|
if (isAuthorizeFormRequired(loginResponse)) {
|
||||||
await parseAndSubmitAuthorizeForm(loginResponse, this.serverUrl)
|
await this.requestClient.authorize(loginResponse)
|
||||||
} else {
|
} else {
|
||||||
loggedIn = isLogInSuccess(loginResponse)
|
loggedIn = isLogInSuccess(loginResponse)
|
||||||
}
|
}
|
||||||
@@ -89,16 +88,12 @@ export class AuthManager {
|
|||||||
* @returns - a promise which resolves with an object containing two values - a boolean `isLoggedIn`, and a string `userName`.
|
* @returns - a promise which resolves with an object containing two values - a boolean `isLoggedIn`, and a string `userName`.
|
||||||
*/
|
*/
|
||||||
public async checkSession() {
|
public async checkSession() {
|
||||||
const loginResponse = await this.httpClient.get(
|
const { result: loginResponse } = await this.requestClient.get<string>(
|
||||||
this.loginUrl.replace('.do', ''),
|
this.loginUrl.replace('.do', ''),
|
||||||
{
|
undefined,
|
||||||
responseType: 'text',
|
'text/plain'
|
||||||
headers: {
|
|
||||||
Accept: '*/*'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
const responseText = loginResponse?.data
|
const responseText = loginResponse
|
||||||
const isLoggedIn = /<button.+onClick.+logout/gm.test(responseText)
|
const isLoggedIn = /<button.+onClick.+logout/gm.test(responseText)
|
||||||
let loginForm: any = null
|
let loginForm: any = null
|
||||||
|
|
||||||
@@ -158,7 +153,8 @@ export class AuthManager {
|
|||||||
* Logs out of the configured SAS server.
|
* Logs out of the configured SAS server.
|
||||||
*/
|
*/
|
||||||
public logOut() {
|
public logOut() {
|
||||||
return this.httpClient.get(this.logoutUrl).then(() => true)
|
this.requestClient.clearCsrfTokens()
|
||||||
|
return this.requestClient.get(this.logoutUrl, undefined).then(() => true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
import { AuthManager } from './AuthManager'
|
|
||||||
|
|
||||||
export * from './AuthManager'
|
export * from './AuthManager'
|
||||||
export * from './isAuthorizeFormRequired'
|
export * from './isAuthorizeFormRequired'
|
||||||
export * from './isLoginRequired'
|
export * from './isLoginRequired'
|
||||||
export * from './parseAndSubmitAuthorizeForm'
|
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
export const parseAndSubmitAuthorizeForm = async (
|
|
||||||
response: string,
|
|
||||||
serverUrl: string
|
|
||||||
) => {
|
|
||||||
let authUrl: string | null = null
|
|
||||||
const params: any = {}
|
|
||||||
|
|
||||||
const responseBody = response.split('<body>')[1].split('</body>')[0]
|
|
||||||
const bodyElement = document.createElement('div')
|
|
||||||
bodyElement.innerHTML = responseBody
|
|
||||||
|
|
||||||
const form = bodyElement.querySelector('#application_authorization')
|
|
||||||
authUrl = form ? serverUrl + form.getAttribute('action') : null
|
|
||||||
|
|
||||||
const inputs: any = form?.querySelectorAll('input')
|
|
||||||
|
|
||||||
for (const input of inputs) {
|
|
||||||
if (input.name === 'user_oauth_approval') {
|
|
||||||
input.value = 'true'
|
|
||||||
}
|
|
||||||
|
|
||||||
params[input.name] = input.value
|
|
||||||
}
|
|
||||||
|
|
||||||
const formData = new FormData()
|
|
||||||
|
|
||||||
for (const key in params) {
|
|
||||||
if (params.hasOwnProperty(key)) {
|
|
||||||
formData.append(key, params[key])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!authUrl) {
|
|
||||||
throw new Error('Auth Form URL is null or undefined.')
|
|
||||||
}
|
|
||||||
|
|
||||||
return await axios
|
|
||||||
.post(authUrl, formData, {
|
|
||||||
withCredentials: true,
|
|
||||||
responseType: 'text',
|
|
||||||
headers: {
|
|
||||||
Accept: '*/*'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then((res) => res.data)
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { AuthManager } from '../AuthManager'
|
import { AuthManager } from '../AuthManager'
|
||||||
import * as dotenv from 'dotenv'
|
import * as dotenv from 'dotenv'
|
||||||
import * as authModule from '..'
|
|
||||||
import { ServerType } from '@sasjs/utils/types'
|
import { ServerType } from '@sasjs/utils/types'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import {
|
import {
|
||||||
@@ -8,8 +7,8 @@ import {
|
|||||||
mockLoginSuccessResponse
|
mockLoginSuccessResponse
|
||||||
} from './mockResponses'
|
} from './mockResponses'
|
||||||
import { serialize } from '../../utils'
|
import { serialize } from '../../utils'
|
||||||
|
import { RequestClient } from '../../request/RequestClient'
|
||||||
jest.mock('axios')
|
jest.mock('axios')
|
||||||
jest.mock('../parseAndSubmitAuthorizeForm')
|
|
||||||
const mockedAxios = axios as jest.Mocked<typeof axios>
|
const mockedAxios = axios as jest.Mocked<typeof axios>
|
||||||
|
|
||||||
describe('AuthManager', () => {
|
describe('AuthManager', () => {
|
||||||
@@ -18,6 +17,7 @@ describe('AuthManager', () => {
|
|||||||
const serverType = ServerType.SasViya
|
const serverType = ServerType.SasViya
|
||||||
const userName = 'test-username'
|
const userName = 'test-username'
|
||||||
const password = 'test-password'
|
const password = 'test-password'
|
||||||
|
const requestClient = new RequestClient(serverUrl)
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
@@ -25,12 +25,16 @@ describe('AuthManager', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should instantiate and set the correct URLs for a Viya server', () => {
|
it('should instantiate and set the correct URLs for a Viya server', () => {
|
||||||
const authManager = new AuthManager(serverUrl, serverType, authCallback)
|
const authManager = new AuthManager(
|
||||||
|
serverUrl,
|
||||||
|
serverType,
|
||||||
|
requestClient,
|
||||||
|
authCallback
|
||||||
|
)
|
||||||
|
|
||||||
expect(authManager).toBeTruthy()
|
expect(authManager).toBeTruthy()
|
||||||
expect((authManager as any).serverUrl).toEqual(serverUrl)
|
expect((authManager as any).serverUrl).toEqual(serverUrl)
|
||||||
expect((authManager as any).serverType).toEqual(serverType)
|
expect((authManager as any).serverType).toEqual(serverType)
|
||||||
expect((authManager as any).httpClient).toBeTruthy()
|
|
||||||
expect((authManager as any).loginUrl).toEqual(`/SASLogon/login`)
|
expect((authManager as any).loginUrl).toEqual(`/SASLogon/login`)
|
||||||
expect((authManager as any).logoutUrl).toEqual('/SASLogon/logout.do?')
|
expect((authManager as any).logoutUrl).toEqual('/SASLogon/logout.do?')
|
||||||
})
|
})
|
||||||
@@ -39,18 +43,27 @@ describe('AuthManager', () => {
|
|||||||
const authCallback = () => Promise.resolve()
|
const authCallback = () => Promise.resolve()
|
||||||
const serverType = ServerType.Sas9
|
const serverType = ServerType.Sas9
|
||||||
|
|
||||||
const authManager = new AuthManager(serverUrl, serverType, authCallback)
|
const authManager = new AuthManager(
|
||||||
|
serverUrl,
|
||||||
|
serverType,
|
||||||
|
requestClient,
|
||||||
|
authCallback
|
||||||
|
)
|
||||||
|
|
||||||
expect(authManager).toBeTruthy()
|
expect(authManager).toBeTruthy()
|
||||||
expect((authManager as any).serverUrl).toEqual(serverUrl)
|
expect((authManager as any).serverUrl).toEqual(serverUrl)
|
||||||
expect((authManager as any).serverType).toEqual(serverType)
|
expect((authManager as any).serverType).toEqual(serverType)
|
||||||
expect((authManager as any).httpClient).toBeTruthy()
|
|
||||||
expect((authManager as any).loginUrl).toEqual(`/SASLogon/login`)
|
expect((authManager as any).loginUrl).toEqual(`/SASLogon/login`)
|
||||||
expect((authManager as any).logoutUrl).toEqual('/SASLogon/logout?')
|
expect((authManager as any).logoutUrl).toEqual('/SASLogon/logout?')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call the auth callback and return when already logged in', async (done) => {
|
it('should call the auth callback and return when already logged in', async (done) => {
|
||||||
const authManager = new AuthManager(serverUrl, serverType, authCallback)
|
const authManager = new AuthManager(
|
||||||
|
serverUrl,
|
||||||
|
serverType,
|
||||||
|
requestClient,
|
||||||
|
authCallback
|
||||||
|
)
|
||||||
jest.spyOn(authManager, 'checkSession').mockImplementation(() =>
|
jest.spyOn(authManager, 'checkSession').mockImplementation(() =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isLoggedIn: true,
|
isLoggedIn: true,
|
||||||
@@ -68,7 +81,12 @@ describe('AuthManager', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should post a login request to the server if not logged in', async (done) => {
|
it('should post a login request to the server if not logged in', async (done) => {
|
||||||
const authManager = new AuthManager(serverUrl, serverType, authCallback)
|
const authManager = new AuthManager(
|
||||||
|
serverUrl,
|
||||||
|
serverType,
|
||||||
|
requestClient,
|
||||||
|
authCallback
|
||||||
|
)
|
||||||
jest.spyOn(authManager, 'checkSession').mockImplementation(() =>
|
jest.spyOn(authManager, 'checkSession').mockImplementation(() =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isLoggedIn: false,
|
isLoggedIn: false,
|
||||||
@@ -96,7 +114,6 @@ describe('AuthManager', () => {
|
|||||||
loginParams,
|
loginParams,
|
||||||
{
|
{
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
responseType: 'text',
|
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
Accept: '*/*'
|
Accept: '*/*'
|
||||||
@@ -108,9 +125,14 @@ describe('AuthManager', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should parse and submit the authorisation form when necessary', async (done) => {
|
it('should parse and submit the authorisation form when necessary', async (done) => {
|
||||||
const authManager = new AuthManager(serverUrl, serverType, authCallback)
|
const authManager = new AuthManager(
|
||||||
|
serverUrl,
|
||||||
|
serverType,
|
||||||
|
requestClient,
|
||||||
|
authCallback
|
||||||
|
)
|
||||||
jest
|
jest
|
||||||
.spyOn(authModule, 'parseAndSubmitAuthorizeForm')
|
.spyOn(requestClient, 'authorize')
|
||||||
.mockImplementation(() => Promise.resolve())
|
.mockImplementation(() => Promise.resolve())
|
||||||
jest.spyOn(authManager, 'checkSession').mockImplementation(() =>
|
jest.spyOn(authManager, 'checkSession').mockImplementation(() =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
@@ -125,15 +147,19 @@ describe('AuthManager', () => {
|
|||||||
|
|
||||||
await authManager.logIn(userName, password)
|
await authManager.logIn(userName, password)
|
||||||
|
|
||||||
expect(authModule.parseAndSubmitAuthorizeForm).toHaveBeenCalledWith(
|
expect(requestClient.authorize).toHaveBeenCalledWith(
|
||||||
mockLoginAuthoriseRequiredResponse,
|
mockLoginAuthoriseRequiredResponse
|
||||||
serverUrl
|
|
||||||
)
|
)
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should check and return session information if logged in', async (done) => {
|
it('should check and return session information if logged in', async (done) => {
|
||||||
const authManager = new AuthManager(serverUrl, serverType, authCallback)
|
const authManager = new AuthManager(
|
||||||
|
serverUrl,
|
||||||
|
serverType,
|
||||||
|
requestClient,
|
||||||
|
authCallback
|
||||||
|
)
|
||||||
mockedAxios.get.mockImplementation(() =>
|
mockedAxios.get.mockImplementation(() =>
|
||||||
Promise.resolve({ data: '<button onClick="logout">' })
|
Promise.resolve({ data: '<button onClick="logout">' })
|
||||||
)
|
)
|
||||||
@@ -141,9 +167,12 @@ describe('AuthManager', () => {
|
|||||||
const response = await authManager.checkSession()
|
const response = await authManager.checkSession()
|
||||||
expect(response.isLoggedIn).toBeTruthy()
|
expect(response.isLoggedIn).toBeTruthy()
|
||||||
expect(mockedAxios.get).toHaveBeenNthCalledWith(1, `/SASLogon/login`, {
|
expect(mockedAxios.get).toHaveBeenNthCalledWith(1, `/SASLogon/login`, {
|
||||||
|
withCredentials: true,
|
||||||
responseType: 'text',
|
responseType: 'text',
|
||||||
|
transformResponse: undefined,
|
||||||
headers: {
|
headers: {
|
||||||
Accept: '*/*'
|
Accept: '*/*',
|
||||||
|
'Content-Type': 'text/plain'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -151,7 +180,12 @@ describe('AuthManager', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should check and return session information if logged in', async (done) => {
|
it('should check and return session information if logged in', async (done) => {
|
||||||
const authManager = new AuthManager(serverUrl, serverType, authCallback)
|
const authManager = new AuthManager(
|
||||||
|
serverUrl,
|
||||||
|
serverType,
|
||||||
|
requestClient,
|
||||||
|
authCallback
|
||||||
|
)
|
||||||
mockedAxios.get.mockImplementation(() =>
|
mockedAxios.get.mockImplementation(() =>
|
||||||
Promise.resolve({ data: '<button onClick="logout">' })
|
Promise.resolve({ data: '<button onClick="logout">' })
|
||||||
)
|
)
|
||||||
@@ -159,9 +193,12 @@ describe('AuthManager', () => {
|
|||||||
const response = await authManager.checkSession()
|
const response = await authManager.checkSession()
|
||||||
expect(response.isLoggedIn).toBeTruthy()
|
expect(response.isLoggedIn).toBeTruthy()
|
||||||
expect(mockedAxios.get).toHaveBeenNthCalledWith(1, `/SASLogon/login`, {
|
expect(mockedAxios.get).toHaveBeenNthCalledWith(1, `/SASLogon/login`, {
|
||||||
|
withCredentials: true,
|
||||||
responseType: 'text',
|
responseType: 'text',
|
||||||
|
transformResponse: undefined,
|
||||||
headers: {
|
headers: {
|
||||||
Accept: '*/*'
|
Accept: '*/*',
|
||||||
|
'Content-Type': 'text/plain'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +1,14 @@
|
|||||||
import { ServerType } from '@sasjs/utils/types'
|
import { ServerType } from '@sasjs/utils/types'
|
||||||
import { ErrorResponse } from '..'
|
import { ErrorResponse } from '..'
|
||||||
import { SASViyaApiClient } from '../SASViyaApiClient'
|
import { SASViyaApiClient } from '../SASViyaApiClient'
|
||||||
import {
|
import { ComputeJobExecutionError, LoginRequiredError } from '../types'
|
||||||
ComputeJobExecutionError,
|
import { parseWeboutResponse } from '../utils'
|
||||||
LoginRequiredError,
|
import { BaseJobExecutor } from './JobExecutor'
|
||||||
SASjsRequest
|
|
||||||
} from '../types'
|
|
||||||
import {
|
|
||||||
asyncForEach,
|
|
||||||
parseGeneratedCode,
|
|
||||||
parseSourceCode,
|
|
||||||
parseWeboutResponse
|
|
||||||
} from '../utils'
|
|
||||||
import { ExecuteFunction, JobExecutor } from './JobExecutor'
|
|
||||||
import { parseSasWork } from './parseSasWork'
|
|
||||||
|
|
||||||
export class ComputeJobExecutor implements JobExecutor {
|
export class ComputeJobExecutor extends BaseJobExecutor {
|
||||||
waitingRequests: ExecuteFunction[] = []
|
constructor(serverUrl: string, private sasViyaApiClient: SASViyaApiClient) {
|
||||||
requests: SASjsRequest[] = []
|
super(serverUrl, ServerType.SasViya)
|
||||||
|
}
|
||||||
constructor(
|
|
||||||
private serverUrl: string,
|
|
||||||
private sasViyaApiClient: SASViyaApiClient
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async execute(
|
async execute(
|
||||||
sasJob: string,
|
sasJob: string,
|
||||||
@@ -34,6 +20,7 @@ export class ComputeJobExecutor implements JobExecutor {
|
|||||||
const loginCallback = loginRequiredCallback || (() => Promise.resolve())
|
const loginCallback = loginRequiredCallback || (() => Promise.resolve())
|
||||||
const waitForResult = true
|
const waitForResult = true
|
||||||
const expectWebout = true
|
const expectWebout = true
|
||||||
|
|
||||||
return this.sasViyaApiClient
|
return this.sasViyaApiClient
|
||||||
?.executeComputeJob(
|
?.executeComputeJob(
|
||||||
sasJob,
|
sasJob,
|
||||||
@@ -46,7 +33,6 @@ export class ComputeJobExecutor implements JobExecutor {
|
|||||||
)
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.appendRequest(response, sasJob, config.debug)
|
this.appendRequest(response, sasJob, config.debug)
|
||||||
|
|
||||||
let responseJson
|
let responseJson
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -67,70 +53,11 @@ export class ComputeJobExecutor implements JobExecutor {
|
|||||||
}
|
}
|
||||||
if (e instanceof LoginRequiredError) {
|
if (e instanceof LoginRequiredError) {
|
||||||
await loginCallback()
|
await loginCallback()
|
||||||
this.waitingRequests.push(() =>
|
this.appendWaitingRequest(() =>
|
||||||
this.execute(sasJob, data, config, loginRequiredCallback)
|
this.execute(sasJob, data, config, loginRequiredCallback)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return Promise.reject(new ErrorResponse(e?.message, e))
|
return Promise.reject(new ErrorResponse(e?.message, e))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
resendWaitingRequests = async () => {
|
|
||||||
await asyncForEach(
|
|
||||||
this.waitingRequests,
|
|
||||||
async (waitingRequest: ExecuteFunction) => {
|
|
||||||
await waitingRequest()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
this.waitingRequests = []
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
getRequests = () => this.requests
|
|
||||||
|
|
||||||
clearRequests = () => {
|
|
||||||
this.requests = []
|
|
||||||
}
|
|
||||||
|
|
||||||
private async appendRequest(response: any, program: string, debug: boolean) {
|
|
||||||
let sourceCode = ''
|
|
||||||
let generatedCode = ''
|
|
||||||
let sasWork = null
|
|
||||||
|
|
||||||
if (debug) {
|
|
||||||
if (response?.result && response?.log) {
|
|
||||||
sourceCode = parseSourceCode(response.log)
|
|
||||||
generatedCode = parseGeneratedCode(response.log)
|
|
||||||
|
|
||||||
if (response.log) {
|
|
||||||
sasWork = response.log
|
|
||||||
} else {
|
|
||||||
sasWork = JSON.parse(parseWeboutResponse(response.result)).WORK
|
|
||||||
}
|
|
||||||
} else if (response?.result) {
|
|
||||||
sourceCode = parseSourceCode(response.result)
|
|
||||||
generatedCode = parseGeneratedCode(response.result)
|
|
||||||
sasWork = await parseSasWork(
|
|
||||||
response.result,
|
|
||||||
debug,
|
|
||||||
this.serverUrl,
|
|
||||||
ServerType.SasViya
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.requests.push({
|
|
||||||
logFile: response?.log || response?.result || response,
|
|
||||||
serviceLink: program,
|
|
||||||
timestamp: new Date(),
|
|
||||||
sourceCode,
|
|
||||||
generatedCode,
|
|
||||||
SASWORK: sasWork
|
|
||||||
})
|
|
||||||
|
|
||||||
if (this.requests.length > 20) {
|
|
||||||
this.requests.splice(0, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,14 @@
|
|||||||
import { ServerType } from '@sasjs/utils/types'
|
import { ServerType } from '@sasjs/utils/types'
|
||||||
import { ErrorResponse } from '..'
|
import { ErrorResponse } from '..'
|
||||||
import { SASViyaApiClient } from '../SASViyaApiClient'
|
import { SASViyaApiClient } from '../SASViyaApiClient'
|
||||||
import { JobExecutionError, LoginRequiredError, SASjsRequest } from '../types'
|
import { JobExecutionError, LoginRequiredError } from '../types'
|
||||||
import {
|
import { parseWeboutResponse } from '../utils'
|
||||||
asyncForEach,
|
import { BaseJobExecutor } from './JobExecutor'
|
||||||
parseGeneratedCode,
|
|
||||||
parseSourceCode,
|
|
||||||
parseWeboutResponse
|
|
||||||
} from '../utils'
|
|
||||||
import { ExecuteFunction, JobExecutor } from './JobExecutor'
|
|
||||||
import { parseSasWork } from './parseSasWork'
|
|
||||||
|
|
||||||
export class JesJobExecutor implements JobExecutor {
|
export class JesJobExecutor extends BaseJobExecutor {
|
||||||
waitingRequests: ExecuteFunction[] = []
|
constructor(serverUrl: string, private sasViyaApiClient: SASViyaApiClient) {
|
||||||
requests: SASjsRequest[] = []
|
super(serverUrl, ServerType.SasViya)
|
||||||
|
}
|
||||||
constructor(
|
|
||||||
private serverUrl: string,
|
|
||||||
private sasViyaApiClient: SASViyaApiClient
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async execute(
|
async execute(
|
||||||
sasJob: string,
|
sasJob: string,
|
||||||
@@ -53,70 +43,11 @@ export class JesJobExecutor implements JobExecutor {
|
|||||||
}
|
}
|
||||||
if (e instanceof LoginRequiredError) {
|
if (e instanceof LoginRequiredError) {
|
||||||
await loginCallback()
|
await loginCallback()
|
||||||
this.waitingRequests.push(() =>
|
this.appendWaitingRequest(() =>
|
||||||
this.execute(sasJob, data, config, loginRequiredCallback)
|
this.execute(sasJob, data, config, loginRequiredCallback)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return Promise.reject(new ErrorResponse(e?.message, e))
|
return Promise.reject(new ErrorResponse(e?.message, e))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
resendWaitingRequests = async () => {
|
|
||||||
await asyncForEach(
|
|
||||||
this.waitingRequests,
|
|
||||||
async (waitingRequest: ExecuteFunction) => {
|
|
||||||
await waitingRequest()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
this.waitingRequests = []
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
getRequests = () => this.requests
|
|
||||||
|
|
||||||
clearRequests = () => {
|
|
||||||
this.requests = []
|
|
||||||
}
|
|
||||||
|
|
||||||
private async appendRequest(response: any, program: string, debug: boolean) {
|
|
||||||
let sourceCode = ''
|
|
||||||
let generatedCode = ''
|
|
||||||
let sasWork = null
|
|
||||||
|
|
||||||
if (debug) {
|
|
||||||
if (response?.result && response?.log) {
|
|
||||||
sourceCode = parseSourceCode(response.log)
|
|
||||||
generatedCode = parseGeneratedCode(response.log)
|
|
||||||
|
|
||||||
if (response.log) {
|
|
||||||
sasWork = response.log
|
|
||||||
} else {
|
|
||||||
sasWork = JSON.parse(parseWeboutResponse(response.result)).WORK
|
|
||||||
}
|
|
||||||
} else if (response?.result) {
|
|
||||||
sourceCode = parseSourceCode(response.result)
|
|
||||||
generatedCode = parseGeneratedCode(response.result)
|
|
||||||
sasWork = await parseSasWork(
|
|
||||||
response.result,
|
|
||||||
debug,
|
|
||||||
this.serverUrl,
|
|
||||||
ServerType.SasViya
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.requests.push({
|
|
||||||
logFile: response?.log || response?.result || response,
|
|
||||||
serviceLink: program,
|
|
||||||
timestamp: new Date(),
|
|
||||||
sourceCode,
|
|
||||||
generatedCode,
|
|
||||||
SASWORK: sasWork
|
|
||||||
})
|
|
||||||
|
|
||||||
if (this.requests.length > 20) {
|
|
||||||
this.requests.splice(0, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
|
import { ServerType } from '@sasjs/utils/types'
|
||||||
|
import { parseSasWork } from '.'
|
||||||
import { SASjsRequest } from '../types'
|
import { SASjsRequest } from '../types'
|
||||||
|
import {
|
||||||
|
asyncForEach,
|
||||||
|
parseGeneratedCode,
|
||||||
|
parseSourceCode,
|
||||||
|
parseWeboutResponse
|
||||||
|
} from '../utils'
|
||||||
|
|
||||||
export type ExecuteFunction = () => Promise<any>
|
export type ExecuteFunction = () => Promise<any>
|
||||||
|
|
||||||
@@ -10,8 +18,89 @@ export interface JobExecutor {
|
|||||||
loginRequiredCallback?: any,
|
loginRequiredCallback?: any,
|
||||||
accessToken?: string
|
accessToken?: string
|
||||||
) => Promise<any>
|
) => Promise<any>
|
||||||
waitingRequests: ExecuteFunction[]
|
|
||||||
resendWaitingRequests: () => Promise<void>
|
resendWaitingRequests: () => Promise<void>
|
||||||
getRequests: () => SASjsRequest[]
|
getRequests: () => SASjsRequest[]
|
||||||
clearRequests: () => void
|
clearRequests: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export abstract class BaseJobExecutor implements JobExecutor {
|
||||||
|
constructor(protected serverUrl: string, protected serverType: ServerType) {}
|
||||||
|
|
||||||
|
private waitingRequests: ExecuteFunction[] = []
|
||||||
|
private requests: SASjsRequest[] = []
|
||||||
|
|
||||||
|
abstract execute(
|
||||||
|
sasJob: string,
|
||||||
|
data: any,
|
||||||
|
config: any,
|
||||||
|
loginRequiredCallback?: any,
|
||||||
|
accessToken?: string | undefined
|
||||||
|
): Promise<any>
|
||||||
|
|
||||||
|
resendWaitingRequests = async () => {
|
||||||
|
await asyncForEach(
|
||||||
|
this.waitingRequests,
|
||||||
|
async (waitingRequest: ExecuteFunction) => {
|
||||||
|
await waitingRequest()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
this.waitingRequests = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
getRequests = () => this.requests
|
||||||
|
|
||||||
|
clearRequests = () => {
|
||||||
|
this.requests = []
|
||||||
|
}
|
||||||
|
|
||||||
|
protected appendWaitingRequest(request: ExecuteFunction) {
|
||||||
|
this.waitingRequests.push(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async appendRequest(
|
||||||
|
response: any,
|
||||||
|
program: string,
|
||||||
|
debug: boolean
|
||||||
|
) {
|
||||||
|
let sourceCode = ''
|
||||||
|
let generatedCode = ''
|
||||||
|
let sasWork = null
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
if (response?.result && response?.log) {
|
||||||
|
sourceCode = parseSourceCode(response.log)
|
||||||
|
generatedCode = parseGeneratedCode(response.log)
|
||||||
|
|
||||||
|
if (response.log) {
|
||||||
|
sasWork = response.log
|
||||||
|
} else {
|
||||||
|
sasWork = JSON.parse(parseWeboutResponse(response.result)).WORK
|
||||||
|
}
|
||||||
|
} else if (response?.result) {
|
||||||
|
sourceCode = parseSourceCode(response.result)
|
||||||
|
generatedCode = parseGeneratedCode(response.result)
|
||||||
|
sasWork = await parseSasWork(
|
||||||
|
response.result,
|
||||||
|
debug,
|
||||||
|
this.serverUrl,
|
||||||
|
this.serverType
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.requests.push({
|
||||||
|
logFile: response?.log || response?.result || response,
|
||||||
|
serviceLink: program,
|
||||||
|
timestamp: new Date(),
|
||||||
|
sourceCode,
|
||||||
|
generatedCode,
|
||||||
|
SASWORK: sasWork
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.requests.length > 20) {
|
||||||
|
this.requests.splice(0, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,28 +4,19 @@ import { generateFileUploadForm } from '../file/generateFileUploadForm'
|
|||||||
import { generateTableUploadForm } from '../file/generateTableUploadForm'
|
import { generateTableUploadForm } from '../file/generateTableUploadForm'
|
||||||
import { RequestClient } from '../request/RequestClient'
|
import { RequestClient } from '../request/RequestClient'
|
||||||
import { SASViyaApiClient } from '../SASViyaApiClient'
|
import { SASViyaApiClient } from '../SASViyaApiClient'
|
||||||
import { SASjsRequest } from '../types'
|
import { isRelativePath } from '../utils'
|
||||||
import {
|
import { BaseJobExecutor } from './JobExecutor'
|
||||||
asyncForEach,
|
|
||||||
isRelativePath,
|
|
||||||
parseGeneratedCode,
|
|
||||||
parseSourceCode,
|
|
||||||
parseWeboutResponse
|
|
||||||
} from '../utils'
|
|
||||||
import { ExecuteFunction, JobExecutor } from './JobExecutor'
|
|
||||||
import { parseSasWork } from './parseSasWork'
|
|
||||||
|
|
||||||
export class WebJobExecutor implements JobExecutor {
|
|
||||||
waitingRequests: ExecuteFunction[] = []
|
|
||||||
requests: SASjsRequest[] = []
|
|
||||||
|
|
||||||
|
export class WebJobExecutor extends BaseJobExecutor {
|
||||||
constructor(
|
constructor(
|
||||||
private serverUrl: string,
|
serverUrl: string,
|
||||||
private serverType: ServerType,
|
serverType: ServerType,
|
||||||
private jobsPath: string,
|
private jobsPath: string,
|
||||||
private requestClient: RequestClient,
|
private requestClient: RequestClient,
|
||||||
private sasViyaApiClient: SASViyaApiClient
|
private sasViyaApiClient: SASViyaApiClient
|
||||||
) {}
|
) {
|
||||||
|
super(serverUrl, serverType)
|
||||||
|
}
|
||||||
|
|
||||||
async execute(
|
async execute(
|
||||||
sasJob: string,
|
sasJob: string,
|
||||||
@@ -89,15 +80,7 @@ export class WebJobExecutor implements JobExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.requestClient!.post(
|
return this.requestClient!.post(apiUrl, formData, undefined)
|
||||||
apiUrl,
|
|
||||||
formData,
|
|
||||||
undefined,
|
|
||||||
'application/json',
|
|
||||||
{
|
|
||||||
referrerPolicy: 'same-origin'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(async (res) => {
|
.then(async (res) => {
|
||||||
this.appendRequest(res, sasJob, config.debug)
|
this.appendRequest(res, sasJob, config.debug)
|
||||||
return res.result
|
return res.result
|
||||||
@@ -108,7 +91,7 @@ export class WebJobExecutor implements JobExecutor {
|
|||||||
}
|
}
|
||||||
if (e instanceof LoginRequiredError) {
|
if (e instanceof LoginRequiredError) {
|
||||||
await loginCallback()
|
await loginCallback()
|
||||||
this.waitingRequests.push(() =>
|
this.appendWaitingRequest(() =>
|
||||||
this.execute(sasJob, data, config, loginRequiredCallback)
|
this.execute(sasJob, data, config, loginRequiredCallback)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -116,24 +99,6 @@ export class WebJobExecutor implements JobExecutor {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
resendWaitingRequests = async () => {
|
|
||||||
await asyncForEach(
|
|
||||||
this.waitingRequests,
|
|
||||||
async (waitingRequest: ExecuteFunction) => {
|
|
||||||
await waitingRequest()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
this.waitingRequests = []
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
getRequests = () => this.requests
|
|
||||||
|
|
||||||
clearRequests = () => {
|
|
||||||
this.requests = []
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getJobUri(sasJob: string) {
|
private async getJobUri(sasJob: string) {
|
||||||
if (!this.sasViyaApiClient) return ''
|
if (!this.sasViyaApiClient) return ''
|
||||||
let uri = ''
|
let uri = ''
|
||||||
@@ -174,47 +139,6 @@ export class WebJobExecutor implements JobExecutor {
|
|||||||
return requestParams
|
return requestParams
|
||||||
}
|
}
|
||||||
|
|
||||||
private async appendRequest(response: any, program: string, debug: boolean) {
|
|
||||||
let sourceCode = ''
|
|
||||||
let generatedCode = ''
|
|
||||||
let sasWork = null
|
|
||||||
|
|
||||||
if (debug) {
|
|
||||||
if (response?.result && response?.log) {
|
|
||||||
sourceCode = parseSourceCode(response.log)
|
|
||||||
generatedCode = parseGeneratedCode(response.log)
|
|
||||||
|
|
||||||
if (response.log) {
|
|
||||||
sasWork = response.log
|
|
||||||
} else {
|
|
||||||
sasWork = JSON.parse(parseWeboutResponse(response.result)).WORK
|
|
||||||
}
|
|
||||||
} else if (response?.result) {
|
|
||||||
sourceCode = parseSourceCode(response.result)
|
|
||||||
generatedCode = parseGeneratedCode(response.result)
|
|
||||||
sasWork = await parseSasWork(
|
|
||||||
response.result,
|
|
||||||
debug,
|
|
||||||
this.serverUrl,
|
|
||||||
this.serverType
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.requests.push({
|
|
||||||
logFile: response?.log || response?.result || response,
|
|
||||||
serviceLink: program,
|
|
||||||
timestamp: new Date(),
|
|
||||||
sourceCode,
|
|
||||||
generatedCode,
|
|
||||||
SASWORK: sasWork
|
|
||||||
})
|
|
||||||
|
|
||||||
if (this.requests.length > 20) {
|
|
||||||
this.requests.splice(0, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private parseSAS9ErrorResponse(response: string) {
|
private parseSAS9ErrorResponse(response: string) {
|
||||||
const logLines = response.split('\n')
|
const logLines = response.split('\n')
|
||||||
const parsedLines: string[] = []
|
const parsedLines: string[] = []
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||||
import { CsrfToken, JobExecutionError } from '..'
|
import { CsrfToken, JobExecutionError } from '..'
|
||||||
|
import { isAuthorizeFormRequired } 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'
|
||||||
@@ -33,10 +34,11 @@ export interface HttpClient {
|
|||||||
): Promise<{ result: T; etag: string }>
|
): Promise<{ result: T; etag: string }>
|
||||||
|
|
||||||
getCsrfToken(type: 'general' | 'file'): CsrfToken | undefined
|
getCsrfToken(type: 'general' | 'file'): CsrfToken | undefined
|
||||||
|
clearCsrfTokens(): void
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RequestClient implements HttpClient {
|
export class RequestClient implements HttpClient {
|
||||||
private csrfToken: CsrfToken | undefined
|
private csrfToken: CsrfToken = { headerName: '', value: '' }
|
||||||
private fileUploadCsrfToken: CsrfToken | undefined
|
private fileUploadCsrfToken: CsrfToken | undefined
|
||||||
private httpClient: AxiosInstance
|
private httpClient: AxiosInstance
|
||||||
|
|
||||||
@@ -48,6 +50,11 @@ export class RequestClient implements HttpClient {
|
|||||||
return type === 'file' ? this.fileUploadCsrfToken : this.csrfToken
|
return type === 'file' ? this.fileUploadCsrfToken : this.csrfToken
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public clearCsrfTokens() {
|
||||||
|
this.csrfToken = { headerName: '', value: '' }
|
||||||
|
this.fileUploadCsrfToken = { headerName: '', value: '' }
|
||||||
|
}
|
||||||
|
|
||||||
public async get<T>(
|
public async get<T>(
|
||||||
url: string,
|
url: string,
|
||||||
accessToken: string | undefined,
|
accessToken: string | undefined,
|
||||||
@@ -68,27 +75,37 @@ export class RequestClient implements HttpClient {
|
|||||||
requestConfig.transformResponse = undefined
|
requestConfig.transformResponse = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return this.httpClient
|
||||||
const response = await this.httpClient.get<T>(url, requestConfig)
|
.get<T>(url, requestConfig)
|
||||||
const etag = response?.headers ? response.headers['etag'] : ''
|
.then((response) => {
|
||||||
|
throwIfError(response)
|
||||||
|
const etag = response?.headers ? response.headers['etag'] : ''
|
||||||
|
|
||||||
return {
|
return {
|
||||||
result: response.data as T,
|
result: response.data as T,
|
||||||
etag
|
etag
|
||||||
}
|
}
|
||||||
} catch (e) {
|
})
|
||||||
const response = e.response as AxiosResponse
|
.catch(async (e) => {
|
||||||
if (response?.status === 403 || response?.status === 449) {
|
const response = e.response as AxiosResponse
|
||||||
this.parseAndSetCsrfToken(response)
|
if (e instanceof AuthorizeError) {
|
||||||
if (this.csrfToken) {
|
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)
|
return this.get<T>(url, accessToken, contentType, overrideHeaders)
|
||||||
}
|
}
|
||||||
|
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
|
throw e
|
||||||
} else if (response?.status === 404) {
|
})
|
||||||
throw new NotFoundError(url)
|
|
||||||
}
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public post<T>(
|
public post<T>(
|
||||||
@@ -99,7 +116,7 @@ export class RequestClient implements HttpClient {
|
|||||||
overrideHeaders: { [key: string]: string | number } = {}
|
overrideHeaders: { [key: string]: string | number } = {}
|
||||||
): Promise<{ result: T; etag: string }> {
|
): Promise<{ result: T; etag: string }> {
|
||||||
const headers = {
|
const headers = {
|
||||||
...this.getHeaders(accessToken, contentType),
|
...this.getHeaders(accessToken, contentType, url.endsWith('login.do')),
|
||||||
...overrideHeaders
|
...overrideHeaders
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,13 +133,25 @@ export class RequestClient implements HttpClient {
|
|||||||
.catch(async (e) => {
|
.catch(async (e) => {
|
||||||
const response = e.response as AxiosResponse
|
const response = e.response as AxiosResponse
|
||||||
if (e instanceof AuthorizeError) {
|
if (e instanceof AuthorizeError) {
|
||||||
await this.post(e.confirmUrl, { value: true }, undefined)
|
const res = await this.get(e.confirmUrl, undefined, 'text/plain')
|
||||||
return this.post<T>(url, data, accessToken)
|
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.csrfToken) {
|
||||||
|
this.csrfToken.value = ''
|
||||||
}
|
}
|
||||||
if (response?.status === 403 || response?.status === 449) {
|
if (response?.status === 403 || response?.status === 449) {
|
||||||
this.parseAndSetCsrfToken(response)
|
this.parseAndSetCsrfToken(response)
|
||||||
|
|
||||||
if (this.csrfToken) {
|
if (this.csrfToken.headerName && this.csrfToken.value) {
|
||||||
return this.post<T>(url, data, accessToken)
|
return this.post<T>(url, data, accessToken)
|
||||||
}
|
}
|
||||||
throw e
|
throw e
|
||||||
@@ -157,7 +186,7 @@ export class RequestClient implements HttpClient {
|
|||||||
if (response?.status === 403 || response?.status === 449) {
|
if (response?.status === 403 || response?.status === 449) {
|
||||||
this.parseAndSetCsrfToken(response)
|
this.parseAndSetCsrfToken(response)
|
||||||
|
|
||||||
if (this.csrfToken) {
|
if (this.csrfToken.headerName && this.csrfToken.value) {
|
||||||
return this.put<T>(url, data, accessToken)
|
return this.put<T>(url, data, accessToken)
|
||||||
}
|
}
|
||||||
throw e
|
throw e
|
||||||
@@ -192,7 +221,7 @@ export class RequestClient implements HttpClient {
|
|||||||
if (response?.status === 403 || response?.status === 449) {
|
if (response?.status === 403 || response?.status === 449) {
|
||||||
this.parseAndSetCsrfToken(response)
|
this.parseAndSetCsrfToken(response)
|
||||||
|
|
||||||
if (this.csrfToken) {
|
if (this.csrfToken.headerName && this.csrfToken.value) {
|
||||||
return this.delete<T>(url, accessToken)
|
return this.delete<T>(url, accessToken)
|
||||||
}
|
}
|
||||||
throw e
|
throw e
|
||||||
@@ -222,7 +251,7 @@ export class RequestClient implements HttpClient {
|
|||||||
if (response?.status === 403 || response?.status === 449) {
|
if (response?.status === 403 || response?.status === 449) {
|
||||||
this.parseAndSetCsrfToken(response)
|
this.parseAndSetCsrfToken(response)
|
||||||
|
|
||||||
if (this.csrfToken) {
|
if (this.csrfToken.headerName && this.csrfToken.value) {
|
||||||
return this.patch<T>(url, accessToken)
|
return this.patch<T>(url, accessToken)
|
||||||
}
|
}
|
||||||
throw e
|
throw e
|
||||||
@@ -264,9 +293,64 @@ export class RequestClient implements HttpClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public authorize = async (response: string) => {
|
||||||
|
let authUrl: string | null = null
|
||||||
|
const params: any = {}
|
||||||
|
|
||||||
|
const responseBody = response.split('<body>')[1].split('</body>')[0]
|
||||||
|
const bodyElement = document.createElement('div')
|
||||||
|
bodyElement.innerHTML = responseBody
|
||||||
|
|
||||||
|
const form = bodyElement.querySelector('#application_authorization')
|
||||||
|
authUrl = form
|
||||||
|
? this.httpClient.defaults.baseURL! + form.getAttribute('action')
|
||||||
|
: null
|
||||||
|
|
||||||
|
const inputs: any = form?.querySelectorAll('input')
|
||||||
|
|
||||||
|
for (const input of inputs) {
|
||||||
|
if (input.name === 'user_oauth_approval') {
|
||||||
|
input.value = 'true'
|
||||||
|
}
|
||||||
|
|
||||||
|
params[input.name] = input.value
|
||||||
|
}
|
||||||
|
|
||||||
|
const csrfTokenKey = Object.keys(params).find((k) =>
|
||||||
|
k?.toLowerCase().includes('csrf')
|
||||||
|
)
|
||||||
|
if (csrfTokenKey) {
|
||||||
|
this.csrfToken.value = params[csrfTokenKey]
|
||||||
|
this.csrfToken.headerName = this.csrfToken.headerName || 'x-csrf-token'
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = new FormData()
|
||||||
|
|
||||||
|
for (const key in params) {
|
||||||
|
if (params.hasOwnProperty(key)) {
|
||||||
|
formData.append(key, params[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!authUrl) {
|
||||||
|
throw new Error('Auth Form URL is null or undefined.')
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.httpClient
|
||||||
|
.post(authUrl, formData, {
|
||||||
|
responseType: 'text',
|
||||||
|
headers: { Accept: '*/*', 'Content-Type': 'text/plain' }
|
||||||
|
})
|
||||||
|
.then((res) => res.data)
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private getHeaders = (
|
private getHeaders = (
|
||||||
accessToken: string | undefined,
|
accessToken: string | undefined,
|
||||||
contentType: string
|
contentType: string,
|
||||||
|
appendCsrfToken = true
|
||||||
) => {
|
) => {
|
||||||
const headers: any = {
|
const headers: any = {
|
||||||
'Content-Type': contentType
|
'Content-Type': contentType
|
||||||
@@ -280,7 +364,7 @@ export class RequestClient implements HttpClient {
|
|||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
headers.Authorization = `Bearer ${accessToken}`
|
headers.Authorization = `Bearer ${accessToken}`
|
||||||
}
|
}
|
||||||
if (this.csrfToken?.value) {
|
if (this.csrfToken.headerName && this.csrfToken.value) {
|
||||||
headers[this.csrfToken.headerName] = this.csrfToken.value
|
headers[this.csrfToken.headerName] = this.csrfToken.value
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,10 +416,8 @@ const throwIfError = (response: AxiosResponse) => {
|
|||||||
throw new LoginRequiredError()
|
throw new LoginRequiredError()
|
||||||
}
|
}
|
||||||
if (response.data?.auth_request) {
|
if (response.data?.auth_request) {
|
||||||
throw new AuthorizeError(
|
const authorizeRequestUrl = response.request.responseURL
|
||||||
response.data.message,
|
throw new AuthorizeError(response.data.message, authorizeRequestUrl)
|
||||||
response.data.options.confirm.location
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const error = parseError(response.data as string)
|
const error = parseError(response.data as string)
|
||||||
|
|||||||
Reference in New Issue
Block a user