mirror of
https://github.com/sasjs/adapter.git
synced 2026-01-03 18:50:05 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f62cd0148 | ||
| bd92c1925e | |||
|
|
6c29d7823b | ||
| 3c9f133374 | |||
| e72195ca5d | |||
| 3e7ddf59b4 | |||
| cd67fb38dc | |||
| 78149e6c54 | |||
| 63e220c5be | |||
| 8464e506e0 | |||
| 0bc69401e5 | |||
| 47fe7686cb | |||
|
|
dd2b3671fd | ||
| bd03b2b06d | |||
|
|
2b2b8e6429 | ||
|
|
5375d0a208 | ||
|
|
f2da84829e | ||
|
|
f172ad66bc | ||
|
|
046c58bb80 | ||
|
|
bf825a4f65 | ||
|
|
d58cff9081 | ||
|
|
7ab1964746 |
@@ -61,24 +61,21 @@ export class FileUploader {
|
|||||||
'Content-Type': 'text/plain'
|
'Content-Type': 'text/plain'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// currently only web approach is supported for file upload
|
||||||
|
// therefore log is part of response with debug enabled and must be parsed
|
||||||
return this.requestClient
|
return this.requestClient
|
||||||
.post(uploadUrl, formData, undefined, 'application/json', headers)
|
.post(uploadUrl, formData, undefined, 'application/json', headers)
|
||||||
.then(async (res) => {
|
.then(async (res) => {
|
||||||
// for web approach on Viya
|
|
||||||
if (
|
if (
|
||||||
this.sasjsConfig.debug &&
|
this.sasjsConfig.serverType === ServerType.SasViya &&
|
||||||
(this.sasjsConfig.useComputeApi === null ||
|
this.sasjsConfig.debug
|
||||||
this.sasjsConfig.useComputeApi === undefined) &&
|
|
||||||
this.sasjsConfig.serverType === ServerType.SasViya
|
|
||||||
) {
|
) {
|
||||||
const jsonResponse = await parseSasViyaDebugResponse(
|
const jsonResponse = await parseSasViyaDebugResponse(
|
||||||
res.result as string,
|
res.result as string,
|
||||||
this.requestClient,
|
this.requestClient,
|
||||||
this.sasjsConfig.serverUrl
|
this.sasjsConfig.serverUrl
|
||||||
)
|
)
|
||||||
return typeof jsonResponse === 'string'
|
return jsonResponse
|
||||||
? getValidJson(jsonResponse)
|
|
||||||
: jsonResponse
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return typeof res.result === 'string'
|
return typeof res.result === 'string'
|
||||||
|
|||||||
@@ -10,9 +10,13 @@ import { isUrl } from './utils'
|
|||||||
export class SAS9ApiClient {
|
export class SAS9ApiClient {
|
||||||
private requestClient: Sas9RequestClient
|
private requestClient: Sas9RequestClient
|
||||||
|
|
||||||
constructor(private serverUrl: string, private jobsPath: string) {
|
constructor(
|
||||||
|
private serverUrl: string,
|
||||||
|
private jobsPath: string,
|
||||||
|
allowInsecureRequests: boolean
|
||||||
|
) {
|
||||||
if (serverUrl) isUrl(serverUrl)
|
if (serverUrl) isUrl(serverUrl)
|
||||||
this.requestClient = new Sas9RequestClient(serverUrl, false)
|
this.requestClient = new Sas9RequestClient(serverUrl, allowInsecureRequests)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
17
src/SASjs.ts
17
src/SASjs.ts
@@ -619,6 +619,11 @@ export default class SASjs {
|
|||||||
authConfig
|
authConfig
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
if (!config.contextName)
|
||||||
|
config = {
|
||||||
|
...config,
|
||||||
|
contextName: 'SAS Job Execution compute context'
|
||||||
|
}
|
||||||
return await this.jesJobExecutor!.execute(
|
return await this.jesJobExecutor!.execute(
|
||||||
sasJob,
|
sasJob,
|
||||||
data,
|
data,
|
||||||
@@ -749,7 +754,11 @@ export default class SASjs {
|
|||||||
)
|
)
|
||||||
sasApiClient.debug = this.sasjsConfig.debug
|
sasApiClient.debug = this.sasjsConfig.debug
|
||||||
} else if (this.sasjsConfig.serverType === ServerType.Sas9) {
|
} else if (this.sasjsConfig.serverType === ServerType.Sas9) {
|
||||||
sasApiClient = new SAS9ApiClient(serverUrl, this.jobsPath)
|
sasApiClient = new SAS9ApiClient(
|
||||||
|
serverUrl,
|
||||||
|
this.jobsPath,
|
||||||
|
this.sasjsConfig.allowInsecureRequests
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let sasClientConfig: any = null
|
let sasClientConfig: any = null
|
||||||
@@ -944,7 +953,8 @@ export default class SASjs {
|
|||||||
else
|
else
|
||||||
this.sas9ApiClient = new SAS9ApiClient(
|
this.sas9ApiClient = new SAS9ApiClient(
|
||||||
this.sasjsConfig.serverUrl,
|
this.sasjsConfig.serverUrl,
|
||||||
this.jobsPath
|
this.jobsPath,
|
||||||
|
this.sasjsConfig.allowInsecureRequests
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -965,7 +975,8 @@ export default class SASjs {
|
|||||||
this.sas9JobExecutor = new Sas9JobExecutor(
|
this.sas9JobExecutor = new Sas9JobExecutor(
|
||||||
this.sasjsConfig.serverUrl,
|
this.sasjsConfig.serverUrl,
|
||||||
this.sasjsConfig.serverType!,
|
this.sasjsConfig.serverType!,
|
||||||
this.jobsPath
|
this.jobsPath,
|
||||||
|
this.sasjsConfig.allowInsecureRequests
|
||||||
)
|
)
|
||||||
|
|
||||||
this.computeJobExecutor = new ComputeJobExecutor(
|
this.computeJobExecutor = new ComputeJobExecutor(
|
||||||
|
|||||||
@@ -124,7 +124,8 @@ export class AuthManager {
|
|||||||
|
|
||||||
if (!isLoggedIn) {
|
if (!isLoggedIn) {
|
||||||
//We will logout to make sure cookies are removed and login form is presented
|
//We will logout to make sure cookies are removed and login form is presented
|
||||||
this.logOut()
|
//Residue can happen in case of session expiration
|
||||||
|
await this.logOut()
|
||||||
|
|
||||||
const { result: formResponse } = await this.requestClient.get<string>(
|
const { result: formResponse } = await this.requestClient.get<string>(
|
||||||
this.loginUrl.replace('.do', ''),
|
this.loginUrl.replace('.do', ''),
|
||||||
|
|||||||
@@ -16,10 +16,11 @@ export class Sas9JobExecutor extends BaseJobExecutor {
|
|||||||
constructor(
|
constructor(
|
||||||
serverUrl: string,
|
serverUrl: string,
|
||||||
serverType: ServerType,
|
serverType: ServerType,
|
||||||
private jobsPath: string
|
private jobsPath: string,
|
||||||
|
allowInsecureRequests: boolean
|
||||||
) {
|
) {
|
||||||
super(serverUrl, serverType)
|
super(serverUrl, serverType)
|
||||||
this.requestClient = new Sas9RequestClient(serverUrl, false)
|
this.requestClient = new Sas9RequestClient(serverUrl, allowInsecureRequests)
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(sasJob: string, data: any, config: any) {
|
async execute(sasJob: string, data: any, config: any) {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import { ServerType } from '@sasjs/utils/types'
|
|||||||
import {
|
import {
|
||||||
ErrorResponse,
|
ErrorResponse,
|
||||||
JobExecutionError,
|
JobExecutionError,
|
||||||
LoginRequiredError
|
LoginRequiredError,
|
||||||
|
WeboutResponseError
|
||||||
} from '../types/errors'
|
} from '../types/errors'
|
||||||
import { generateFileUploadForm } from '../file/generateFileUploadForm'
|
import { generateFileUploadForm } from '../file/generateFileUploadForm'
|
||||||
import { generateTableUploadForm } from '../file/generateTableUploadForm'
|
import { generateTableUploadForm } from '../file/generateTableUploadForm'
|
||||||
@@ -54,7 +55,21 @@ export class WebJobExecutor extends BaseJobExecutor {
|
|||||||
|
|
||||||
apiUrl += jobUri.length > 0 ? '&_job=' + jobUri : ''
|
apiUrl += jobUri.length > 0 ? '&_job=' + jobUri : ''
|
||||||
|
|
||||||
apiUrl += config.contextName ? `&_contextname=${config.contextName}` : ''
|
if (jobUri.length > 0) {
|
||||||
|
apiUrl += '&_job=' + jobUri
|
||||||
|
/**
|
||||||
|
* Using both _job and _program parameters will cause a conflict in the JES web app, as it’s not clear whether or not the server should make the extra fetch for the job uri.
|
||||||
|
* To handle this, we add the extra underscore and recreate the _program variable in the SAS side of the SASjs adapter so it remains available for backend developers.
|
||||||
|
*/
|
||||||
|
apiUrl = apiUrl.replace('_program=', '__program=')
|
||||||
|
}
|
||||||
|
|
||||||
|
// if context name exists and is not blank string
|
||||||
|
// then add _contextname variable in apiUrl
|
||||||
|
apiUrl +=
|
||||||
|
config.contextName && !/\s/.test(config.contextName)
|
||||||
|
? `&_contextname=${config.contextName}`
|
||||||
|
: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
let requestParams = {
|
let requestParams = {
|
||||||
@@ -97,10 +112,10 @@ export class WebJobExecutor extends BaseJobExecutor {
|
|||||||
|
|
||||||
const requestPromise = new Promise((resolve, reject) => {
|
const requestPromise = new Promise((resolve, reject) => {
|
||||||
this.requestClient!.post(apiUrl, formData, undefined)
|
this.requestClient!.post(apiUrl, formData, undefined)
|
||||||
.then(async (res) => {
|
.then(async (res: any) => {
|
||||||
if (this.serverType === ServerType.SasViya && config.debug) {
|
if (this.serverType === ServerType.SasViya && config.debug) {
|
||||||
const jsonResponse = await parseSasViyaDebugResponse(
|
const jsonResponse = await parseSasViyaDebugResponse(
|
||||||
res.result as string,
|
res.result,
|
||||||
this.requestClient,
|
this.requestClient,
|
||||||
this.serverUrl
|
this.serverUrl
|
||||||
)
|
)
|
||||||
@@ -108,19 +123,16 @@ export class WebJobExecutor extends BaseJobExecutor {
|
|||||||
resolve(jsonResponse)
|
resolve(jsonResponse)
|
||||||
}
|
}
|
||||||
if (this.serverType === ServerType.Sas9 && config.debug) {
|
if (this.serverType === ServerType.Sas9 && config.debug) {
|
||||||
const jsonResponse = parseWeboutResponse(res.result as string)
|
let jsonResponse = res.result
|
||||||
if (jsonResponse === '') {
|
if (typeof res.result === 'string')
|
||||||
throw new Error(
|
jsonResponse = parseWeboutResponse(res.result, apiUrl)
|
||||||
'Valid JSON could not be extracted from response.'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
getValidJson(jsonResponse)
|
getValidJson(jsonResponse)
|
||||||
this.appendRequest(res, sasJob, config.debug)
|
this.appendRequest(res, sasJob, config.debug)
|
||||||
resolve(res.result)
|
resolve(res.result)
|
||||||
}
|
}
|
||||||
getValidJson(res.result as string)
|
|
||||||
this.appendRequest(res, sasJob, config.debug)
|
this.appendRequest(res, sasJob, config.debug)
|
||||||
|
getValidJson(res.result as string)
|
||||||
resolve(res.result)
|
resolve(res.result)
|
||||||
})
|
})
|
||||||
.catch(async (e: Error) => {
|
.catch(async (e: Error) => {
|
||||||
|
|||||||
@@ -429,13 +429,7 @@ export class RequestClient implements HttpClient {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
try {
|
try {
|
||||||
const weboutResponse = parseWeboutResponse(response.data)
|
parsedResponse = JSON.parse(parseWeboutResponse(response.data))
|
||||||
if (weboutResponse === '') {
|
|
||||||
throw new Error('Valid JSON could not be extracted from response.')
|
|
||||||
}
|
|
||||||
|
|
||||||
const jsonResponse = getValidJson(weboutResponse)
|
|
||||||
parsedResponse = jsonResponse
|
|
||||||
} catch {
|
} catch {
|
||||||
parsedResponse = response.data
|
parsedResponse = response.data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { getValidJson } from '../../utils'
|
import { getValidJson } from '../../utils'
|
||||||
|
import { JsonParseArrayError, InvalidJsonError } from '../../types/errors'
|
||||||
|
|
||||||
describe('jsonValidator', () => {
|
describe('jsonValidator', () => {
|
||||||
it('should not throw an error with a valid json', () => {
|
it('should not throw an error with a valid json', () => {
|
||||||
@@ -19,23 +20,17 @@ describe('jsonValidator', () => {
|
|||||||
|
|
||||||
it('should throw an error with an invalid json', () => {
|
it('should throw an error with an invalid json', () => {
|
||||||
const json = `{\"test\":\"test\"\"test2\":\"test\"}`
|
const json = `{\"test\":\"test\"\"test2\":\"test\"}`
|
||||||
let errorThrown = false
|
const test = () => {
|
||||||
try {
|
|
||||||
getValidJson(json)
|
getValidJson(json)
|
||||||
} catch (error) {
|
|
||||||
errorThrown = true
|
|
||||||
}
|
}
|
||||||
expect(errorThrown).toBe(true)
|
expect(test).toThrowError(InvalidJsonError)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should throw an error when an array is passed', () => {
|
it('should throw an error when an array is passed', () => {
|
||||||
const array = ['hello', 'world']
|
const array = ['hello', 'world']
|
||||||
let errorThrown = false
|
const test = () => {
|
||||||
try {
|
|
||||||
getValidJson(array)
|
getValidJson(array)
|
||||||
} catch (error) {
|
|
||||||
errorThrown = true
|
|
||||||
}
|
}
|
||||||
expect(errorThrown).toBe(true)
|
expect(test).toThrow(JsonParseArrayError)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
7
src/types/errors/InvalidJsonError.ts
Normal file
7
src/types/errors/InvalidJsonError.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export class InvalidJsonError extends Error {
|
||||||
|
constructor() {
|
||||||
|
super('Error: invalid Json string')
|
||||||
|
this.name = 'InvalidJsonError'
|
||||||
|
Object.setPrototypeOf(this, InvalidJsonError.prototype)
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/types/errors/JsonParseArrayError.ts
Normal file
7
src/types/errors/JsonParseArrayError.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export class JsonParseArrayError extends Error {
|
||||||
|
constructor() {
|
||||||
|
super('Can not parse array object to json.')
|
||||||
|
this.name = 'JsonParseArrayError'
|
||||||
|
Object.setPrototypeOf(this, JsonParseArrayError.prototype)
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/types/errors/WeboutResponseError.ts
Normal file
7
src/types/errors/WeboutResponseError.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export class WeboutResponseError extends Error {
|
||||||
|
constructor(public url: string) {
|
||||||
|
super(`Error: error while parsing response from ${url}`)
|
||||||
|
this.name = 'WeboutResponseError'
|
||||||
|
Object.setPrototypeOf(this, WeboutResponseError.prototype)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,3 +8,6 @@ export * from './NotFoundError'
|
|||||||
export * from './ErrorResponse'
|
export * from './ErrorResponse'
|
||||||
export * from './NoSessionStateError'
|
export * from './NoSessionStateError'
|
||||||
export * from './RootFolderNotFoundError'
|
export * from './RootFolderNotFoundError'
|
||||||
|
export * from './JsonParseArrayError'
|
||||||
|
export * from './WeboutResponseError'
|
||||||
|
export * from './InvalidJsonError'
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
|
import { JsonParseArrayError, InvalidJsonError } from '../types/errors'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if string passed then parse the string to json else if throw error for all other types unless it is not a valid json object.
|
* if string passed then parse the string to json else if throw error for all other types unless it is not a valid json object.
|
||||||
* @param str - string to check.
|
* @param str - string to check.
|
||||||
*/
|
*/
|
||||||
export const getValidJson = (str: string | object) => {
|
export const getValidJson = (str: string | object) => {
|
||||||
try {
|
try {
|
||||||
if (Array.isArray(str)) {
|
if (Array.isArray(str)) throw new JsonParseArrayError()
|
||||||
throw new Error('Can not parse array object to json.')
|
|
||||||
}
|
|
||||||
if (typeof str === 'object') return str
|
if (typeof str === 'object') return str
|
||||||
|
|
||||||
return JSON.parse(str)
|
return JSON.parse(str)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error('Invalid JSON response.')
|
if (e instanceof JsonParseArrayError) throw e
|
||||||
|
throw new InvalidJsonError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { RequestClient } from '../request/RequestClient'
|
import { RequestClient } from '../request/RequestClient'
|
||||||
|
import { getValidJson } from '../utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When querying a Viya job using the Web approach (as opposed to using the APIs) with _DEBUG enabled,
|
* When querying a Viya job using the Web approach (as opposed to using the APIs) with _DEBUG enabled,
|
||||||
@@ -25,5 +26,5 @@ export const parseSasViyaDebugResponse = async (
|
|||||||
|
|
||||||
return requestClient
|
return requestClient
|
||||||
.get(serverUrl + jsonUrl, undefined)
|
.get(serverUrl + jsonUrl, undefined)
|
||||||
.then((res) => res.result)
|
.then((res: any) => getValidJson(res.result))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
export const parseWeboutResponse = (response: string) => {
|
import { WeboutResponseError } from '../types/errors'
|
||||||
|
|
||||||
|
export const parseWeboutResponse = (response: string, url?: string) => {
|
||||||
let sasResponse = ''
|
let sasResponse = ''
|
||||||
|
|
||||||
if (response.includes('>>weboutBEGIN<<')) {
|
if (response.includes('>>weboutBEGIN<<')) {
|
||||||
@@ -7,6 +9,7 @@ export const parseWeboutResponse = (response: string) => {
|
|||||||
.split('>>weboutBEGIN<<')[1]
|
.split('>>weboutBEGIN<<')[1]
|
||||||
.split('>>weboutEND<<')[0]
|
.split('>>weboutEND<<')[0]
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (url) throw new WeboutResponseError(url)
|
||||||
sasResponse = ''
|
sasResponse = ''
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user