mirror of
https://github.com/sasjs/adapter.git
synced 2026-01-05 03:30:05 +00:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dad99557a7 | ||
| c7cc2e5fa4 | |||
|
|
2bd7544051 | ||
| 1fb972d88a | |||
| 64f8f8c893 | |||
|
|
ddb4a51c55 | ||
| 921d6ef364 | |||
|
|
105675d46a | ||
|
|
e4addba762 | ||
| 8203e918fd | |||
|
|
2210e43880 | ||
|
|
b04df0bc6d | ||
| 98e851b4d8 | |||
| 84306bea3d | |||
| 89d32262f8 | |||
| 257010f57d | |||
| eb9991015b | |||
|
|
9d17e87a09 | ||
| 55f309e998 | |||
| 3d9b40398c | |||
|
|
e0badae973 | ||
|
|
524c561390 | ||
| e7ceac1b78 | |||
|
|
72ddd424a5 | ||
| 85f771d1ed | |||
|
|
1a781c3a56 | ||
|
|
296d4efdfb | ||
| 8df09d01de | |||
|
|
2d4a9d6dee | ||
|
|
38c30f6342 | ||
|
|
dd72304bc7 | ||
|
|
296a543b2d | ||
| 70b31dcb8f | |||
| b0c2a81989 | |||
| 53e167b17d | |||
|
|
5159318d0d | ||
|
|
6842ee13e4 | ||
|
|
2ce0395a2e | ||
|
|
63440ddfd2 | ||
|
|
eb6729a9c7 | ||
|
|
9cd9dc83f3 | ||
|
|
7608887a0e |
12
.github/issue_template.md
vendored
Normal file
12
.github/issue_template.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
## Expected behaviour
|
||||||
|
*Describe what should be happening*
|
||||||
|
|
||||||
|
## Current behaviour
|
||||||
|
*Describe what is actually happening*
|
||||||
|
|
||||||
|
## Environment info
|
||||||
|
**Client tech stack**: *Angular, React, Vue, VanillaJS, NodeJS etc.*
|
||||||
|
**Server type**: SASJS|SASVIYA|SAS9
|
||||||
|
**Login mechanism**: Default|Redirected
|
||||||
|
**Debug**: true|false
|
||||||
|
**Use Compute Api (relevant only on VIYA)**: true|false
|
||||||
30
.github/vpn/config.ovpn
vendored
30
.github/vpn/config.ovpn
vendored
@@ -1,30 +0,0 @@
|
|||||||
cipher AES-256-CBC
|
|
||||||
setenv FORWARD_COMPATIBLE 1
|
|
||||||
client
|
|
||||||
server-poll-timeout 4
|
|
||||||
nobind
|
|
||||||
remote vpn.analytium.co.uk 1194 udp
|
|
||||||
remote vpn.analytium.co.uk 1194 udp
|
|
||||||
remote vpn.analytium.co.uk 443 tcp
|
|
||||||
remote vpn.analytium.co.uk 1194 udp
|
|
||||||
remote vpn.analytium.co.uk 1194 udp
|
|
||||||
remote vpn.analytium.co.uk 1194 udp
|
|
||||||
remote vpn.analytium.co.uk 1194 udp
|
|
||||||
remote vpn.analytium.co.uk 1194 udp
|
|
||||||
dev tun
|
|
||||||
dev-type tun
|
|
||||||
ns-cert-type server
|
|
||||||
setenv opt tls-version-min 1.0 or-highest
|
|
||||||
reneg-sec 604800
|
|
||||||
sndbuf 0
|
|
||||||
rcvbuf 0
|
|
||||||
# NOTE: LZO commands are pushed by the Access Server at connect time.
|
|
||||||
# NOTE: The below line doesn't disable LZO.
|
|
||||||
comp-lzo no
|
|
||||||
verb 3
|
|
||||||
setenv PUSH_PEER_INFO
|
|
||||||
|
|
||||||
ca ca.crt
|
|
||||||
cert user.crt
|
|
||||||
key user.key
|
|
||||||
tls-auth tls.key 1
|
|
||||||
25
.github/workflows/build.yml
vendored
25
.github/workflows/build.yml
vendored
@@ -45,31 +45,6 @@ jobs:
|
|||||||
key: ${{ secrets.DCGITLAB_KEY }}
|
key: ${{ secrets.DCGITLAB_KEY }}
|
||||||
known_hosts: 'placeholder'
|
known_hosts: 'placeholder'
|
||||||
|
|
||||||
- name: Write VPN Files
|
|
||||||
run: |
|
|
||||||
echo "$CA_CRT" > .github/vpn/ca.crt
|
|
||||||
echo "$USER_CRT" > .github/vpn/user.crt
|
|
||||||
echo "$USER_KEY" > .github/vpn/user.key
|
|
||||||
echo "$TLS_KEY" > .github/vpn/tls.key
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
CA_CRT: ${{ secrets.CA_CRT}}
|
|
||||||
USER_CRT: ${{ secrets.USER_CRT }}
|
|
||||||
USER_KEY: ${{ secrets.USER_KEY }}
|
|
||||||
TLS_KEY: ${{ secrets.TLS_KEY }}
|
|
||||||
|
|
||||||
- name: Install Open VPN
|
|
||||||
run: |
|
|
||||||
sudo apt install apt-transport-https
|
|
||||||
sudo wget https://swupdate.openvpn.net/repos/openvpn-repo-pkg-key.pub
|
|
||||||
sudo apt-key add openvpn-repo-pkg-key.pub
|
|
||||||
sudo wget -O /etc/apt/sources.list.d/openvpn3.list https://swupdate.openvpn.net/community/openvpn3/repos/openvpn3-focal.list
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install openvpn3=16~beta+focal
|
|
||||||
|
|
||||||
- name: Start Open VPN 3
|
|
||||||
run: openvpn3 session-start --config .github/vpn/config.ovpn
|
|
||||||
|
|
||||||
- name: Deploy sasjs-tests
|
- name: Deploy sasjs-tests
|
||||||
run: |
|
run: |
|
||||||
npm install -g replace-in-files-cli
|
npm install -g replace-in-files-cli
|
||||||
|
|||||||
22
.github/workflows/generateDocs.yml
vendored
22
.github/workflows/generateDocs.yml
vendored
@@ -26,17 +26,19 @@ jobs:
|
|||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Ensure docs folder exists
|
||||||
|
run: |
|
||||||
|
rm -rf docs || true # avoid error if docs folder does not exist
|
||||||
|
mkdir docs
|
||||||
|
|
||||||
- name: Generate Docs
|
- name: Generate Docs
|
||||||
run: npm run typedoc
|
run: npm run typedoc
|
||||||
|
|
||||||
- name: Create CNAME file in docs
|
- name: Push generated docs
|
||||||
run: |
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
touch CNAME
|
with:
|
||||||
echo adapter.sasjs.io >> CNAME
|
github_token: ${{ secrets.GH_TOKEN }}
|
||||||
|
publish_branch: gh-pages
|
||||||
|
publish_dir: ./docs
|
||||||
|
cname: adapter.sasjs.io
|
||||||
|
|
||||||
- name: Push generated docs to docs branch
|
|
||||||
uses: nicholasgriffintn/github-branch-deployment-action@0.0.1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
|
||||||
BRANCH: docs
|
|
||||||
MESSAGE: 'Docs: ({sha}) {msg}'
|
|
||||||
|
|||||||
4
.github/workflows/npmpublish.yml
vendored
4
.github/workflows/npmpublish.yml
vendored
@@ -34,10 +34,10 @@ jobs:
|
|||||||
run: npm run build
|
run: npm run build
|
||||||
|
|
||||||
- name: Semantic Release
|
- name: Semantic Release
|
||||||
uses: cycjimmy/semantic-release-action@v2
|
uses: cycjimmy/semantic-release-action@v3
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
|
||||||
- name: Send Matrix message
|
- name: Send Matrix message
|
||||||
run: curl -X POST --data-urlencode "payload={\"msgtype\":\"m.text\", \"body\":\"New version of @sasjs/adapter has been released! \n Please deploy and run `dctests` with new adapter to make sure everything is still in place.\"}" https://matrix.4gl.io/_matrix/client/r0/rooms/%21BDUPBPEGVvRLKLQUxY:4gl.io/send/m.room.message?access_token=${{ secrets.MATRIX_TOKEN }}
|
run: curl -XPOST -d "{\"msgtype\":\"m.text\", \"body\":\"New version of @sasjs/adapter has been released! \n Please deploy and run 'dctests' with new adapter to make sure everything is still in place.\"}" https://matrix.4gl.io/_matrix/client/r0/rooms/!jRebyiGmHZlpfDwYXN:4gl.io/send/m.room.message?access_token=${{ secrets.MATRIX_TOKEN }}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ SASjs is a open-source framework for building Web Apps on SAS® platforms. You c
|
|||||||
|
|
||||||
1 - `npm install @sasjs/adapter` - for use in a nodeJS project (recommended)
|
1 - `npm install @sasjs/adapter` - for use in a nodeJS project (recommended)
|
||||||
|
|
||||||
2 - [Download](https://cdn.jsdelivr.net/npm/@sasjs/adapter@3/index.min.js) and use a copy of the latest JS file
|
2 - [Download](https://cdn.jsdelivr.net/npm/@sasjs/adapter@4/index.min.js) and use a copy of the latest JS file
|
||||||
|
|
||||||
3 - Reference directly from the CDN - in which case click [here](https://www.jsdelivr.com/package/npm/@sasjs/adapter?tab=collection) and select "SRI" to get the script tag with the integrity hash.
|
3 - Reference directly from the CDN - in which case click [here](https://www.jsdelivr.com/package/npm/@sasjs/adapter?tab=collection) and select "SRI" to get the script tag with the integrity hash.
|
||||||
|
|
||||||
|
|||||||
3372
package-lock.json
generated
3372
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@@ -45,9 +45,7 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cypress/webpack-preprocessor": "5.9.1",
|
"@cypress/webpack-preprocessor": "5.9.1",
|
||||||
"@types/axios": "0.14.0",
|
|
||||||
"@types/express": "4.17.13",
|
"@types/express": "4.17.13",
|
||||||
"@types/form-data": "2.5.0",
|
|
||||||
"@types/jest": "27.4.0",
|
"@types/jest": "27.4.0",
|
||||||
"@types/mime": "2.0.3",
|
"@types/mime": "2.0.3",
|
||||||
"@types/pem": "1.9.6",
|
"@types/pem": "1.9.6",
|
||||||
@@ -71,16 +69,16 @@
|
|||||||
"ts-loader": "9.4.0",
|
"ts-loader": "9.4.0",
|
||||||
"tslint": "6.1.3",
|
"tslint": "6.1.3",
|
||||||
"tslint-config-prettier": "1.18.0",
|
"tslint-config-prettier": "1.18.0",
|
||||||
"typedoc": "0.23.15",
|
"typedoc": "0.23.24",
|
||||||
"typedoc-plugin-rename-defaults": "0.4.0",
|
"typedoc-plugin-rename-defaults": "0.6.4",
|
||||||
"typescript": "4.8.3",
|
"typescript": "4.8.3",
|
||||||
"webpack": "5.69.0",
|
"webpack": "5.69.0",
|
||||||
"webpack-cli": "4.9.2"
|
"webpack-cli": "4.9.2"
|
||||||
},
|
},
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/utils": "2.48.2",
|
"@sasjs/utils": "2.52.0",
|
||||||
"axios": "0.26.0",
|
"axios": "0.27.2",
|
||||||
"axios-cookiejar-support": "1.0.1",
|
"axios-cookiejar-support": "1.0.1",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.0",
|
||||||
"https": "1.0.0",
|
"https": "1.0.0",
|
||||||
|
|||||||
863
sasjs-tests/package-lock.json
generated
863
sasjs-tests/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/adapter": "file:../build/sasjs-adapter-5.0.0.tgz",
|
"@sasjs/adapter": "file:../build/sasjs-adapter-5.0.0.tgz",
|
||||||
"@sasjs/test-framework": "^1.5.6",
|
"@sasjs/test-framework": "1.5.7",
|
||||||
"@types/jest": "^26.0.20",
|
"@types/jest": "^26.0.20",
|
||||||
"@types/node": "^14.14.41",
|
"@types/node": "^14.14.41",
|
||||||
"@types/react": "^17.0.1",
|
"@types/react": "^17.0.1",
|
||||||
@@ -43,6 +43,6 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"node-sass": "^7.0.1"
|
"node-sass": "7.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ if npm run cy:run -- --spec "cypress/integration/sasjs.tests.ts" ; then
|
|||||||
echo "Cypress sasjs testing passed!"
|
echo "Cypress sasjs testing passed!"
|
||||||
else
|
else
|
||||||
echo '{"msgtype":"m.text", "body":"Automated sasjs-tests failed on the @sasjs/adapter PR: '$2'"}'
|
echo '{"msgtype":"m.text", "body":"Automated sasjs-tests failed on the @sasjs/adapter PR: '$2'"}'
|
||||||
curl -XPOST -d '{"msgtype":"m.text", "body":"Automated sasjs-tests failed on the @sasjs/adapter PR: '$2'"}' https://matrix.4gl.io/_matrix/client/r0/rooms/%21BDUPBPEGVvRLKLQUxY:4gl.io/send/m.room.message?access_token=$1
|
curl -XPOST -d '{"msgtype":"m.text", "body":"Automated sasjs-tests failed on the @sasjs/adapter PR: '$2'"}' https://matrix.4gl.io/_matrix/client/r0/rooms/%21jRebyiGmHZlpfDwYXN:4gl.io:4gl.io/send/m.room.message?access_token=$1
|
||||||
echo "Cypress sasjs testing failed!"
|
echo "Cypress sasjs testing failed!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -71,13 +71,12 @@ export const computeTests = (adapter: SASjs, appLoc: string): TestSuite => ({
|
|||||||
test: () => {
|
test: () => {
|
||||||
const fileLines = [`data;`, `do x=1 to 100;`, `output;`, `end;`, `run;`]
|
const fileLines = [`data;`, `do x=1 to 100;`, `output;`, `end;`, `run;`]
|
||||||
|
|
||||||
return adapter.executeScriptSASViya(
|
return adapter.executeScript({
|
||||||
'sasCode.sas',
|
fileName: 'sasCode.sas',
|
||||||
fileLines,
|
linesOfCode: fileLines,
|
||||||
'SAS Studio compute context',
|
contextName: 'SAS Studio compute context',
|
||||||
undefined,
|
debug: true
|
||||||
true
|
})
|
||||||
)
|
|
||||||
},
|
},
|
||||||
assertion: (res: any) => {
|
assertion: (res: any) => {
|
||||||
const expectedLogContent = `1 data;\\n2 do x=1 to 100;\\n3 output;\\n4 end;\\n5 run;\\n\\n`
|
const expectedLogContent = `1 data;\\n2 do x=1 to 100;\\n3 output;\\n4 end;\\n5 run;\\n\\n`
|
||||||
@@ -92,13 +91,12 @@ export const computeTests = (adapter: SASjs, appLoc: string): TestSuite => ({
|
|||||||
const fileLines = [`%abort;`]
|
const fileLines = [`%abort;`]
|
||||||
|
|
||||||
return adapter
|
return adapter
|
||||||
.executeScriptSASViya(
|
.executeScript({
|
||||||
'sasCode.sas',
|
fileName: 'sasCode.sas',
|
||||||
fileLines,
|
linesOfCode: fileLines,
|
||||||
'SAS Studio compute context',
|
contextName: 'SAS Studio compute context',
|
||||||
undefined,
|
debug: true
|
||||||
true
|
})
|
||||||
)
|
|
||||||
.catch((err: any) => err)
|
.catch((err: any) => err)
|
||||||
},
|
},
|
||||||
assertion: (res: any) => {
|
assertion: (res: any) => {
|
||||||
|
|||||||
160
src/SASjs.ts
160
src/SASjs.ts
@@ -17,7 +17,8 @@ import {
|
|||||||
AuthConfig,
|
AuthConfig,
|
||||||
ExtraResponseAttributes,
|
ExtraResponseAttributes,
|
||||||
SasAuthResponse,
|
SasAuthResponse,
|
||||||
ServicePackSASjs
|
ServicePackSASjs,
|
||||||
|
AuthConfigSas9
|
||||||
} from '@sasjs/utils/types'
|
} from '@sasjs/utils/types'
|
||||||
import { RequestClient } from './request/RequestClient'
|
import { RequestClient } from './request/RequestClient'
|
||||||
import { SasjsRequestClient } from './request/SasjsRequestClient'
|
import { SasjsRequestClient } from './request/SasjsRequestClient'
|
||||||
@@ -33,6 +34,16 @@ import {
|
|||||||
import { ErrorResponse } from './types/errors'
|
import { ErrorResponse } from './types/errors'
|
||||||
import { LoginOptions, LoginResult } from './types/Login'
|
import { LoginOptions, LoginResult } from './types/Login'
|
||||||
|
|
||||||
|
interface ExecuteScriptParams {
|
||||||
|
linesOfCode: string[]
|
||||||
|
fileName?: string
|
||||||
|
contextName?: string
|
||||||
|
runTime?: string
|
||||||
|
authConfig?: AuthConfig
|
||||||
|
authConfigSas9?: AuthConfigSas9
|
||||||
|
debug?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
const defaultConfig: SASjsConfig = {
|
const defaultConfig: SASjsConfig = {
|
||||||
serverUrl: '',
|
serverUrl: '',
|
||||||
pathSASJS: '/SASjsApi/stp/execute',
|
pathSASJS: '/SASjsApi/stp/execute',
|
||||||
@@ -79,74 +90,73 @@ export default class SASjs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes SAS code on a SAS 9 server. Requires a runner to be present in
|
* Executes code on a SAS server.
|
||||||
* the users home directory in metadata.
|
* @param linesOfCode - lines of code to run.
|
||||||
* @param linesOfCode - lines of sas code from the file to run.
|
* @param fileName - (required for server type sas viya) name of the file to run. It will be converted to path to the file being submitted for execution.
|
||||||
* @param username - a string representing the username.
|
* @param contextName - (required for server type sas viya) context name on which code will be run on the server.
|
||||||
* @param password - a string representing the password.
|
* @param runTime - (required for server type sasjs) a string to represent runTime for code execution.
|
||||||
*/
|
|
||||||
public async executeScriptSAS9(
|
|
||||||
linesOfCode: string[],
|
|
||||||
userName: string,
|
|
||||||
password: string
|
|
||||||
) {
|
|
||||||
this.isMethodSupported('executeScriptSAS9', [ServerType.Sas9])
|
|
||||||
|
|
||||||
return await this.sas9ApiClient?.executeScript(
|
|
||||||
linesOfCode,
|
|
||||||
userName,
|
|
||||||
password
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes SAS code on a SASJS server
|
|
||||||
* @param code - a string of code from the file to run.
|
|
||||||
* @param authConfig - (optional) a valid client, secret, refresh and access tokens that are authorised to execute scripts.
|
* @param authConfig - (optional) a valid client, secret, refresh and access tokens that are authorised to execute scripts.
|
||||||
*/
|
* @param authConfigSas9 - (required for server type sas9) a valid username and password that are authorised to execute scripts.
|
||||||
public async executeScriptSASjs(
|
|
||||||
code: string,
|
|
||||||
runTime?: string,
|
|
||||||
authConfig?: AuthConfig
|
|
||||||
) {
|
|
||||||
this.isMethodSupported('executeScriptSASJS', [ServerType.Sasjs])
|
|
||||||
|
|
||||||
return await this.sasJSApiClient?.executeScript(code, runTime, authConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes sas code in a SAS Viya compute session.
|
|
||||||
* @param fileName - name of the file to run. It will be converted to path to the file being submitted for execution.
|
|
||||||
* @param linesOfCode - lines of sas code from the file to run.
|
|
||||||
* @param contextName - context name on which code will be run on the server.
|
|
||||||
* @param authConfig - (optional) the access token, refresh token, client and secret for authorizing the request.
|
|
||||||
* @param debug - (optional) if true, global debug config will be overriden
|
* @param debug - (optional) if true, global debug config will be overriden
|
||||||
*/
|
*/
|
||||||
public async executeScriptSASViya(
|
public async executeScript({
|
||||||
fileName: string,
|
linesOfCode,
|
||||||
linesOfCode: string[],
|
fileName,
|
||||||
contextName: string,
|
contextName,
|
||||||
authConfig?: AuthConfig,
|
runTime,
|
||||||
debug?: boolean
|
authConfig,
|
||||||
) {
|
authConfigSas9,
|
||||||
this.isMethodSupported('executeScriptSASViya', [ServerType.SasViya])
|
debug
|
||||||
|
}: ExecuteScriptParams) {
|
||||||
|
this.isMethodSupported('executeScript', [
|
||||||
|
ServerType.Sas9,
|
||||||
|
ServerType.Sasjs,
|
||||||
|
ServerType.SasViya
|
||||||
|
])
|
||||||
|
|
||||||
contextName = contextName || this.sasjsConfig.contextName
|
if (this.sasjsConfig.serverType === ServerType.Sas9) {
|
||||||
|
if (!authConfigSas9)
|
||||||
|
throw new Error('Auth config for sas9 is not provided')
|
||||||
|
|
||||||
if (!contextName) {
|
return await this.sas9ApiClient?.executeScript(
|
||||||
throw new Error(
|
linesOfCode,
|
||||||
'Context name is undefined. Please set a `contextName` in your SASjs or override config.'
|
authConfigSas9.userName,
|
||||||
|
authConfigSas9.password
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.sasViyaApiClient!.executeScript(
|
if (this.sasjsConfig.serverType === ServerType.Sasjs) {
|
||||||
fileName,
|
return await this.sasJSApiClient?.executeScript(
|
||||||
linesOfCode,
|
linesOfCode.join('\n'),
|
||||||
contextName,
|
runTime,
|
||||||
authConfig,
|
authConfig
|
||||||
null,
|
)
|
||||||
debug ? debug : this.sasjsConfig.debug
|
}
|
||||||
)
|
|
||||||
|
if (this.sasjsConfig.serverType === ServerType.SasViya) {
|
||||||
|
contextName = contextName || this.sasjsConfig.contextName
|
||||||
|
|
||||||
|
if (!contextName) {
|
||||||
|
throw new Error(
|
||||||
|
'Context name is undefined. Please set a `contextName` in your SASjs or override config.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fileName) {
|
||||||
|
throw new Error(
|
||||||
|
'File name is required in case of SAS VIYA. Please provide a `fileName`.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.sasViyaApiClient!.executeScript(
|
||||||
|
fileName,
|
||||||
|
linesOfCode,
|
||||||
|
contextName,
|
||||||
|
authConfig,
|
||||||
|
null,
|
||||||
|
debug ? debug : this.sasjsConfig.debug
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -826,33 +836,6 @@ export default class SASjs {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the folders and services at the given location `appLoc` on the given server `serverUrl`.
|
|
||||||
* @param dataJson - the JSON specifying the folders and files to be created, can also includes
|
|
||||||
* appLoc, streamServiceName, streamWebFolder, streamLogo
|
|
||||||
* @param appLoc - (optional) the base folder in which to create the new folders and
|
|
||||||
* services. If not provided, is taken from SASjsConfig. Precedence will be of appLoc present in dataJson.
|
|
||||||
* @param authConfig - (optional) a valid client, secret, refresh and access tokens that are authorised to execute compute jobs.
|
|
||||||
*/
|
|
||||||
public async deployToSASjs(
|
|
||||||
dataJson: ServicePackSASjs,
|
|
||||||
appLoc?: string,
|
|
||||||
authConfig?: AuthConfig
|
|
||||||
) {
|
|
||||||
if (!appLoc) {
|
|
||||||
appLoc = this.sasjsConfig.appLoc
|
|
||||||
}
|
|
||||||
return await this.sasJSApiClient?.deploy(dataJson, appLoc, authConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
public async executeJobSASjs(query: ExecutionQuery, authConfig?: AuthConfig) {
|
|
||||||
return await this.sasJSApiClient?.executeJob(
|
|
||||||
query,
|
|
||||||
this.sasjsConfig.appLoc,
|
|
||||||
authConfig
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kicks off execution of the given job via the compute API.
|
* Kicks off execution of the given job via the compute API.
|
||||||
* @returns an object representing the compute session created for the given job.
|
* @returns an object representing the compute session created for the given job.
|
||||||
@@ -912,6 +895,7 @@ export default class SASjs {
|
|||||||
await this.computeJobExecutor?.resendWaitingRequests()
|
await this.computeJobExecutor?.resendWaitingRequests()
|
||||||
await this.jesJobExecutor?.resendWaitingRequests()
|
await this.jesJobExecutor?.resendWaitingRequests()
|
||||||
await this.fileUploader?.resendWaitingRequests()
|
await this.fileUploader?.resendWaitingRequests()
|
||||||
|
await this.sasjsJobExecutor?.resendWaitingRequests()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import * as NodeFormData from 'form-data'
|
||||||
import { AuthConfig, ServerType, ServicePackSASjs } from '@sasjs/utils/types'
|
import { AuthConfig, ServerType, ServicePackSASjs } from '@sasjs/utils/types'
|
||||||
import { ExecutionQuery } from './types'
|
import { ExecutionQuery } from './types'
|
||||||
import { RequestClient } from './request/RequestClient'
|
import { RequestClient } from './request/RequestClient'
|
||||||
@@ -8,20 +9,31 @@ import { getTokens } from './auth/getTokens'
|
|||||||
export class SASjsApiClient {
|
export class SASjsApiClient {
|
||||||
constructor(private requestClient: RequestClient) {}
|
constructor(private requestClient: RequestClient) {}
|
||||||
|
|
||||||
|
private async getAccessTokenForRequest(authConfig?: AuthConfig) {
|
||||||
|
if (authConfig) {
|
||||||
|
const { access_token } = await getTokens(
|
||||||
|
this.requestClient,
|
||||||
|
authConfig,
|
||||||
|
ServerType.Sasjs
|
||||||
|
)
|
||||||
|
|
||||||
|
return access_token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the folders and services at the given location `appLoc` on the given server `serverUrl`.
|
||||||
|
* @param dataJson - the JSON specifying the folders and files to be created, can also includes
|
||||||
|
* appLoc, streamServiceName, streamWebFolder, streamLogo
|
||||||
|
* @param appLoc - the base folder in which to create the new folders and services.
|
||||||
|
* @param authConfig - (optional) a valid client, secret, refresh and access tokens that are authorised to execute compute jobs.
|
||||||
|
*/
|
||||||
public async deploy(
|
public async deploy(
|
||||||
dataJson: ServicePackSASjs,
|
dataJson: ServicePackSASjs,
|
||||||
appLoc: string,
|
appLoc: string,
|
||||||
authConfig?: AuthConfig
|
authConfig?: AuthConfig
|
||||||
) {
|
) {
|
||||||
let access_token = (authConfig || {}).access_token
|
const access_token = await this.getAccessTokenForRequest(authConfig)
|
||||||
if (authConfig) {
|
|
||||||
;({ access_token } = await getTokens(
|
|
||||||
this.requestClient,
|
|
||||||
authConfig,
|
|
||||||
ServerType.Sasjs
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
dataJson.appLoc = dataJson.appLoc || appLoc
|
dataJson.appLoc = dataJson.appLoc || appLoc
|
||||||
|
|
||||||
const { result } = await this.requestClient.post<{
|
const { result } = await this.requestClient.post<{
|
||||||
@@ -41,6 +53,40 @@ export class SASjsApiClient {
|
|||||||
return Promise.resolve(result)
|
return Promise.resolve(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates/updates files within SASjs drive using uploaded json compressed file.
|
||||||
|
* @param zipFilePath - Compressed file path; file should only contain one JSON file and
|
||||||
|
* should have same name as of compressed file e.g. deploy.JSON should be compressed to deploy.JSON.zip
|
||||||
|
* Any other file or JSON file in zipped will be ignored!
|
||||||
|
* @param authConfig - (optional) a valid client, secret, refresh and access tokens that are authorised to execute compute jobs.
|
||||||
|
*/
|
||||||
|
public async deployZipFile(zipFilePath: string, authConfig?: AuthConfig) {
|
||||||
|
const { createReadStream } = require('@sasjs/utils/file')
|
||||||
|
const access_token = await this.getAccessTokenForRequest(authConfig)
|
||||||
|
|
||||||
|
const file = await createReadStream(zipFilePath)
|
||||||
|
const formData = new NodeFormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
|
||||||
|
const contentType = `multipart/form-data; boundary=${formData.getBoundary()}`
|
||||||
|
|
||||||
|
const { result } = await this.requestClient.post<{
|
||||||
|
status: string
|
||||||
|
message: string
|
||||||
|
streamServiceName?: string
|
||||||
|
example?: {}
|
||||||
|
}>(
|
||||||
|
'SASjsApi/drive/deploy/upload',
|
||||||
|
formData,
|
||||||
|
access_token,
|
||||||
|
contentType,
|
||||||
|
{},
|
||||||
|
{ maxContentLength: Infinity, maxBodyLength: Infinity }
|
||||||
|
)
|
||||||
|
|
||||||
|
return Promise.resolve(result)
|
||||||
|
}
|
||||||
|
|
||||||
public async executeJob(
|
public async executeJob(
|
||||||
query: ExecutionQuery,
|
query: ExecutionQuery,
|
||||||
appLoc: string,
|
appLoc: string,
|
||||||
@@ -73,14 +119,7 @@ export class SASjsApiClient {
|
|||||||
runTime: string = 'sas',
|
runTime: string = 'sas',
|
||||||
authConfig?: AuthConfig
|
authConfig?: AuthConfig
|
||||||
) {
|
) {
|
||||||
let access_token = (authConfig || {}).access_token
|
const access_token = await this.getAccessTokenForRequest(authConfig)
|
||||||
if (authConfig) {
|
|
||||||
;({ access_token } = await getTokens(
|
|
||||||
this.requestClient,
|
|
||||||
authConfig,
|
|
||||||
ServerType.Sasjs
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
let parsedSasjsServerLog = ''
|
let parsedSasjsServerLog = ''
|
||||||
|
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ export class AuthManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { result: formResponse } = await this.requestClient.get<string>(
|
const { result: formResponse } = await this.requestClient.get<string>(
|
||||||
this.loginUrl.replace('.do', ''),
|
this.loginUrl.replace('/SASLogon/login.do', '/SASLogon/login'),
|
||||||
undefined,
|
undefined,
|
||||||
'text/plain'
|
'text/plain'
|
||||||
)
|
)
|
||||||
@@ -348,7 +348,7 @@ export class AuthManager {
|
|||||||
this.loginUrl =
|
this.loginUrl =
|
||||||
this.serverType === ServerType.SasViya
|
this.serverType === ServerType.SasViya
|
||||||
? tempLoginLink
|
? tempLoginLink
|
||||||
: loginUrl.replace('.do', '')
|
: loginUrl.replace('/SASLogon/login.do', '/SASLogon/login')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,4 +3,6 @@ export * from './types'
|
|||||||
export * from './types/errors'
|
export * from './types/errors'
|
||||||
export * from './SASViyaApiClient'
|
export * from './SASViyaApiClient'
|
||||||
export * from './SAS9ApiClient'
|
export * from './SAS9ApiClient'
|
||||||
|
export * from './SASjsApiClient'
|
||||||
|
export * from './request/SasjsRequestClient'
|
||||||
export default SASjs
|
export default SASjs
|
||||||
|
|||||||
@@ -691,7 +691,9 @@ const parseError = (data: string) => {
|
|||||||
const parts = data.split(/stored process not found: /i)
|
const parts = data.split(/stored process not found: /i)
|
||||||
if (parts.length > 1) {
|
if (parts.length > 1) {
|
||||||
const storedProcessPath = parts[1].split('<i>')[1].split('</i>')[0]
|
const storedProcessPath = parts[1].split('<i>')[1].split('</i>')[0]
|
||||||
const message = `Stored process not found: ${storedProcessPath}`
|
const message = storedProcessPath.endsWith('runner')
|
||||||
|
? `SASJS runner not found. Here's the link (https://cli.sasjs.io/auth/#sasjs-runner) to the SAS code for registering the SASjs runner`
|
||||||
|
: `Stored process not found: ${storedProcessPath}`
|
||||||
return new JobExecutionError(500, message, '')
|
return new JobExecutionError(500, message, '')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import axiosCookieJarSupport from 'axios-cookiejar-support'
|
|||||||
import * as tough from 'tough-cookie'
|
import * as tough from 'tough-cookie'
|
||||||
import { prefixMessage } from '@sasjs/utils/error'
|
import { prefixMessage } from '@sasjs/utils/error'
|
||||||
import { RequestClient, throwIfError } from './RequestClient'
|
import { RequestClient, throwIfError } from './RequestClient'
|
||||||
|
import { JobExecutionError } from '../types/errors'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specific request client for SAS9 in Node.js environments.
|
* Specific request client for SAS9 in Node.js environments.
|
||||||
@@ -69,6 +70,8 @@ export class Sas9RequestClient extends RequestClient {
|
|||||||
return this.parseResponse<T>(response)
|
return this.parseResponse<T>(response)
|
||||||
})
|
})
|
||||||
.catch(async (e: any) => {
|
.catch(async (e: any) => {
|
||||||
|
if (e instanceof JobExecutionError) throw e
|
||||||
|
|
||||||
return await this.handleError(
|
return await this.handleError(
|
||||||
e,
|
e,
|
||||||
() =>
|
() =>
|
||||||
|
|||||||
Reference in New Issue
Block a user