1
0
mirror of https://github.com/sasjs/adapter.git synced 2025-12-12 09:44:36 +00:00

Compare commits

...

24 Commits

Author SHA1 Message Date
Krishna Acondy
596c1de5cb fix(request-client): return Internal Server Error code in case of Stored Process Error 2021-12-12 20:37:19 +00:00
Allan Bowe
b645d1495b Merge pull request #586 from sasjs/handle-sasjs-server-response
fix: handle sasjs server response
2021-12-08 09:09:42 +00:00
3a4a4c3460 chore: sasjs path optional 2021-12-06 17:44:08 +01:00
Saad Jutt
182de51f9b chore: added comments 2021-12-05 19:27:07 +05:00
Saad Jutt
712d1549c7 feat(server): added sasjs server support 2021-12-05 18:31:36 +05:00
Allan Bowe
5a478c8936 Merge pull request #594 from sasjs/issue-588
fix: sas9JobExecutor issues
2021-12-03 11:13:15 +00:00
c9ecc1dde4 chore: requestClient naming 2021-12-03 11:54:52 +01:00
bdf9e2fd5b fix: sas9JobExecutor not returning response 2021-11-26 15:32:10 +01:00
Vladislav Parhomchik
0b795b26c0 Merge pull request #589 from sasjs/issue-587
Pinned typedoc version to 0.19.2
2021-11-26 12:47:21 +03:00
96aac0cfa2 fix: Sas9JobExecutor appendRequest 2021-11-24 20:26:02 +01:00
Yury Shkoda
a82e1f33e3 docs: update docs 2021-11-24 11:23:27 +03:00
Yury Shkoda
058c887cd3 chore(docs): pinned typedoc version 2021-11-24 11:19:41 +03:00
Allan Bowe
81d959c7c1 fix: parsing _webout from response regardless of debug status 2021-11-18 19:56:12 +00:00
7c5adeabb5 feat: parse response in webJobExecutor when server type is sasjs server 2021-11-19 00:35:02 +05:00
Yury Shkoda
cb88376bda Merge pull request #576 from sasjs/update-dependencies
chore(deps): update dependencies
2021-11-01 15:50:51 +03:00
Yury Shkoda
4c8ddeca25 chore(deps): regenerate package-lock with --legacy-peer-deps option 2021-11-01 15:26:31 +03:00
Yury Shkoda
d264a3f239 chore(ci/cd): used node lts/fermium for GH actions 2021-11-01 14:58:27 +03:00
Yury Shkoda
840b1aa1bf chore(deps): regenerate package-lock.json 2021-11-01 14:47:48 +03:00
Yury Shkoda
e26fd307c8 chore(git): merge branch 'master' into update-dependencies 2021-11-01 14:45:38 +03:00
Allan Bowe
f25c76fdfd fix: merge 2021-10-28 13:50:29 +00:00
Allan Bowe
bc1a7dc54f chore: lint fix 2021-10-27 09:53:42 +00:00
Allan Bowe
93c267fd4e Update AuthManager.ts 2021-10-27 12:20:40 +03:00
Vladislav Parhomchik
de5c38f0fb chore(deps): versions fix, authmanager default returns 2021-10-26 15:46:51 +03:00
Vladislav Parhomchik
208470e7d9 chore(deps): update dependencies 2021-10-26 13:06:03 +03:00
88 changed files with 21301 additions and 11874 deletions

View File

