1
0
mirror of https://github.com/sasjs/adapter.git synced 2026-01-03 18:50:05 +00:00

Compare commits

...

22 Commits

Author SHA1 Message Date
Allan Bowe
4f62cd0148 Merge pull request #518 from sasjs/loginFix
fix: web request and sas9 login
2021-08-18 19:17:43 +03:00
bd92c1925e chore: merge main into loginFix, conflicts resolved 2021-08-18 20:37:24 +05:00
Allan Bowe
6c29d7823b Merge pull request #517 from sasjs/issue-506
fix: double parsing issue in sas9 when debug is enabled
2021-08-18 18:22:54 +03:00
3c9f133374 fix: throw error from parseWeboutResponse function if unable to find webout response 2021-08-18 16:33:26 +05:00
e72195ca5d fix: predefine jsonParseArrayError message 2021-08-18 16:09:51 +05:00
3e7ddf59b4 style: lint 2021-08-18 11:43:09 +02:00
cd67fb38dc fix: web request and login 2021-08-18 11:42:34 +02:00
78149e6c54 chore: remove console log statement 2021-08-18 00:27:10 +05:00
63e220c5be fix: double parsing issue in sas9 debug mode fixed 2021-08-18 00:05:52 +05:00
8464e506e0 fix: check for valid json while parsing sas viya debug response 2021-08-18 00:04:30 +05:00
0bc69401e5 chore: refactor code for getValidJson function 2021-08-18 00:02:48 +05:00
47fe7686cb chore: introduced new error types: InvalidJsonError, JsonParseArrayError, WeboutResponseError 2021-08-18 00:01:28 +05:00
Allan Bowe
dd2b3671fd Merge pull request #513 from sasjs/issue-508
fix: handle context name when it's undefined/null or empty string
2021-08-16 14:34:40 +03:00
bd03b2b06d fix: when contextName is falsy value, do not add it to apiUrl in web approach and fallback to default in jes approach 2021-08-15 16:11:50 +05:00
Allan Bowe
2b2b8e6429 Merge pull request #505 from sasjs/fileuploader-quick-fix
fix(fileUploader): parsing debug response for SASVIYA
2021-08-09 18:22:46 +03:00
Allan Bowe
5375d0a208 Update FileUploader.ts 2021-08-09 15:42:29 +03:00
Saad Jutt
f2da84829e fix(fileUploader): parsing debug response for SASVIYA 2021-08-09 17:28:55 +05:00
Yury Shkoda
f172ad66bc Merge pull request #501 from sasjs/cli-issue-862
Allow self-signed certificates in requests to SAS9
2021-08-06 09:25:32 +03:00
Yury Shkoda
046c58bb80 chore(deps): restore package-lock 2021-08-05 15:57:47 +03:00
Yury Shkoda
bf825a4f65 chore(deps): discard versions bump 2021-08-05 15:55:45 +03:00
Yury Shkoda
d58cff9081 chore(deps): bump ts-jest, ts-loader, typedoc, webpack 2021-08-04 16:59:55 +03:00
Yury Shkoda
7ab1964746 feat(insecureRequests): allow self-signed certificates for SAS9 2021-08-04 16:59:03 +03:00
15 changed files with 95 additions and 50 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,7 @@
export class InvalidJsonError extends Error {
constructor() {
super('Error: invalid Json string')
this.name = 'InvalidJsonError'
Object.setPrototypeOf(this, InvalidJsonError.prototype)
}
}

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

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

View File

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

View File

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

View File

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

View File

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