mirror of
https://github.com/sasjs/adapter.git
synced 2026-01-08 04:50:06 +00:00
Compare commits
2 Commits
v3.10.1
...
snyk-upgra
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59195e6379 | ||
|
|
3a820c56a9 |
@@ -1,12 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Using `--silent` helps for showing any errs in the first line of the response
|
|
||||||
# The first line is picked up by the VS Code GIT UI popup when rc is not 0
|
|
||||||
|
|
||||||
if npm run --silent lint:silent ; then
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
npm run --silent lint:fix
|
|
||||||
echo "❌ Prettier check failed! We ran lint:fix for you. Please add & commit again."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
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
|
|
||||||
67
.github/workflows/build.yml
vendored
67
.github/workflows/build.yml
vendored
@@ -4,6 +4,7 @@
|
|||||||
name: SASjs Build
|
name: SASjs Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
push:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -21,77 +22,19 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: npm
|
cache: npm
|
||||||
|
|
||||||
- name: Check npm audit
|
- name: Check npm audit
|
||||||
run: npm audit --production --audit-level=low
|
run: npm audit --production --audit-level=low
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Check code style
|
- name: Check code style
|
||||||
run: npm run lint
|
run: npm run lint
|
||||||
|
|
||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
run: npm test
|
run: npm test
|
||||||
|
|
||||||
- name: Build Package
|
|
||||||
run: npm run package:lib
|
|
||||||
env:
|
|
||||||
CI: true
|
|
||||||
|
|
||||||
- name: Install SSH Key
|
|
||||||
uses: shimataro/ssh-key-action@v2
|
|
||||||
with:
|
|
||||||
key: ${{ secrets.DCGITLAB_KEY }}
|
|
||||||
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
|
|
||||||
run: |
|
|
||||||
npm install -g replace-in-files-cli
|
|
||||||
cd sasjs-tests
|
|
||||||
replace-in-files --regex='"@sasjs/adapter".*' --replacement='"@sasjs/adapter":"latest",' ./package.json
|
|
||||||
npm i
|
|
||||||
replace-in-files --regex='"serverUrl".*' --replacement='"serverUrl":"${{ secrets.SASJS_SERVER_URL }}",' ./public/config.json
|
|
||||||
replace-in-files --regex='"userName".*' --replacement='"userName":"${{ secrets.SASJS_USERNAME }}",' ./public/config.json
|
|
||||||
replace-in-files --regex='"password".*' --replacement='"password":"${{ secrets.SASJS_PASSWORD }}",' ./public/config.json
|
|
||||||
replace-in-files --regex='"serverType".*' --replacement='"serverType":"SASJS",' ./public/config.json
|
|
||||||
npm run update:adapter && npm run build
|
|
||||||
scp -o stricthostkeychecking=no -r ./build/* ${{ secrets.DCGITLAB_DEPLOY_PATH_VIYA }}
|
|
||||||
|
|
||||||
- name: Run cypress on sasjs
|
|
||||||
run: |
|
|
||||||
replace-in-files --regex='"sasjsTestsUrl".*' --replacement='"sasjsTestsUrl":"${{ secrets.SASJS_TEST_URL_VIYA }}",' ./cypress.json
|
|
||||||
replace-in-files --regex='"username".*' --replacement='"username":"${{ secrets.SASJS_USERNAME }}",' ./cypress.json
|
|
||||||
replace-in-files --regex='"password".*' --replacement='"password":"${{ secrets.SASJS_PASSWORD }}",' ./cypress.json
|
|
||||||
sh ./sasjs-cypress-run.sh ${{ secrets.DISCORD_WEBHOOK }} https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}
|
|
||||||
|
|
||||||
# For some reason if coverage report action is run before other commands, those commands can't access the directories and files on which they depend on
|
|
||||||
- name: Generate coverage report
|
- name: Generate coverage report
|
||||||
uses: artiomtr/jest-coverage-report-action@v2.0-rc.2
|
uses: artiomtr/jest-coverage-report-action@v2.0-rc.2
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Build Package
|
||||||
|
run: npm run package:lib
|
||||||
|
env:
|
||||||
|
CI: true
|
||||||
|
|||||||
4
.github/workflows/npmpublish.yml
vendored
4
.github/workflows/npmpublish.yml
vendored
@@ -14,16 +14,12 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Check code style
|
- name: Check code style
|
||||||
run: npm run lint
|
run: npm run lint
|
||||||
|
|
||||||
- name: Build Project
|
- name: Build Project
|
||||||
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@v2
|
||||||
env:
|
env:
|
||||||
|
|||||||
10
.npmignore
10
.npmignore
@@ -4,13 +4,3 @@ docs/
|
|||||||
*.md
|
*.md
|
||||||
*.spec.ts
|
*.spec.ts
|
||||||
.all-contributorsrc
|
.all-contributorsrc
|
||||||
cypress/
|
|
||||||
.gitpod.yml
|
|
||||||
.prettierrc
|
|
||||||
cypress.json
|
|
||||||
jest.config.js
|
|
||||||
sasjs-cypress-run.sh
|
|
||||||
tsconfig.json
|
|
||||||
tslint.json
|
|
||||||
typedoc.json
|
|
||||||
webpack.config.js
|
|
||||||
|
|||||||
@@ -16,5 +16,5 @@ No PR (that involves a non-trivial code change) should be merged, unless all ite
|
|||||||
|
|
||||||
|
|
||||||
- [ ] All `sasjs-cli` unit tests are passing (`npm test`).
|
- [ ] All `sasjs-cli` unit tests are passing (`npm test`).
|
||||||
- (CI Runs this) All `sasjs-tests` are passing. If you want to run it manually (instructions available [here](https://github.com/sasjs/adapter/blob/master/sasjs-tests/README.md)).
|
- [ ] All `sasjs-tests` are passing (instructions available [here](https://github.com/sasjs/adapter/blob/master/sasjs-tests/README.md)).
|
||||||
- [ ] [Data Controller](https://datacontroller.io) builds and is functional on both SAS 9 and Viya
|
- [ ] [Data Controller](https://datacontroller.io) builds and is functional on both SAS 9 and Viya
|
||||||
|
|||||||
11
cypress.json
11
cypress.json
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"chromeWebSecurity": false,
|
|
||||||
"defaultCommandTimeout": 20000,
|
|
||||||
"env": {
|
|
||||||
"sasjsTestsUrl": "",
|
|
||||||
"username": "",
|
|
||||||
"password": "",
|
|
||||||
"screenshotOnRunFailure": false,
|
|
||||||
"testingFinishTimeout": 600000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
const sasjsTestsUrl = Cypress.env('sasjsTestsUrl')
|
|
||||||
const username = Cypress.env('username')
|
|
||||||
const password = Cypress.env('password')
|
|
||||||
const testingFinishTimeout = Cypress.env('testingFinishTimeout')
|
|
||||||
|
|
||||||
context('sasjs-tests', function () {
|
|
||||||
this.beforeAll(() => {
|
|
||||||
cy.visit(sasjsTestsUrl)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.beforeEach(() => {
|
|
||||||
cy.reload()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('Should have all tests successfull', (done) => {
|
|
||||||
cy.get('body').then(($body) => {
|
|
||||||
if ($body.find('input[placeholder="User Name"]').length > 0) {
|
|
||||||
cy.get('input[placeholder="User Name"]').type(username)
|
|
||||||
cy.get('input[placeholder="Password"]').type(password)
|
|
||||||
cy.get('.submit-button').click()
|
|
||||||
}
|
|
||||||
|
|
||||||
cy.get('input[placeholder="User Name"]', { timeout: 40000 })
|
|
||||||
.should('not.exist')
|
|
||||||
.then(() => {
|
|
||||||
cy.get('.ui.massive.icon.primary.left.labeled.button')
|
|
||||||
.click()
|
|
||||||
.then(() => {
|
|
||||||
cy.get('.ui.massive.loading.primary.button', {
|
|
||||||
timeout: testingFinishTimeout
|
|
||||||
})
|
|
||||||
.should('not.exist')
|
|
||||||
.then(() => {
|
|
||||||
cy.get('span.icon.failed')
|
|
||||||
.should('not.exist')
|
|
||||||
.then(() => {
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('Should have all tests successfull with debug on', (done) => {
|
|
||||||
cy.get('body').then(($body) => {
|
|
||||||
if ($body.find('input[placeholder="User Name"]').length > 0) {
|
|
||||||
cy.get('input[placeholder="User Name"]').type(username)
|
|
||||||
cy.get('input[placeholder="Password"]').type(password)
|
|
||||||
cy.get('.submit-button').click()
|
|
||||||
}
|
|
||||||
|
|
||||||
cy.get('.ui.fitted.toggle.checkbox label')
|
|
||||||
.click()
|
|
||||||
.then(() => {
|
|
||||||
cy.get('input[placeholder="User Name"]', { timeout: 40000 })
|
|
||||||
.should('not.exist')
|
|
||||||
.then(() => {
|
|
||||||
cy.get('.ui.massive.icon.primary.left.labeled.button')
|
|
||||||
.click()
|
|
||||||
.then(() => {
|
|
||||||
cy.get('.ui.massive.loading.primary.button', {
|
|
||||||
timeout: testingFinishTimeout
|
|
||||||
})
|
|
||||||
.should('not.exist')
|
|
||||||
.then(() => {
|
|
||||||
cy.get('span.icon.failed')
|
|
||||||
.should('not.exist')
|
|
||||||
.then(() => {
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
const wp = require('@cypress/webpack-preprocessor')
|
|
||||||
|
|
||||||
const webpackOptions = {
|
|
||||||
resolve: {
|
|
||||||
extensions: ['.ts', '.js']
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.ts$/,
|
|
||||||
loaders: ['ts-loader'],
|
|
||||||
exclude: [/node_modules/]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.(html|css)$/,
|
|
||||||
loader: 'raw-loader',
|
|
||||||
exclude: /\.async\.(html|css)$/
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.async\.(html|css)$/,
|
|
||||||
loaders: ['file?name=[name].[hash].[ext]', 'extract']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
webpackOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = wp(options)
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
/// <reference types="cypress" />
|
|
||||||
// ***********************************************************
|
|
||||||
// This example plugins/index.js can be used to load plugins
|
|
||||||
//
|
|
||||||
// You can change the location of this file or turn off loading
|
|
||||||
// the plugins file with the 'pluginsFile' configuration option.
|
|
||||||
//
|
|
||||||
// You can read more here:
|
|
||||||
// https://on.cypress.io/plugins-guide
|
|
||||||
// ***********************************************************
|
|
||||||
|
|
||||||
// This function is called when a project is opened or re-opened (e.g. due to
|
|
||||||
// the project's config changing)
|
|
||||||
|
|
||||||
const wp = require('@cypress/webpack-preprocessor')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Cypress.PluginConfig}
|
|
||||||
*/
|
|
||||||
module.exports = (on, config) => {
|
|
||||||
// `on` is used to hook into various events Cypress emits
|
|
||||||
// `config` is the resolved Cypress config
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
webpackOptions: require('../webpack.config.js')
|
|
||||||
}
|
|
||||||
on('file:preprocessor', wp(options))
|
|
||||||
|
|
||||||
on('before:browser:launch', (browser = {}, launchOptions) => {
|
|
||||||
if (browser.name === 'chrome') {
|
|
||||||
launchOptions.args.push('--disable-site-isolation-trials')
|
|
||||||
launchOptions.args.push('--auto-open-devtools-for-tabs')
|
|
||||||
launchOptions.args.push('--aggressive-cache-discard')
|
|
||||||
launchOptions.args.push('--disable-cache')
|
|
||||||
launchOptions.args.push('--disable-application-cache')
|
|
||||||
launchOptions.args.push('--disable-offline-load-stale-cache')
|
|
||||||
launchOptions.args.push('--disk-cache-size=0')
|
|
||||||
|
|
||||||
return launchOptions
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"strict": true,
|
|
||||||
"baseUrl": "../node_modules",
|
|
||||||
"target": "es6",
|
|
||||||
"lib": ["es2019", "dom"],
|
|
||||||
"types": ["cypress"]
|
|
||||||
},
|
|
||||||
"include": ["**/*.ts"]
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
mode: 'development',
|
|
||||||
resolve: {
|
|
||||||
extensions: ['.ts', '.js']
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.ts$/,
|
|
||||||
exclude: [/node_modules/],
|
|
||||||
use: [
|
|
||||||
{
|
|
||||||
loader: 'ts-loader',
|
|
||||||
options: {
|
|
||||||
// skip typechecking for speed
|
|
||||||
transpileOnly: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
9540
package-lock.json
generated
9540
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@@ -8,17 +8,14 @@
|
|||||||
"build": "rimraf build && rimraf node && mkdir node && copyfiles -u 1 \"./src/**/*\" ./node && webpack && rimraf build/src && rimraf node",
|
"build": "rimraf build && rimraf node && mkdir node && copyfiles -u 1 \"./src/**/*\" ./node && webpack && rimraf build/src && rimraf node",
|
||||||
"package:lib": "npm run build && copyfiles ./package.json ./checkNodeVersion.js build && cd build && npm version \"5.0.0\" && npm pack",
|
"package:lib": "npm run build && copyfiles ./package.json ./checkNodeVersion.js build && cd build && npm version \"5.0.0\" && npm pack",
|
||||||
"publish:lib": "npm run build && cd build && npm publish",
|
"publish:lib": "npm run build && cd build && npm publish",
|
||||||
"lint:fix": "npx prettier --loglevel silent --write \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\" && npx prettier --loglevel silent --write \"sasjs-tests/src/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\" && npx prettier --loglevel silent --write \"cypress/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\"",
|
"lint:fix": "npx prettier --write \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\" && npx prettier --write \"sasjs-tests/src/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\"",
|
||||||
"lint": "npx prettier --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\" && npx prettier --check \"sasjs-tests/src/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\" && npx prettier --check \"cypress/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\"",
|
"lint": "npx prettier --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\" && npx prettier --check \"sasjs-tests/src/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\"",
|
||||||
"lint:silent": "npx prettier --loglevel silent --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\" && npx prettier --loglevel silent --check \"sasjs-tests/src/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\" && npx prettier --loglevel silent --check \"cypress/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\"",
|
|
||||||
"test": "jest --silent --coverage",
|
"test": "jest --silent --coverage",
|
||||||
"prepublishOnly": "cp -r ./build/* . && rm -rf ./build",
|
"prepublishOnly": "cp -r ./build/* . && rm -rf ./build",
|
||||||
"postpublish": "git clean -fd",
|
"postpublish": "git clean -fd",
|
||||||
"semantic-release": "semantic-release",
|
"semantic-release": "semantic-release",
|
||||||
"typedoc": "node createTSDocs",
|
"typedoc": "node createTSDocs",
|
||||||
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks && git config core.autocrlf false || true",
|
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks && git config core.autocrlf false || true"
|
||||||
"cypress": "cypress open",
|
|
||||||
"cy:run": "cypress run"
|
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
@@ -43,13 +40,9 @@
|
|||||||
},
|
},
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cypress/webpack-preprocessor": "5.9.1",
|
|
||||||
"@types/form-data": "2.5.0",
|
|
||||||
"cypress": "7.7.0",
|
|
||||||
"typedoc-neo-theme": "1.1.1",
|
|
||||||
"typedoc-plugin-external-module-name": "4.0.6",
|
|
||||||
"@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/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",
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
if npm run cy:run -- --spec "cypress/integration/sasjs.tests.ts" ; then
|
|
||||||
echo "Cypress sasjs testing passed!"
|
|
||||||
else
|
|
||||||
curl -X POST --header "Content-Type:application/json" --data '{"username":"GitHub CI - Adapter SASJS-TESTS (FAIL)", "content":"Automated sasjs-tests failed on the @sasjs/adapter PR on following link.\n'$2'", "avatar_url":"https://i.ibb.co/Lpk7Xvq/error-outline.png"}' $1
|
|
||||||
|
|
||||||
echo "Cypress sasjs testing failed!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
@@ -60,7 +60,7 @@ If you'd like to deploy just `sasjs-tests` without changing the adapter version,
|
|||||||
|
|
||||||
The below services need to be created on your SAS server, at the location specified as the `appLoc` in the SASjs configuration.
|
The below services need to be created on your SAS server, at the location specified as the `appLoc` in the SASjs configuration.
|
||||||
|
|
||||||
The code below will work on ALL SAS platforms (Viya, SAS 9 EBI, SASjs Server).
|
### SAS 9
|
||||||
|
|
||||||
```sas
|
```sas
|
||||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
@@ -70,32 +70,20 @@ parmcards4;
|
|||||||
%webout(FETCH)
|
%webout(FETCH)
|
||||||
%webout(OPEN)
|
%webout(OPEN)
|
||||||
%macro x();
|
%macro x();
|
||||||
%if %symexist(sasjs_tables) %then %do i=1 %to %sysfunc(countw(&sasjs_tables));
|
%do i=1 %to &_webin_file_count; %webout(OBJ,&&_webin_name&i,missing=STRING,showmeta=YES) %end;
|
||||||
%let table=%scan(&sasjs_tables,&i);
|
|
||||||
%webout(OBJ,&table,missing=STRING,showmeta=YES)
|
|
||||||
%end;
|
|
||||||
%else %do i=1 %to &_webin_file_count;
|
|
||||||
%webout(OBJ,&&_webin_name&i,missing=STRING,showmeta=YES)
|
|
||||||
%end;
|
|
||||||
%mend; %x()
|
%mend; %x()
|
||||||
%webout(CLOSE)
|
%webout(CLOSE)
|
||||||
;;;;
|
;;;;
|
||||||
%mx_createwebservice(path=/Public/app/common,name=sendObj)
|
%mm_createwebservice(path=/Public/app/common,name=sendObj)
|
||||||
parmcards4;
|
parmcards4;
|
||||||
%webout(FETCH)
|
%webout(FETCH)
|
||||||
%webout(OPEN)
|
%webout(OPEN)
|
||||||
%macro x();
|
%macro x();
|
||||||
%if %symexist(sasjs_tables) %then %do i=1 %to %sysfunc(countw(&sasjs_tables));
|
%do i=1 %to &_webin_file_count; %webout(ARR,&&_webin_name&i,missing=STRING,showmeta=YES) %end;
|
||||||
%let table=%scan(&sasjs_tables,&i);
|
|
||||||
%webout(ARR,&table,missing=STRING,showmeta=YES)
|
|
||||||
%end;
|
|
||||||
%else %do i=1 %to &_webin_file_count;
|
|
||||||
%webout(ARR,&&_webin_name&i,missing=STRING,showmeta=YES)
|
|
||||||
%end;
|
|
||||||
%mend; %x()
|
%mend; %x()
|
||||||
%webout(CLOSE)
|
%webout(CLOSE)
|
||||||
;;;;
|
;;;;
|
||||||
%mx_createwebservice(path=/Public/app/common,name=sendArr)
|
%mm_createwebservice(path=/Public/app/common,name=sendArr)
|
||||||
parmcards4;
|
parmcards4;
|
||||||
data work.macvars;
|
data work.macvars;
|
||||||
set sashelp.vmacro;
|
set sashelp.vmacro;
|
||||||
@@ -104,14 +92,11 @@ parmcards4;
|
|||||||
%webout(OBJ,macvars)
|
%webout(OBJ,macvars)
|
||||||
%webout(CLOSE)
|
%webout(CLOSE)
|
||||||
;;;;
|
;;;;
|
||||||
%mx_createwebservice(path=/Public/app/common,name=sendMacVars)
|
%mm_createwebservice(path=/Public/app/common,name=sendMacVars)
|
||||||
parmcards4;
|
parmcards4;
|
||||||
If you can keep your head when all about you
|
let he who hath understanding, reckon the number of the beast
|
||||||
Are losing theirs and blaming it on you,
|
|
||||||
If you can trust yourself when all men doubt you,
|
|
||||||
But make allowance for their doubting too;
|
|
||||||
;;;;
|
;;;;
|
||||||
%mx_createwebservice(path=/Public/app/common,name=makeErr)
|
%mm_createwebservice(path=/Public/app/common,name=makeErr)
|
||||||
parmcards4;
|
parmcards4;
|
||||||
%webout(OPEN)
|
%webout(OPEN)
|
||||||
data _null_;
|
data _null_;
|
||||||
@@ -120,7 +105,66 @@ data _null_;
|
|||||||
run;
|
run;
|
||||||
%webout(CLOSE)
|
%webout(CLOSE)
|
||||||
;;;;
|
;;;;
|
||||||
%mx_createwebservice(path=/Public/app/common,name=invalidJSON)
|
%mm_createwebservice(path=/Public/app/common,name=invalidJSON)
|
||||||
|
```
|
||||||
|
|
||||||
|
### SAS Viya
|
||||||
|
|
||||||
|
```sas
|
||||||
|
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
|
%inc mc;
|
||||||
|
filename ft15f001 temp lrecl=1000;
|
||||||
|
parmcards4;
|
||||||
|
%webout(FETCH)
|
||||||
|
%webout(OPEN)
|
||||||
|
%macro x();
|
||||||
|
%do i=1 %to %sysfunc(countw(&sasjs_tables));
|
||||||
|
%let table=%scan(&sasjs_tables,&i);
|
||||||
|
%webout(OBJ,&table,missing=STRING,showmeta=YES)
|
||||||
|
%end;
|
||||||
|
%mend;
|
||||||
|
%x()
|
||||||
|
%webout(CLOSE)
|
||||||
|
;;;;
|
||||||
|
%mp_createwebservice(path=/Public/app/common,name=sendObj)
|
||||||
|
parmcards4;
|
||||||
|
%webout(FETCH)
|
||||||
|
%webout(OPEN)
|
||||||
|
%macro x();
|
||||||
|
%do i=1 %to %sysfunc(countw(&sasjs_tables));
|
||||||
|
%let table=%scan(&sasjs_tables,&i);
|
||||||
|
%webout(ARR,&table,missing=STRING,showmeta=YES)
|
||||||
|
%end;
|
||||||
|
%mend;
|
||||||
|
%x()
|
||||||
|
%webout(CLOSE)
|
||||||
|
;;;;
|
||||||
|
%mp_createwebservice(path=/Public/app/common,name=sendArr)
|
||||||
|
parmcards4;
|
||||||
|
data work.macvars;
|
||||||
|
set sashelp.vmacro;
|
||||||
|
run;
|
||||||
|
%webout(OPEN)
|
||||||
|
%webout(OBJ,macvars)
|
||||||
|
%webout(CLOSE)
|
||||||
|
;;;;
|
||||||
|
%mp_createwebservice(path=/Public/app/common,name=sendMacVars)
|
||||||
|
parmcards4;
|
||||||
|
If you can keep your head when all about you
|
||||||
|
Are losing theirs and blaming it on you,
|
||||||
|
If you can trust yourself when all men doubt you,
|
||||||
|
But make allowance for their doubting too;
|
||||||
|
;;;;
|
||||||
|
%mp_createwebservice(path=/Public/app/common,name=makeErr)
|
||||||
|
parmcards4;
|
||||||
|
%webout(OPEN)
|
||||||
|
data _null_;
|
||||||
|
file _webout;
|
||||||
|
put ' the discovery channel ';
|
||||||
|
run;
|
||||||
|
%webout(CLOSE)
|
||||||
|
;;;;
|
||||||
|
%mp_createwebservice(path=/Public/app/common,name=invalidJSON)
|
||||||
```
|
```
|
||||||
|
|
||||||
You should now be able to access the tests in your browser at the deployed path on your server.
|
You should now be able to access the tests in your browser at the deployed path on your server.
|
||||||
|
|||||||
7668
sasjs-tests/package-lock.json
generated
7668
sasjs-tests/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -61,7 +61,7 @@ export const basicTests = (
|
|||||||
'Should fail on first attempt and should log the user in on second attempt',
|
'Should fail on first attempt and should log the user in on second attempt',
|
||||||
test: async () => {
|
test: async () => {
|
||||||
await adapter.logOut()
|
await adapter.logOut()
|
||||||
await adapter.logIn('invalid', 'invalid').catch((err: any) => {})
|
await adapter.logIn('invalid', 'invalid')
|
||||||
return await adapter.logIn(userName, password)
|
return await adapter.logIn(userName, password)
|
||||||
},
|
},
|
||||||
assertion: (response: any) =>
|
assertion: (response: any) =>
|
||||||
@@ -161,17 +161,26 @@ export const basicTests = (
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Web request',
|
title: 'Request with extra attributes on JES approach',
|
||||||
description: 'Should run the request with old web approach',
|
description:
|
||||||
|
'Should complete successful request with extra attributes present in response',
|
||||||
test: async () => {
|
test: async () => {
|
||||||
const config: Partial<SASjsConfig> = {
|
const config: Partial<SASjsConfig> = {
|
||||||
useComputeApi: false
|
useComputeApi: false
|
||||||
}
|
}
|
||||||
|
|
||||||
return await adapter.request('common/sendArr', stringData, config)
|
return await adapter.request(
|
||||||
|
'common/sendArr',
|
||||||
|
stringData,
|
||||||
|
config,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
['file', 'data']
|
||||||
|
)
|
||||||
},
|
},
|
||||||
assertion: (response: any) => {
|
assertion: (response: any) => {
|
||||||
return response.table1[0][0] === stringData.table1[0].col1
|
const responseKeys: any = Object.keys(response)
|
||||||
|
return responseKeys.includes('file') && responseKeys.includes('data')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,35 +1,9 @@
|
|||||||
import SASjs from '@sasjs/adapter'
|
import SASjs from '@sasjs/adapter'
|
||||||
import { TestSuite } from '@sasjs/test-framework'
|
import { TestSuite } from '@sasjs/test-framework'
|
||||||
|
|
||||||
const stringData: any = { table1: [{ col1: 'first col value' }] }
|
|
||||||
|
|
||||||
export const computeTests = (adapter: SASjs): TestSuite => ({
|
export const computeTests = (adapter: SASjs): TestSuite => ({
|
||||||
name: 'Compute',
|
name: 'Compute',
|
||||||
tests: [
|
tests: [
|
||||||
{
|
|
||||||
title: 'Compute API request',
|
|
||||||
description: 'Should run the request with compute API approach',
|
|
||||||
test: async () => {
|
|
||||||
return await adapter.request('common/sendArr', stringData)
|
|
||||||
},
|
|
||||||
assertion: (response: any) => {
|
|
||||||
return response.table1[0][0] === stringData.table1[0].col1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'JES API request',
|
|
||||||
description: 'Should run the request with JES API approach',
|
|
||||||
test: async () => {
|
|
||||||
const config = {
|
|
||||||
useComputeApi: false
|
|
||||||
}
|
|
||||||
|
|
||||||
return await adapter.request('common/sendArr', stringData, config)
|
|
||||||
},
|
|
||||||
assertion: (response: any) => {
|
|
||||||
return response.table1[0][0] === stringData.table1[0].col1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: 'Start Compute Job - not waiting for result',
|
title: 'Start Compute Job - not waiting for result',
|
||||||
description: 'Should start a compute job and return the session',
|
description: 'Should start a compute job and return the session',
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
|
|||||||
result =
|
result =
|
||||||
result &&
|
result &&
|
||||||
res.table1[index][3] ===
|
res.table1[index][3] ===
|
||||||
(multipleRowsWithNulls.table1[index].col4 || '')
|
(multipleRowsWithNulls.table1[index].col4 || ' ')
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -164,7 +164,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
|
|||||||
result =
|
result =
|
||||||
result &&
|
result &&
|
||||||
res.table1[index][3] ===
|
res.table1[index][3] ===
|
||||||
(multipleColumnsWithNulls.table1[index].col4 || '')
|
(multipleColumnsWithNulls.table1[index].col4 || ' ')
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -329,7 +329,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
|
|||||||
result =
|
result =
|
||||||
result &&
|
result &&
|
||||||
res.table1[index].COL4 ===
|
res.table1[index].COL4 ===
|
||||||
(multipleRowsWithNulls.table1[index].col4 || '')
|
(multipleRowsWithNulls.table1[index].col4 || ' ')
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -358,7 +358,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
|
|||||||
result =
|
result =
|
||||||
result &&
|
result &&
|
||||||
res.table1[index].COL4 ===
|
res.table1[index].COL4 ===
|
||||||
(multipleColumnsWithNulls.table1[index].col4 || '')
|
(multipleColumnsWithNulls.table1[index].col4 || ' ')
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,6 @@ const moreSpecialCharData: any = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const stringData: any = { table1: [{ col1: 'first col value' }] }
|
|
||||||
|
|
||||||
const getWideData = () => {
|
const getWideData = () => {
|
||||||
const cols: any = {}
|
const cols: any = {}
|
||||||
for (let i = 1; i <= 10000; i++) {
|
for (let i = 1; i <= 10000; i++) {
|
||||||
@@ -271,34 +269,6 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: 'Request with extra attributes on JES approach',
|
|
||||||
description:
|
|
||||||
'Should complete successful request with extra attributes present in response',
|
|
||||||
test: async () => {
|
|
||||||
if (adapter.getSasjsConfig().serverType !== 'SASVIYA')
|
|
||||||
return Promise.resolve('skip')
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
useComputeApi: false
|
|
||||||
}
|
|
||||||
|
|
||||||
return await adapter.request(
|
|
||||||
'common/sendArr',
|
|
||||||
stringData,
|
|
||||||
config,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
['file', 'data']
|
|
||||||
)
|
|
||||||
},
|
|
||||||
assertion: (response: any) => {
|
|
||||||
if (response === 'skip') return true
|
|
||||||
|
|
||||||
const responseKeys: any = Object.keys(response)
|
|
||||||
return responseKeys.includes('file') && responseKeys.includes('data')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: 'Special missing values',
|
title: 'Special missing values',
|
||||||
description: 'Should support special missing values',
|
description: 'Should support special missing values',
|
||||||
|
|||||||
@@ -592,6 +592,15 @@ export default class SASjs {
|
|||||||
'A username and password are required when using the default login mechanism.'
|
'A username and password are required when using the default login mechanism.'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (this.sasjsConfig.serverType === ServerType.Sasjs) {
|
||||||
|
if (!clientId)
|
||||||
|
throw new Error(
|
||||||
|
'A username, password and clientId are required when using the default login mechanism with server type SASJS.'
|
||||||
|
)
|
||||||
|
|
||||||
|
return this.authManager!.logInSasjs(username, password, clientId)
|
||||||
|
}
|
||||||
|
|
||||||
return this.authManager!.logIn(username, password)
|
return this.authManager!.logIn(username, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { ExecutionQuery } from './types'
|
|||||||
import { RequestClient } from './request/RequestClient'
|
import { RequestClient } from './request/RequestClient'
|
||||||
import { getAccessTokenForSasjs } from './auth/getAccessTokenForSasjs'
|
import { getAccessTokenForSasjs } from './auth/getAccessTokenForSasjs'
|
||||||
import { refreshTokensForSasjs } from './auth/refreshTokensForSasjs'
|
import { refreshTokensForSasjs } from './auth/refreshTokensForSasjs'
|
||||||
|
import { getAuthCodeForSasjs } from './auth/getAuthCodeForSasjs'
|
||||||
import { parseWeboutResponse } from './utils'
|
import { parseWeboutResponse } from './utils'
|
||||||
import { getTokens } from './auth/getTokens'
|
import { getTokens } from './auth/getTokens'
|
||||||
|
|
||||||
@@ -113,6 +114,20 @@ export class SASjsApiClient {
|
|||||||
public async refreshTokens(refreshToken: string): Promise<SASjsAuthResponse> {
|
public async refreshTokens(refreshToken: string): Promise<SASjsAuthResponse> {
|
||||||
return refreshTokensForSasjs(this.requestClient, refreshToken)
|
return refreshTokensForSasjs(this.requestClient, refreshToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a login authenticate and returns an auth code for the given client.
|
||||||
|
* @param username - a string representing the username.
|
||||||
|
* @param password - a string representing the password.
|
||||||
|
* @param clientId - the client ID to authenticate with.
|
||||||
|
*/
|
||||||
|
public async getAuthCode(
|
||||||
|
username: string,
|
||||||
|
password: string,
|
||||||
|
clientId: string
|
||||||
|
) {
|
||||||
|
return getAuthCodeForSasjs(this.requestClient, username, password, clientId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo move to sasjs/utils
|
// todo move to sasjs/utils
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { ServerType } from '@sasjs/utils/types'
|
|||||||
import { RequestClient } from '../request/RequestClient'
|
import { RequestClient } from '../request/RequestClient'
|
||||||
import { LoginOptions, LoginResult } from '../types/Login'
|
import { LoginOptions, LoginResult } from '../types/Login'
|
||||||
import { serialize } from '../utils'
|
import { serialize } from '../utils'
|
||||||
|
import { getAccessTokenForSasjs } from './getAccessTokenForSasjs'
|
||||||
|
import { getAuthCodeForSasjs } from './getAuthCodeForSasjs'
|
||||||
import { openWebPage } from './openWebPage'
|
import { openWebPage } from './openWebPage'
|
||||||
import { verifySas9Login } from './verifySas9Login'
|
import { verifySas9Login } from './verifySas9Login'
|
||||||
import { verifySasViyaLogin } from './verifySasViyaLogin'
|
import { verifySasViyaLogin } from './verifySasViyaLogin'
|
||||||
@@ -81,6 +83,39 @@ export class AuthManager {
|
|||||||
return { isLoggedIn: false, userName: '' }
|
return { isLoggedIn: false, userName: '' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs into the SAS server with the supplied credentials.
|
||||||
|
* @param userName - a string representing the username.
|
||||||
|
* @param password - a string representing the password.
|
||||||
|
* @param clientId - a string representing the client ID.
|
||||||
|
* @returns - a boolean `isLoggedin` and a string `username`
|
||||||
|
*/
|
||||||
|
public async logInSasjs(
|
||||||
|
username: string,
|
||||||
|
password: string,
|
||||||
|
clientId: string
|
||||||
|
): Promise<LoginResult> {
|
||||||
|
const isLoggedIn = await this.sendLoginRequestSasjs(
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
clientId
|
||||||
|
)
|
||||||
|
.then((res) => {
|
||||||
|
this.userName = username
|
||||||
|
this.requestClient.saveLocalStorageToken(
|
||||||
|
res.access_token,
|
||||||
|
res.refresh_token
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
.catch(() => false)
|
||||||
|
|
||||||
|
return {
|
||||||
|
isLoggedIn,
|
||||||
|
userName: this.userName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs into the SAS server with the supplied credentials.
|
* Logs into the SAS server with the supplied credentials.
|
||||||
* @param username - a string representing the username.
|
* @param username - a string representing the username.
|
||||||
@@ -117,7 +152,7 @@ export class AuthManager {
|
|||||||
|
|
||||||
let loginResponse = await this.sendLoginRequest(loginForm, loginParams)
|
let loginResponse = await this.sendLoginRequest(loginForm, loginParams)
|
||||||
|
|
||||||
let isLoggedIn = isLogInSuccess(this.serverType, loginResponse)
|
let isLoggedIn = isLogInSuccess(loginResponse)
|
||||||
|
|
||||||
if (!isLoggedIn) {
|
if (!isLoggedIn) {
|
||||||
if (isCredentialsVerifyError(loginResponse)) {
|
if (isCredentialsVerifyError(loginResponse)) {
|
||||||
@@ -161,17 +196,6 @@ export class AuthManager {
|
|||||||
loginForm: { [key: string]: any },
|
loginForm: { [key: string]: any },
|
||||||
loginParams: { [key: string]: any }
|
loginParams: { [key: string]: any }
|
||||||
) {
|
) {
|
||||||
if (this.serverType === ServerType.Sasjs) {
|
|
||||||
const { username, password } = loginParams
|
|
||||||
const { result: loginResponse } = await this.requestClient.post<string>(
|
|
||||||
this.loginUrl,
|
|
||||||
{ username, password },
|
|
||||||
undefined
|
|
||||||
)
|
|
||||||
|
|
||||||
return loginResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key in loginForm) {
|
for (const key in loginForm) {
|
||||||
loginParams[key] = loginForm[key]
|
loginParams[key] = loginForm[key]
|
||||||
}
|
}
|
||||||
@@ -191,6 +215,19 @@ export class AuthManager {
|
|||||||
return loginResponse
|
return loginResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async sendLoginRequestSasjs(
|
||||||
|
username: string,
|
||||||
|
password: string,
|
||||||
|
clientId: string
|
||||||
|
) {
|
||||||
|
const authCode = await getAuthCodeForSasjs(
|
||||||
|
this.requestClient,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
clientId
|
||||||
|
)
|
||||||
|
return getAccessTokenForSasjs(this.requestClient, clientId, authCode)
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Checks whether a session is active, or login is required.
|
* Checks whether a session is active, or login is required.
|
||||||
* @returns - a promise which resolves with an object containing three values
|
* @returns - a promise which resolves with an object containing three values
|
||||||
@@ -211,7 +248,8 @@ export class AuthManager {
|
|||||||
//Residue can happen in case of session expiration
|
//Residue can happen in case of session expiration
|
||||||
await this.logOut()
|
await this.logOut()
|
||||||
|
|
||||||
loginForm = await this.getNewLoginForm()
|
if (this.serverType !== ServerType.Sasjs)
|
||||||
|
loginForm = await this.getNewLoginForm()
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
@@ -222,12 +260,6 @@ export class AuthManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getNewLoginForm() {
|
private async getNewLoginForm() {
|
||||||
if (this.serverType === ServerType.Sasjs) {
|
|
||||||
// server will be sending CSRF cookie,
|
|
||||||
// http client will use it automatically
|
|
||||||
return this.requestClient.get('/', undefined)
|
|
||||||
}
|
|
||||||
|
|
||||||
const { result: formResponse } = await this.requestClient.get<string>(
|
const { result: formResponse } = await this.requestClient.get<string>(
|
||||||
this.loginUrl.replace('.do', ''),
|
this.loginUrl.replace('.do', ''),
|
||||||
undefined,
|
undefined,
|
||||||
@@ -257,12 +289,6 @@ export class AuthManager {
|
|||||||
const isLoggedIn = loginResponse !== 'authErr'
|
const isLoggedIn = loginResponse !== 'authErr'
|
||||||
const userName = isLoggedIn ? this.extractUserName(loginResponse) : ''
|
const userName = isLoggedIn ? this.extractUserName(loginResponse) : ''
|
||||||
|
|
||||||
if (!isLoggedIn) {
|
|
||||||
//We will logout to make sure cookies are removed and login form is presented
|
|
||||||
//Residue can happen in case of session expiration
|
|
||||||
await this.logOut()
|
|
||||||
}
|
|
||||||
|
|
||||||
return { isLoggedIn, userName }
|
return { isLoggedIn, userName }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,8 +384,5 @@ const isCredentialsVerifyError = (response: string): boolean =>
|
|||||||
response
|
response
|
||||||
)
|
)
|
||||||
|
|
||||||
const isLogInSuccess = (serverType: ServerType, response: any): boolean => {
|
const isLogInSuccess = (response: string): boolean =>
|
||||||
if (serverType === ServerType.Sasjs) return response?.loggedin
|
/You have signed in/gm.test(response)
|
||||||
|
|
||||||
return /You have signed in/gm.test(response)
|
|
||||||
}
|
|
||||||
|
|||||||
31
src/auth/getAuthCodeForSasjs.ts
Normal file
31
src/auth/getAuthCodeForSasjs.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { prefixMessage } from '@sasjs/utils/error'
|
||||||
|
import { RequestClient } from '../request/RequestClient'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a login authenticate and returns an auth code for the given client.
|
||||||
|
* @param requestClient - the pre-configured HTTP request client
|
||||||
|
* @param username - a string representing the username.
|
||||||
|
* @param password - a string representing the password.
|
||||||
|
* @param clientId - the client ID to authenticate with.
|
||||||
|
*/
|
||||||
|
export const getAuthCodeForSasjs = async (
|
||||||
|
requestClient: RequestClient,
|
||||||
|
username: string,
|
||||||
|
password: string,
|
||||||
|
clientId: string
|
||||||
|
) => {
|
||||||
|
const url = '/SASjsApi/auth/authorize'
|
||||||
|
const data = { username, password, clientId }
|
||||||
|
|
||||||
|
const { code: authCode } = await requestClient
|
||||||
|
.post<{ code: string }>(url, data, undefined)
|
||||||
|
.then((res) => res.result)
|
||||||
|
.catch((err) => {
|
||||||
|
throw prefixMessage(
|
||||||
|
err,
|
||||||
|
'Error while authenticating with provided username, password and clientId. '
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return authCode
|
||||||
|
}
|
||||||
@@ -92,7 +92,7 @@ export class WebJobExecutor extends BaseJobExecutor {
|
|||||||
if (jobUri.length > 0) {
|
if (jobUri.length > 0) {
|
||||||
apiUrl += '&_job=' + jobUri
|
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.
|
* 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.
|
* 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=')
|
apiUrl = apiUrl.replace('_program=', '__program=')
|
||||||
@@ -169,17 +169,13 @@ export class WebJobExecutor extends BaseJobExecutor {
|
|||||||
? res.result.log.map((logLine: any) => logLine.line).join('\n')
|
? res.result.log.map((logLine: any) => logLine.line).join('\n')
|
||||||
: res.result.log
|
: res.result.log
|
||||||
|
|
||||||
const resObj = res
|
const resObj =
|
||||||
|
this.serverType === ServerType.Sasjs
|
||||||
if (this.serverType === ServerType.Sasjs) {
|
? {
|
||||||
if (res.result._webout.length < 1)
|
result: res.result._webout,
|
||||||
throw new JobExecutionError(
|
log: parsedSasjsServerLog
|
||||||
0,
|
}
|
||||||
'Job execution failed',
|
: res
|
||||||
parsedSasjsServerLog
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.requestClient!.appendRequest(resObj, sasJob, config.debug)
|
this.requestClient!.appendRequest(resObj, sasJob, config.debug)
|
||||||
|
|
||||||
let jsonResponse = res.result
|
let jsonResponse = res.result
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import {
|
|||||||
parseSourceCode,
|
parseSourceCode,
|
||||||
createAxiosInstance
|
createAxiosInstance
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
import { InvalidCsrfError } from '../types/errors/InvalidCsrfError'
|
|
||||||
|
|
||||||
export interface HttpClient {
|
export interface HttpClient {
|
||||||
get<T>(
|
get<T>(
|
||||||
@@ -499,24 +498,6 @@ export class RequestClient implements HttpClient {
|
|||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e instanceof InvalidCsrfError) {
|
|
||||||
// Fetching root will inject CSRF token in cookie
|
|
||||||
await this.httpClient
|
|
||||||
.get('/', {
|
|
||||||
withCredentials: true
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
throw prefixMessage(err, 'Error while re-fetching CSRF token.')
|
|
||||||
})
|
|
||||||
|
|
||||||
return await callback().catch((err: any) => {
|
|
||||||
throw prefixMessage(
|
|
||||||
err,
|
|
||||||
'Error while executing callback in handleError. '
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response?.status === 403 || response?.status === 449) {
|
if (response?.status === 403 || response?.status === 449) {
|
||||||
this.parseAndSetCsrfToken(response)
|
this.parseAndSetCsrfToken(response)
|
||||||
|
|
||||||
@@ -603,17 +584,9 @@ export class RequestClient implements HttpClient {
|
|||||||
export const throwIfError = (response: AxiosResponse) => {
|
export const throwIfError = (response: AxiosResponse) => {
|
||||||
switch (response.status) {
|
switch (response.status) {
|
||||||
case 400:
|
case 400:
|
||||||
if (
|
if (typeof response.data === 'object') {
|
||||||
typeof response.data === 'object' &&
|
|
||||||
response.data.error === 'invalid_grant'
|
|
||||||
) {
|
|
||||||
// In SASVIYA when trying to get access token, if auth code is wrong status code will be 400 so in such case we return login required error.
|
|
||||||
throw new LoginRequiredError(response.data)
|
throw new LoginRequiredError(response.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.data.toLowerCase() === 'invalid csrf token!') {
|
|
||||||
throw new InvalidCsrfError()
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
case 401:
|
case 401:
|
||||||
if (typeof response.data === 'object') {
|
if (typeof response.data === 'object') {
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
export class InvalidCsrfError extends Error {
|
|
||||||
constructor() {
|
|
||||||
const message = 'Invalid CSRF token!'
|
|
||||||
|
|
||||||
super(`Auth error: ${message}`)
|
|
||||||
this.name = 'InvalidCsrfError'
|
|
||||||
Object.setPrototypeOf(this, InvalidCsrfError.prototype)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { isSpecialMissing } from '@sasjs/utils/input/validators'
|
import { isSpecialMissing } from '@sasjs/utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the given JSON object array to a CSV string.
|
* Converts the given JSON object array to a CSV string.
|
||||||
|
|||||||
Reference in New Issue
Block a user