@@ -13,7 +13,7 @@ jobs:
strategy: strategy:
matrix: matrix:
node-version: [lts/*] node-version: [lts/fermium]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

455
docs/modules/api_viya.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

251
docs/modules/test.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

20509
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -43,7 +43,7 @@
"@types/axios": "^0.14.0", "@types/axios": "^0.14.0",
"@types/express": "^4.17.13", "@types/express": "^4.17.13",
"@types/form-data": "^2.5.0", "@types/form-data": "^2.5.0",
"@types/jest": "^27.0.1", "@types/jest": "^27.0.2",
"@types/mime": "^2.0.3", "@types/mime": "^2.0.3",
"@types/pem": "^1.9.6", "@types/pem": "^1.9.6",
"@types/tough-cookie": "^4.0.1", "@types/tough-cookie": "^4.0.1",
@@ -58,17 +58,17 @@
"pem": "^1.14.4", "pem": "^1.14.4",
"process": "^0.11.10", "process": "^0.11.10",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"semantic-release": "^17.4.7", "semantic-release": "^18.0.0",
"terser-webpack-plugin": "^5.2.4", "terser-webpack-plugin": "^5.2.4",
"ts-jest": "^27.0.3", "ts-jest": "^27.0.3",
"ts-loader": "^9.2.2", "ts-loader": "^9.2.6",
"tslint": "^6.1.3", "tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0", "tslint-config-prettier": "^1.18.0",
"typedoc": "^0.22.3", "typedoc": "0.19.2",
"typedoc-neo-theme": "^1.1.1", "typedoc-neo-theme": "^1.1.1",
"typedoc-plugin-external-module-name": "^4.0.6", "typedoc-plugin-external-module-name": "^4.0.6",
"typescript": "4.3.5", "typescript": "4.3.5",
"webpack": "^5.52.1", "webpack": "^5.56.0",
"webpack-cli": "^4.7.2" "webpack-cli": "^4.7.2"
}, },
"main": "index.js", "main": "index.js",

View File

@@ -33,6 +33,7 @@ import { LoginOptions, LoginResult } from './types/Login'
const defaultConfig: SASjsConfig = { const defaultConfig: SASjsConfig = {
serverUrl: '', serverUrl: '',
pathSASJS: '/SASjsApi/stp/execute',
pathSAS9: '/SASStoredProcess/do', pathSAS9: '/SASStoredProcess/do',
pathSASViya: '/SASJobExecution', pathSASViya: '/SASJobExecution',
appLoc: '/Public/seedapp', appLoc: '/Public/seedapp',
@@ -527,10 +528,11 @@ export default class SASjs {
/** /**
* Checks whether a session is active, or login is required. * Checks whether a session is active, or login is required.
* @param accessToken - an optional access token is required for SASjs server type.
* @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(accessToken?: string) {
return this.authManager!.checkSession() return this.authManager!.checkSession(accessToken)
} }
/** /**
@@ -563,9 +565,10 @@ export default class SASjs {
/** /**
* Logs out of the configured SAS server. * Logs out of the configured SAS server.
* @param accessToken - an optional access token is required for SASjs server type.
*/ */
public logOut() { public logOut(accessToken?: string) {
return this.authManager!.logOut() return this.authManager!.logOut(accessToken)
} }
/** /**
@@ -975,7 +978,9 @@ export default class SASjs {
this.jobsPath = this.jobsPath =
this.sasjsConfig.serverType === ServerType.SasViya this.sasjsConfig.serverType === ServerType.SasViya
? this.sasjsConfig.pathSASViya ? this.sasjsConfig.pathSASViya
: this.sasjsConfig.pathSAS9 : this.sasjsConfig.serverType === ServerType.Sas9
? this.sasjsConfig.pathSAS9
: this.sasjsConfig.pathSASJS || ''
this.authManager = new AuthManager( this.authManager = new AuthManager(
this.sasjsConfig.serverUrl, this.sasjsConfig.serverUrl,
@@ -1044,6 +1049,7 @@ export default class SASjs {
this.sasjsConfig.serverUrl, this.sasjsConfig.serverUrl,
this.sasjsConfig.serverType!, this.sasjsConfig.serverType!,
this.jobsPath, this.jobsPath,
this.requestClient,
this.sasjsConfig.httpsAgentOptions this.sasjsConfig.httpsAgentOptions
) )

View File

@@ -21,7 +21,9 @@ export class AuthManager {
this.logoutUrl = this.logoutUrl =
this.serverType === ServerType.Sas9 this.serverType === ServerType.Sas9
? '/SASLogon/logout?' ? '/SASLogon/logout?'
: '/SASLogon/logout.do?' : this.serverType === ServerType.SasViya
? '/SASLogon/logout.do?'
: '/SASjsApi/auth/logout'
} }
/** /**
@@ -180,20 +182,21 @@ export class AuthManager {
/** /**
* Checks whether a session is active, or login is required. * Checks whether a session is active, or login is required.
* @param accessToken - an optional access token is required for SASjs server type.
* @returns - a promise which resolves with an object containing three values * @returns - a promise which resolves with an object containing three values
* - a boolean `isLoggedIn` * - a boolean `isLoggedIn`
* - a string `userName` and * - a string `userName` and
* - a form `loginForm` if not loggedin. * - a form `loginForm` if not loggedin.
*/ */
public async checkSession(): Promise<{ public async checkSession(accessToken?: string): Promise<{
isLoggedIn: boolean isLoggedIn: boolean
userName: string userName: string
loginForm?: any loginForm?: any
}> { }> {
const { isLoggedIn, userName } = await this.fetchUserName() const { isLoggedIn, userName } = await this.fetchUserName(accessToken)
let loginForm = null let loginForm = null
if (!isLoggedIn) { if (!isLoggedIn && this.serverType !== ServerType.Sasjs) {
//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
//Residue can happen in case of session expiration //Residue can happen in case of session expiration
await this.logOut() await this.logOut()
@@ -218,19 +221,20 @@ export class AuthManager {
return await this.getLoginForm(formResponse) return await this.getLoginForm(formResponse)
} }
private async fetchUserName(): Promise<{ private async fetchUserName(accessToken?: string): Promise<{
isLoggedIn: boolean isLoggedIn: boolean
userName: string userName: string
}> { }> {
//For VIYA we will send request on API endpoint. Which is faster then pinging SASJobExecution.
//For SAS9 we will send request on SASStoredProcess
const url = const url =
this.serverType === ServerType.SasViya this.serverType === ServerType.SasViya
? `${this.serverUrl}/identities/users/@currentUser` ? `${this.serverUrl}/identities/users/@currentUser`
: `${this.serverUrl}/SASStoredProcess` : this.serverType === ServerType.Sas9
? `${this.serverUrl}/SASStoredProcess`
: `${this.serverUrl}/SASjsApi/session`
// Access token is required for server type `SASjs`
const { result: loginResponse } = await this.requestClient const { result: loginResponse } = await this.requestClient
.get<string>(url, undefined, 'text/plain') .get<string>(url, accessToken, 'text/plain')
.catch((err: any) => { .catch((err: any) => {
return { result: 'authErr' } return { result: 'authErr' }
}) })
@@ -256,7 +260,12 @@ export class AuthManager {
.split(' ') .split(' ')
.map((name: string) => name.slice(0, 3).toLowerCase()) .map((name: string) => name.slice(0, 3).toLowerCase())
.join('') .join('')
case ServerType.Sasjs:
return response?.username
default: default:
console.error('Server Type not found in extractUserName function')
return '' return ''
} }
} }
@@ -304,8 +313,12 @@ export class AuthManager {
/** /**
* Logs out of the configured SAS server. * Logs out of the configured SAS server.
* @param accessToken - an optional access token is required for SASjs server type.
*/ */
public logOut() { public logOut(accessToken?: string) {
if (this.serverType === ServerType.Sasjs) {
return this.requestClient.post(this.logoutUrl, undefined, accessToken)
}
this.requestClient.clearCsrfTokens() this.requestClient.clearCsrfTokens()
return this.requestClient.get(this.logoutUrl, undefined).then(() => true) return this.requestClient.get(this.logoutUrl, undefined).then(() => true)
} }

View File

@@ -5,6 +5,7 @@ import { ErrorResponse } from '../types/errors'
import { convertToCSV, isRelativePath } from '../utils' import { convertToCSV, isRelativePath } from '../utils'
import { BaseJobExecutor } from './JobExecutor' import { BaseJobExecutor } from './JobExecutor'
import { Sas9RequestClient } from '../request/Sas9RequestClient' import { Sas9RequestClient } from '../request/Sas9RequestClient'
import { RequestClient } from '../request/RequestClient'
/** /**
* Job executor for SAS9 servers for use in Node.js environments. * Job executor for SAS9 servers for use in Node.js environments.
@@ -13,15 +14,16 @@ import { Sas9RequestClient } from '../request/Sas9RequestClient'
* job execution requests. * job execution requests.
*/ */
export class Sas9JobExecutor extends BaseJobExecutor { export class Sas9JobExecutor extends BaseJobExecutor {
private requestClient: Sas9RequestClient private sas9RequestClient: Sas9RequestClient
constructor( constructor(
serverUrl: string, serverUrl: string,
serverType: ServerType, serverType: ServerType,
private jobsPath: string, private jobsPath: string,
private requestClient: RequestClient,
httpsAgentOptions?: https.AgentOptions httpsAgentOptions?: https.AgentOptions
) { ) {
super(serverUrl, serverType) super(serverUrl, serverType)
this.requestClient = new Sas9RequestClient(serverUrl, httpsAgentOptions) this.sas9RequestClient = new Sas9RequestClient(serverUrl, httpsAgentOptions)
} }
async execute(sasJob: string, data: any, config: any) { async execute(sasJob: string, data: any, config: any) {
@@ -37,6 +39,8 @@ export class Sas9JobExecutor extends BaseJobExecutor {
: '' : ''
}` }`
apiUrl = `${apiUrl}${config.debug ? '&_debug=131' : ''}`
let requestParams = { let requestParams = {
...this.getRequestParams(config) ...this.getRequestParams(config)
} }
@@ -49,6 +53,8 @@ export class Sas9JobExecutor extends BaseJobExecutor {
} catch (e: any) { } catch (e: any) {
return Promise.reject(new ErrorResponse(e?.message, e)) return Promise.reject(new ErrorResponse(e?.message, e))
} }
} else {
data = ''
} }
for (const key in requestParams) { for (const key in requestParams) {
@@ -57,16 +63,18 @@ export class Sas9JobExecutor extends BaseJobExecutor {
} }
} }
await this.requestClient.login( await this.sas9RequestClient.login(
config.username, config.username,
config.password, config.password,
this.jobsPath this.jobsPath
) )
const contentType = const contentType =
data && Object.keys(data).length data && Object.keys(data).length
? 'multipart/form-data; boundary=' + (formData as any)._boundary ? 'multipart/form-data; boundary=' + (formData as any)._boundary
: 'text/plain' : 'text/plain'
return await this.requestClient!.post(
return await this.sas9RequestClient!.post(
apiUrl, apiUrl,
formData, formData,
undefined, undefined,
@@ -76,6 +84,28 @@ export class Sas9JobExecutor extends BaseJobExecutor {
Connection: 'Keep-Alive' Connection: 'Keep-Alive'
} }
) )
.then((res: any) => {
let resString = res
if (typeof res === 'object') {
resString = JSON.stringify(res)
}
this.requestClient!.appendRequest(resString, sasJob, config.debug)
return res
})
.catch((err: any) => {
let errString = err
if (typeof err === 'object') {
errString = JSON.stringify(errString)
}
this.requestClient!.appendRequest(errString, sasJob, config.debug)
return err
})
} }
private getRequestParams(config: any): any { private getRequestParams(config: any): any {

View File

@@ -15,7 +15,8 @@ import { SASViyaApiClient } from '../SASViyaApiClient'
import { import {
isRelativePath, isRelativePath,
parseSasViyaDebugResponse, parseSasViyaDebugResponse,
appendExtraResponseAttributes appendExtraResponseAttributes,
getValidJson
} from '../utils' } from '../utils'
import { BaseJobExecutor } from './JobExecutor' import { BaseJobExecutor } from './JobExecutor'
import { parseWeboutResponse } from '../utils/parseWeboutResponse' import { parseWeboutResponse } from '../utils/parseWeboutResponse'
@@ -113,6 +114,7 @@ export class WebJobExecutor extends BaseJobExecutor {
const stringifiedData = JSON.stringify(data) const stringifiedData = JSON.stringify(data)
if ( if (
config.serverType === ServerType.Sas9 || config.serverType === ServerType.Sas9 ||
config.serverType === ServerType.Sasjs ||
stringifiedData.length > 500000 || stringifiedData.length > 500000 ||
stringifiedData.includes(';') stringifiedData.includes(';')
) { ) {
@@ -142,12 +144,25 @@ export class WebJobExecutor extends BaseJobExecutor {
} }
const requestPromise = new Promise((resolve, reject) => { const requestPromise = new Promise((resolve, reject) => {
this.requestClient!.post(apiUrl, formData, undefined) // Access token is required for server type `SASjs`
this.requestClient!.post(apiUrl, formData, authConfig?.access_token)
.then(async (res: any) => { .then(async (res: any) => {
this.requestClient!.appendRequest(res, sasJob, config.debug) const resObj =
this.serverType === ServerType.Sasjs
? {
result: res.result._webout,
log: res.result.log
}
: res
this.requestClient!.appendRequest(resObj, sasJob, config.debug)
let jsonResponse = res.result let jsonResponse = res.result
if (this.serverType === ServerType.Sasjs) {
const webout = parseWeboutResponse(res.result._webout, apiUrl)
jsonResponse = getValidJson(webout)
}
if (config.debug) { if (config.debug) {
switch (this.serverType) { switch (this.serverType) {
case ServerType.SasViya: case ServerType.SasViya:

View File

@@ -627,7 +627,7 @@ const parseError = (data: string) => {
if (parts.length > 1) { if (parts.length > 1) {
const log = parts[1].split('<pre>')[1].split('</pre>')[0] const log = parts[1].split('<pre>')[1].split('</pre>')[0]
const message = `This request completed with errors.` const message = `This request completed with errors.`
return new JobExecutionError(404, message, log) return new JobExecutionError(500, message, log)
} }
} }
} catch (_) {} } catch (_) {}

View File

@@ -12,6 +12,11 @@ export class SASjsConfig {
* streamed. * streamed.
*/ */
serverUrl: string = '' serverUrl: string = ''
/**
* The location of the STP Process Web Application. By default the adapter
* will use '/SASjsApi/stp/execute' on SAS JS.
*/
pathSASJS?: string = ''
/** /**
* The location of the Stored Process Web Application. By default the adapter * The location of the Stored Process Web Application. By default the adapter
* will use '/SASStoredProcess/do' on SAS 9. * will use '/SASStoredProcess/do' on SAS 9.

View File

@@ -4,7 +4,7 @@ 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): object => {
try { try {
if (str === null || str === undefined) throw new InvalidJsonError() if (str === null || str === undefined) throw new InvalidJsonError()

View File

@@ -1,6 +1,6 @@
import { WeboutResponseError } from '../types/errors' import { WeboutResponseError } from '../types/errors'
export const parseWeboutResponse = (response: string, url?: string) => { export const parseWeboutResponse = (response: string, url?: string): string => {
let sasResponse = '' let sasResponse = ''
if (response.includes('>>weboutBEGIN<<')) { if (response.includes('>>weboutBEGIN<<')) {