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

Compare commits

...

74 Commits

Author SHA1 Message Date
Krishna Acondy
d5de6b50a9 Merge pull request #59 from sasjs/debug-log-fetch
fix(logs): fetch log only when debug is switched on
2020-09-02 10:59:50 +01:00
Krishna Acondy
f1184346d8 Merge branch 'master' into debug-log-fetch 2020-09-02 10:47:36 +01:00
Krishna Acondy
337fe5e988 Merge pull request #55 from sasjs/dependabot/npm_and_yarn/typedoc-neo-theme-1.0.10
chore(deps-dev): bump typedoc-neo-theme from 1.0.9 to 1.0.10
2020-09-02 10:47:23 +01:00
Krishna Acondy
c23e6352e2 fix(logs): fetch log only when debug is switched on 2020-09-02 10:43:36 +01:00
dependabot-preview[bot]
57ce0ae35f chore(deps-dev): bump typedoc-neo-theme from 1.0.9 to 1.0.10
Bumps [typedoc-neo-theme](https://github.com/google/typedoc-neo-theme) from 1.0.9 to 1.0.10.
- [Release notes](https://github.com/google/typedoc-neo-theme/releases)
- [Commits](https://github.com/google/typedoc-neo-theme/compare/v1.0.9...v1.0.10)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-02 09:34:22 +00:00
Krishna Acondy
115caec761 Merge pull request #58 from sasjs/change-code-style
chore(*): change code style to use single quote
2020-09-02 10:31:53 +01:00
Yury Shkoda
c626c57662 chore(*): change code style to use single quote 2020-09-01 14:28:15 +03:00
Krishna Acondy
82b14fad14 Merge pull request #57 from sasjs/change-code-style
chore(*): change code style to remove semicolons
2020-09-01 12:07:24 +01:00
Krishna Acondy
755bf7d07c chore(*): change code style to remove semicolons 2020-09-01 11:51:17 +01:00
Krishna Acondy
619833db29 Merge pull request #56 from sasjs/performance-improvements
fix(*): Performance improvements
2020-09-01 11:49:34 +01:00
Krishna Acondy
a587d9f6de chore(ci): add lint action 2020-09-01 11:20:46 +01:00
Krishna Acondy
83fb89f779 fix(*): cache job definition code after first fetch, make initial state request before poll 2020-09-01 11:13:52 +01:00
Krishna Acondy
6b98bbce7c chore(types): add code property to Job model 2020-09-01 11:12:56 +01:00
Krishna Acondy
3c2487e423 Merge pull request #49 from sasjs/dependabot/npm_and_yarn/ts-loader-8.0.3
chore(deps-dev): bump ts-loader from 8.0.2 to 8.0.3
2020-08-31 10:23:01 +01:00
dependabot-preview[bot]
0d52af5375 chore(deps-dev): bump ts-loader from 8.0.2 to 8.0.3
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 8.0.2 to 8.0.3.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v8.0.2...v8.0.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-31 09:04:08 +00:00
Krishna Acondy
d0da343efc Merge pull request #53 from sasjs/dependabot/npm_and_yarn/prettier-2.1.1
chore(deps-dev): bump prettier from 2.0.5 to 2.1.1
2020-08-31 09:58:58 +01:00
dependabot-preview[bot]
54f401a319 chore(deps-dev): bump prettier from 2.0.5 to 2.1.1
Bumps [prettier](https://github.com/prettier/prettier) from 2.0.5 to 2.1.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.0.5...2.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-31 08:24:44 +00:00
Krishna Acondy
5efcb11b7d chore(*): add lint fix command 2020-08-31 09:22:21 +01:00
Krishna Acondy
929d7b993b chore(*): add .prettierrc, fix formatting 2020-08-31 09:15:02 +01:00
Allan Bowe
688221c042 Update example.html 2020-08-28 20:11:04 +02:00
Allan Bowe
57d0b30f47 Merge pull request #52 from sasjs/deploy-issue
fix: members of type folder should be processed first on service pack deploy.
2020-08-28 20:00:43 +02:00
0d5af2487d fix: members of type folder should be proccessed first on service pack deploy 2020-08-28 15:30:53 +03:00
1ea163fd03 fix: members of type folder should be proccessed first on service pack deploy 2020-08-28 14:31:35 +03:00
Allan Bowe
f27444bc52 Merge pull request #48 from sasjs/issue47
fix: login not working on non english browsers
2020-08-23 18:24:36 +02:00
Mihajlo Medjedovic
de426c9a92 fix: login not working on non english browsers 2020-08-23 18:17:35 +02:00
Allan Bowe
a006ead205 Merge pull request #46 from sasjs/issue45
fix: jobViaWeb config override
2020-08-23 17:07:47 +02:00
Mihajlo Medjedovic
422c2a1fd5 fix: jobViaWeb config override 2020-08-23 17:04:39 +02:00
Krishna Acondy
0c6409e402 Merge pull request #44 from sasjs/session-expiry-retry
fix(session-expiry-retry): retry job with new session on expiry
2020-08-18 21:45:47 +01:00
Krishna Acondy
68b864cf75 fix(session-expiry): discard and create new session if expired 2020-08-18 21:35:02 +01:00
Krishna Acondy
75a11cdff4 fix(session-expiry-retry): retry job with new session when current session has expired 2020-08-18 20:23:59 +01:00
Allan Bowe
4e2b6d32cc Merge pull request #43 from sasjs/parse-compute-log
fix(log): use compute log directly when available
2020-08-18 18:21:56 +02:00
Krishna Acondy
cd9757b383 Merge branch 'master' into parse-compute-log 2020-08-18 17:01:33 +01:00
Mihajlo Medjedovic
fb727788d0 fix: log capture if job fails, test framework update, added test for log capture 2020-08-18 17:36:25 +02:00
Allan Bowe
35eb6c4935 Merge pull request #40 from sasjs/dependabot/npm_and_yarn/types/jest-26.0.10
chore(deps-dev): bump @types/jest from 26.0.9 to 26.0.10
2020-08-18 13:26:57 +02:00
Mihajlo Medjedovic
ea0f338b90 Merge branch 'master' into parse-compute-log 2020-08-18 13:26:16 +02:00
Allan Bowe
b6a17b39b9 Merge branch 'master' into dependabot/npm_and_yarn/types/jest-26.0.10 2020-08-18 13:25:28 +02:00
Allan Bowe
9ed64e5a2c Merge pull request #42 from sasjs/issue41
fix: csrfTokenWeb setter callback
2020-08-18 13:22:33 +02:00
Mihajlo Medjedovic
0479a5d651 fix: csrfTokenWeb setter callback 2020-08-18 13:17:29 +02:00
Allan Bowe
005f10bb47 Update CONTRIBUTING.md 2020-08-18 11:24:30 +02:00
Krishna Acondy
98c9cb78ff fix(log): use compute log as-is when available 2020-08-18 10:05:34 +01:00
Krishna Acondy
8192f69f67 fix(*): do not use polyfill when running on Node.js 2020-08-18 08:25:40 +01:00
dependabot-preview[bot]
c28a8ebf15 chore(deps-dev): bump @types/jest from 26.0.9 to 26.0.10
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.9 to 26.0.10.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-17 13:19:21 +00:00
Krishna Acondy
a409d8cdb6 Merge pull request #38 from sasjs/brwoserCheckFix
fix: isIEorEdgeOrOldFirefox error
2020-08-14 13:10:44 +01:00
Mihajlo Medjedovic
618a20eaba fix: isIEorEdgeOrOldFirefox error 2020-08-14 14:09:28 +02:00
Krishna Acondy
c9b1273c31 Merge pull request #37 from sasjs/ie-edge-fetch
fix(*): use fetch polyfill in IE, Edge and Firefox <60
2020-08-13 21:56:11 +01:00
Krishna Acondy
59674744be fix(*): use fetch polyfill for Firefox versions older than 60 2020-08-13 21:46:08 +01:00
Krishna Acondy
870cc0055b fix(*): use fetch polyfill in Firefox 60 2020-08-13 21:07:21 +01:00
Krishna Acondy
0ffa62fab4 fix(*): use fetch polyfill in IE and Edge 2020-08-13 20:53:27 +01:00
Allan Bowe
b4c7868fb6 Merge pull request #36 from sasjs/issue34
fix: job definition debug log parse
2020-08-11 18:44:39 +02:00
Mihajlo Medjedovic
2266578013 Merge branch 'master' into issue34 2020-08-11 18:29:41 +02:00
Mihajlo Medjedovic
f2ebe1a5b0 fix: job definition debug log parse 2020-08-10 18:13:11 +02:00
Allan Bowe
6a52bbe560 Merge pull request #35 from sasjs/issue33
fix: makeRequest inconsistent response structure
2020-08-10 17:19:05 +02:00
Mihajlo Medjedovic
a5c725e677 fix: makeRequest incositent response structure 2020-08-10 17:05:10 +02:00
Krishna Acondy
f5e1907e28 feat(clear-requests): add function to clear debug requests 2020-08-08 14:27:40 +01:00
Krishna Acondy
f7a9b0cbb6 fix(compute-api): ignore 404s when requesting webout content 2020-08-08 14:26:09 +01:00
Krishna Acondy
1258a1a180 fix(login-callback): fix request failure when login is required 2020-08-08 12:56:31 +01:00
Krishna Acondy
0bb343a1de chore(deps): bump adapter and test framework 2020-08-08 11:42:16 +01:00
Krishna Acondy
929c89b70f Merge pull request #32 from sasjs/dependabot/npm_and_yarn/types/jest-26.0.9
chore(deps-dev): bump @types/jest from 26.0.8 to 26.0.9
2020-08-08 11:04:25 +01:00
dependabot-preview[bot]
169ca35238 chore(deps-dev): bump @types/jest from 26.0.8 to 26.0.9
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.8 to 26.0.9.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-06 12:59:52 +00:00
Allan Bowe
60be28f149 Merge pull request #31 from sasjs/session-cleanup
feat(session-cleanup): delete a session after it has been used
2020-08-05 23:17:42 +02:00
Krishna Acondy
14daa55184 feat(session-cleanup): delete a session after it has been used 2020-08-05 21:52:23 +01:00
Krishna Acondy
f763f05b5e Merge pull request #30 from sasjs/deployFix
fix: makeRequest retry
2020-08-05 20:02:44 +01:00
Mihajlo Medjedovic
b6aced5bad fix: makeRequest retry 2020-08-05 20:58:10 +02:00
Allan Bowe
7bb7db0f27 Merge pull request #24 from sasjs/dependabot/npm_and_yarn/webpack-4.44.1
chore(deps-dev): bump webpack from 4.43.0 to 4.44.1
2020-08-04 20:57:22 +02:00
Allan Bowe
36ea148446 Merge branch 'master' into dependabot/npm_and_yarn/webpack-4.44.1 2020-08-04 20:56:51 +02:00
Allan Bowe
762254d8c4 Merge pull request #29 from sasjs/deployFixing
fix: makeRequest retry, getFolderUri error catching
2020-08-04 20:56:20 +02:00
Mihajlo Medjedovic
8474b222ea fix: makeRequest retry, getFolderUri error catching 2020-08-04 20:39:09 +02:00
dependabot-preview[bot]
c1750c014e chore(deps-dev): bump webpack from 4.43.0 to 4.44.1
Bumps [webpack](https://github.com/webpack/webpack) from 4.43.0 to 4.44.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v4.43.0...v4.44.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-03 20:41:21 +00:00
Krishna Acondy
d7a7909529 Merge pull request #26 from sasjs/dependabot/npm_and_yarn/tslint-6.1.3
chore(deps-dev): bump tslint from 6.1.2 to 6.1.3
2020-08-03 21:39:07 +01:00
dependabot-preview[bot]
31b60a985e chore(deps-dev): bump tslint from 6.1.2 to 6.1.3
Bumps [tslint](https://github.com/palantir/tslint) from 6.1.2 to 6.1.3.
- [Release notes](https://github.com/palantir/tslint/releases)
- [Changelog](https://github.com/palantir/tslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/palantir/tslint/compare/6.1.2...6.1.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-03 20:38:05 +00:00
Krishna Acondy
a6b13d9cb9 Merge pull request #27 from sasjs/dependabot/npm_and_yarn/types/jest-26.0.8
chore(deps-dev): bump @types/jest from 26.0.5 to 26.0.8
2020-08-03 21:35:56 +01:00
dependabot-preview[bot]
55fcbf2e36 chore(deps-dev): bump @types/jest from 26.0.5 to 26.0.8
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.5 to 26.0.8.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-03 19:34:31 +00:00
Krishna Acondy
fad8549d92 Merge pull request #28 from sasjs/dependabot/npm_and_yarn/ts-loader-8.0.2
chore(deps-dev): bump ts-loader from 8.0.1 to 8.0.2
2020-08-03 20:32:10 +01:00
dependabot-preview[bot]
95c03e5d07 chore(deps-dev): bump ts-loader from 8.0.1 to 8.0.2
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 8.0.1 to 8.0.2.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v8.0.1...v8.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-03 18:57:46 +00:00
59 changed files with 2358 additions and 2434 deletions

View File

@@ -21,7 +21,11 @@ jobs:
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run package:lib
- name: Install Dependencies
run: npm ci
- name: Check code style
run: npm run lint
- name: Build Package
run: npm run package:lib
env:
CI: true

View File

@@ -16,6 +16,8 @@ jobs:
uses: actions/checkout@v2
- name: Install Dependencies
run: npm ci
- name: Check code style
run: npm run lint
- name: Build Project
run: npm run build
- name: Semantic Release

6
.prettierrc Normal file
View File

@@ -0,0 +1,6 @@
{
"trailingComma": "none",
"tabWidth": 2,
"semi": false,
"singleQuote": true
}

View File

@@ -70,6 +70,14 @@ parmcards4;
%webout(CLOSE)
;;;;
%mp_createwebservice(path=/Public/app/common,name=sendArr)
filename ft15f001 temp;
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)
```
The above services will return anything you send. To run the tests simply launch `npm run cypress`.

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/combine/npm/chart.js@2.9.3,npm/jquery@3.5.1,npm/@sasjs/adapter@1.0.6"></script>
<script src="https://cdn.jsdelivr.net/combine/npm/chart.js@2.9.3,npm/jquery@3.5.1,npm/@sasjs/adapter@1"></script>
<script>
var sasJs = new SASjs.default({
appLoc: "/Public/app/readme"
@@ -106,4 +106,4 @@
<canvas id="myChart" style="display: none;"></canvas>
</div>
</body>
</head>
</head>

1717
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,8 +5,8 @@
"build": "rimraf build && webpack",
"package:lib": "npm run build && cp ./package.json build && cd build && npm version \"5.0.0\" && npm pack",
"publish:lib": "npm run build && cd build && npm publish",
"format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
"lint": "tslint -p tsconfig.json",
"lint:fix": "npx prettier --write '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}'",
"test": "jest",
"prepublishOnly": "cp -r ./build/* . && rm -rf ./build",
"postpublish": "git clean -fd",
@@ -37,23 +37,22 @@
"license": "ISC",
"devDependencies": {
"@types/isomorphic-fetch": "0.0.35",
"@types/jest": "^26.0.5",
"@types/jest": "^26.0.10",
"cp": "^0.2.0",
"jest": "^25.5.4",
"path": "^0.12.7",
"prettier": "^2.0.5",
"rimraf": "^3.0.2",
"semantic-release": "^17.1.1",
"ts-jest": "^25.5.1",
"ts-loader": "^8.0.1",
"tslint": "^6.1.2",
"ts-loader": "^8.0.3",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"typedoc": "^0.17.8",
"typedoc-neo-theme": "^1.0.9",
"typedoc-neo-theme": "^1.0.10",
"typedoc-plugin-external-module-name": "^4.0.3",
"typescript": "^3.9.7",
"uglifyjs-webpack-plugin": "^2.2.0",
"webpack": "^4.43.0",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12"
},
"main": "index.js",

6
sasjs-tests/.prettierrc Normal file
View File

@@ -0,0 +1,6 @@
{
"trailingComma": "none",
"tabWidth": 2,
"semi": true,
"singleQuote": false
}

View File

@@ -1357,9 +1357,9 @@
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
},
"@sasjs/adapter": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-1.0.5.tgz",
"integrity": "sha512-54gQZD7QdNmQu77axOqr0vMS7hUVXO5hPbUtwXXocMIi3kRQDbROYjC3kuiFM9FrxqiZWbLRcyOqmFv3W/N36w==",
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-1.3.6.tgz",
"integrity": "sha512-d2B+cTII+vabKCU8mJy90mEz3tCWw2pEp4qIBGsDamJiTS0Rx69dgXGHuRUm8KtjLDHHrzwXATsqviU3dnU0QQ==",
"requires": {
"es6-promise": "^4.2.8",
"form-data": "^3.0.0",
@@ -1379,15 +1379,23 @@
}
},
"@sasjs/test-framework": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@sasjs/test-framework/-/test-framework-1.3.0.tgz",
"integrity": "sha512-vrbRFUhNUShLlNFZO+XwVwFLXDLApQG9zOPx00xhQ8IUA0cSDFFmf2mP/KBdFCxa1REaR6GHvMctUj+xRZo9QQ==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@sasjs/test-framework/-/test-framework-1.4.0.tgz",
"integrity": "sha512-Pd8PUH5B5RO6q4w3OQXX7aWicvA/CJMXA/FCf2xp332ZTKBb/5uV+HphAOFKpCh58y+ykYYVSV0ZaDO/4t1h3A==",
"requires": {
"@types/react-highlight.js": "^1.0.0",
"immer": "^7.0.7",
"moment": "^2.27.0",
"react-highlight.js": "^1.0.7",
"semantic-ui-css": "^2.4.1",
"semantic-ui-react": "^1.0.0"
},
"dependencies": {
"immer": {
"version": "7.0.7",
"resolved": "https://registry.npmjs.org/immer/-/immer-7.0.7.tgz",
"integrity": "sha512-Q8yYwVADJXrNfp1ZUAh4XDHkcoE3wpdpb4mC5abDSajs2EbW8+cGdPyAnglMyLnm7EF6ojD2xBFX7L5i4TIytw=="
}
}
},
"@semantic-ui-react/event-stack": {
@@ -3747,11 +3755,6 @@
}
}
},
"classnames": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
"integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
},
"clean-css": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
@@ -3829,6 +3832,11 @@
"shallow-clone": "^0.1.2"
}
},
"clsx": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
"integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
},
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -12226,21 +12234,36 @@
}
},
"semantic-ui-react": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-1.0.0.tgz",
"integrity": "sha512-85mYHYuDBNa6la1BgKwuOSD1vcIPsFQEXRxGsZ9pUtE4iHlEcylF+x46NYHIGbBjlys63SpNH3PtK6VyZj9LBw==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-1.2.0.tgz",
"integrity": "sha512-9tNL94nEy16RdupTQNiURyemWUIxtTpQgFimCbOOHRBOe1ApsFz3FWFsrGjv9zFtE7dQMslLYov9BQOelTCVwA==",
"requires": {
"@babel/runtime": "^7.1.2",
"@babel/runtime": "^7.10.5",
"@semantic-ui-react/event-stack": "^3.1.0",
"@stardust-ui/react-component-event-listener": "~0.38.0",
"@stardust-ui/react-component-ref": "~0.38.0",
"classnames": "^2.2.6",
"keyboard-key": "^1.0.4",
"lodash": "^4.17.15",
"clsx": "^1.1.1",
"keyboard-key": "^1.1.0",
"lodash": "^4.17.19",
"prop-types": "^15.7.2",
"react-is": "^16.8.6",
"react-popper": "^1.3.4",
"react-popper": "^1.3.7",
"shallowequal": "^1.1.0"
},
"dependencies": {
"@babel/runtime": {
"version": "7.11.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz",
"integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==",
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
}
}
},
"semver": {

View File

@@ -4,8 +4,8 @@
"homepage": ".",
"private": true,
"dependencies": {
"@sasjs/adapter": "^1.0.5",
"@sasjs/test-framework": "^1.3.0",
"@sasjs/adapter": "^1.3.6",
"@sasjs/test-framework": "^1.4.0",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",

View File

@@ -1,8 +1,8 @@
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
import React from "react";
import { render } from "@testing-library/react";
import App from "./App";
test('renders learn react link', () => {
test("renders learn react link", () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();

View File

@@ -17,7 +17,7 @@ const App = (): ReactElement<{}> => {
sendArrTests(adapter),
sendObjTests(adapter),
specialCaseTests(adapter),
sasjsRequestTests(adapter),
sasjsRequestTests(adapter)
]);
}
}, [adapter, config]);

View File

@@ -12,7 +12,7 @@ const Login = (): ReactElement<{}> => {
(e) => {
e.preventDefault();
appContext.adapter.logIn(username, password).then((res) => {
appContext.setIsLoggedIn(true);
appContext.setIsLoggedIn(res.isLoggedIn);
});
},
[username, password, appContext]

View File

@@ -1,8 +1,7 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans",
"Droid Sans", "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: #1f2027;
@@ -10,8 +9,7 @@ body {
}
* {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
}
input {

View File

@@ -11,9 +11,9 @@
// opt-in, read https://bit.ly/CRA-PWA
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
window.location.hostname === "localhost" ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
window.location.hostname === "[::1]" ||
// 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
@@ -21,7 +21,7 @@ const isLocalhost = Boolean(
);
export function register(config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
@@ -31,7 +31,7 @@ export function register(config) {
return;
}
window.addEventListener('load', () => {
window.addEventListener("load", () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
@@ -42,8 +42,8 @@ export function register(config) {
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://bit.ly/CRA-PWA'
"This web app is being served cache-first by a service " +
"worker. To learn more, visit https://bit.ly/CRA-PWA"
);
});
} else {
@@ -57,21 +57,21 @@ export function register(config) {
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
.then((registration) => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (installingWorker.state === "installed") {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
"New content is available and will be used when all " +
"tabs for this page are closed. See https://bit.ly/CRA-PWA."
);
// Execute callback
@@ -82,7 +82,7 @@ function registerValidSW(swUrl, config) {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
console.log("Content is cached for offline use.");
// Execute callback
if (config && config.onSuccess) {
@@ -93,25 +93,25 @@ function registerValidSW(swUrl, config) {
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
.catch((error) => {
console.error("Error during service worker registration:", error);
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { 'Service-Worker': 'script' },
headers: { "Service-Worker": "script" }
})
.then(response => {
.then((response) => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
const contentType = response.headers.get("content-type");
if (
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
(contentType != null && contentType.indexOf("javascript") === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
navigator.serviceWorker.ready.then((registration) => {
registration.unregister().then(() => {
window.location.reload();
});
@@ -123,18 +123,18 @@ function checkValidServiceWorker(swUrl, config) {
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
"No internet connection found. App is running in offline mode."
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.ready
.then(registration => {
.then((registration) => {
registration.unregister();
})
.catch(error => {
.catch((error) => {
console.error(error.message);
});
}

View File

@@ -2,4 +2,4 @@
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';
import "@testing-library/jest-dom/extend-expect";

View File

@@ -9,7 +9,7 @@ const defaultConfig: SASjsConfig = {
serverType: ServerType.SASViya,
debug: true,
contextName: "SAS Job Execution compute context",
useComputeApi: false,
useComputeApi: false
};
const customConfig = {
@@ -18,7 +18,7 @@ const customConfig = {
pathSASViya: "viya",
appLoc: "/Public/seedapp",
serverType: ServerType.SAS9,
debug: false,
debug: false
};
export const basicTests = (
@@ -35,7 +35,7 @@ export const basicTests = (
return adapter.logIn(userName, password);
},
assertion: (response: any) =>
response && response.isLoggedIn && response.userName === userName,
response && response.isLoggedIn && response.userName === userName
},
{
title: "Default config",
@@ -54,7 +54,7 @@ export const basicTests = (
sasjsConfig.serverType === defaultConfig.serverType &&
sasjsConfig.debug === defaultConfig.debug
);
},
}
},
{
title: "Custom config",
@@ -72,7 +72,7 @@ export const basicTests = (
sasjsConfig.serverType === customConfig.serverType &&
sasjsConfig.debug === customConfig.debug
);
},
}
},
{
title: "Config overrides",
@@ -92,7 +92,7 @@ export const basicTests = (
sasjsConfig.serverType === defaultConfig.serverType &&
sasjsConfig.debug === false
);
},
},
],
}
}
]
});

View File

@@ -4,7 +4,7 @@ import { TestSuite } from "@sasjs/test-framework";
const stringData: any = { table1: [{ col1: "first col value" }] };
const numericData: any = { table1: [{ col1: 3.14159265 }] };
const multiColumnData: any = {
table1: [{ col1: 42, col2: 1.618, col3: "x", col4: "x" }],
table1: [{ col1: 42, col2: 1.618, col3: "x", col4: "x" }]
};
const multipleRowsWithNulls: any = {
table1: [
@@ -12,8 +12,8 @@ const multipleRowsWithNulls: any = {
{ col1: 42, col2: null, col3: "x", col4: "" },
{ col1: 42, col2: null, col3: "x", col4: "" },
{ col1: 42, col2: 1.62, col3: "x", col4: "x" },
{ col1: 42, col2: 1.62, col3: "x", col4: "x" },
],
{ col1: 42, col2: 1.62, col3: "x", col4: "x" }
]
};
const multipleColumnsWithNulls: any = {
table1: [
@@ -21,8 +21,8 @@ const multipleColumnsWithNulls: any = {
{ col1: 42, col2: null, col3: "x", col4: null },
{ col1: 42, col2: null, col3: "x", col4: null },
{ col1: 42, col2: null, col3: "x", col4: "" },
{ col1: 42, col2: null, col3: "x", col4: "" },
],
{ col1: 42, col2: null, col3: "x", col4: "" }
]
};
const getLongStringData = (length = 32764) => {
@@ -55,7 +55,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
},
assertion: (res: any) => {
return res.table1[0][0] === stringData.table1[0].col1;
},
}
},
{
title: "Long string value",
@@ -67,7 +67,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
assertion: (res: any) => {
const longStringData = getLongStringData();
return res.table1[0][0] === longStringData.table1[0].col1;
},
}
},
{
title: "Overly long string value",
@@ -79,7 +79,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
},
assertion: (error: any) => {
return !!error && !!error.MESSAGE;
},
}
},
{
title: "Single numeric value",
@@ -89,7 +89,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
},
assertion: (res: any) => {
return res.table1[0][0] === numericData.table1[0].col1;
},
}
},
{
title: "Multiple columns",
@@ -104,7 +104,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
res.table1[0][2] === multiColumnData.table1[0].col3 &&
res.table1[0][3] === multiColumnData.table1[0].col4
);
},
}
},
{
title: "Multiple rows with nulls",
@@ -129,7 +129,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
res.table1[index][3] === multipleRowsWithNulls.table1[index].col4;
});
return result;
},
}
},
{
title: "Multiple columns with nulls",
@@ -158,9 +158,9 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
(multipleColumnsWithNulls.table1[index].col4 || "");
});
return result;
},
},
],
}
}
]
});
export const sendObjTests = (adapter: SASjs): TestSuite => ({
@@ -171,11 +171,11 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
description: "Should throw an error",
test: async () => {
const invalidData: any = {
"1 invalid table": [{ col1: 42 }],
"1 invalid table": [{ col1: 42 }]
};
return adapter.request("common/sendObj", invalidData).catch((e) => e);
},
assertion: (error: any) => !!error && !!error.MESSAGE,
assertion: (error: any) => !!error && !!error.MESSAGE
},
{
title: "Single string value",
@@ -185,7 +185,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
},
assertion: (res: any) => {
return res.table1[0].COL1 === stringData.table1[0].col1;
},
}
},
{
title: "Long string value",
@@ -197,7 +197,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
assertion: (res: any) => {
const longStringData = getLongStringData();
return res.table1[0].COL1 === longStringData.table1[0].col1;
},
}
},
{
title: "Overly long string value",
@@ -210,7 +210,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
},
assertion: (error: any) => {
return !!error && !!error.MESSAGE;
},
}
},
{
title: "Single numeric value",
@@ -220,7 +220,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
},
assertion: (res: any) => {
return res.table1[0].COL1 === numericData.table1[0].col1;
},
}
},
{
@@ -232,7 +232,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
assertion: (res: any) => {
const data = getLargeObjectData();
return res.table1[9000].BIG === data.table1[9000].big;
},
}
},
{
title: "Multiple columns",
@@ -247,7 +247,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
res.table1[0].COL3 === multiColumnData.table1[0].col3 &&
res.table1[0].COL4 === multiColumnData.table1[0].col4
);
},
}
},
{
title: "Multiple rows with nulls",
@@ -272,7 +272,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
res.table1[index].COL4 === multipleRowsWithNulls.table1[index].col4;
});
return result;
},
}
},
{
title: "Multiple columns with nulls",
@@ -301,7 +301,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
(multipleColumnsWithNulls.table1[index].col4 || "");
});
return result;
},
},
],
}
}
]
});

View File

@@ -19,7 +19,32 @@ export const sasjsRequestTests = (adapter: SASjs): TestSuite => ({
} else {
return requests[0].SASWORK === null;
}
},
}
},
],
{
title: "Make error and capture log",
description: "Should make an error and capture log",
test: async () => {
return new Promise(async (resolve, reject) => {
adapter
.request("common/makeErr", data)
.then((res) => {
//no action here, this request must throw error
})
.catch((err) => {
let sasRequests = adapter.getSasRequests();
let makeErrRequest =
sasRequests.find((req) =>
req.serviceLink.includes("makeErr")
) || null;
resolve(!!makeErrRequest);
});
});
},
assertion: (response) => {
return response;
}
}
]
});

View File

@@ -13,9 +13,9 @@ const specialCharData: any = {
doubleQuote: '"',
crlf: "\r\n",
euro: "€euro",
banghash: "!#banghash",
},
],
banghash: "!#banghash"
}
]
};
const moreSpecialCharData: any = {
@@ -31,9 +31,9 @@ const moreSpecialCharData: any = {
sigma: "Σsigma",
at: "@at",
serbian: "Српски",
dollar: "$",
},
],
dollar: "$"
}
]
};
const getWideData = () => {
@@ -43,7 +43,7 @@ const getWideData = () => {
}
const data: any = {
table1: [cols],
table1: [cols]
};
return data;
@@ -67,7 +67,7 @@ const getLargeDataset = () => {
}
const data: any = {
table1: rows,
table1: rows
};
return data;
@@ -75,7 +75,7 @@ const getLargeDataset = () => {
const errorAndCsrfData: any = {
error: [{ col1: "q", col2: "w", col3: "e", col4: "r" }],
_csrf: [{ col1: "q", col2: "w", col3: "e", col4: "r" }],
_csrf: [{ col1: "q", col2: "w", col3: "e", col4: "r" }]
};
export const specialCaseTests = (adapter: SASjs): TestSuite => ({
@@ -100,7 +100,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
res.table1[0][8] === specialCharData.table1[0].euro &&
res.table1[0][9] === specialCharData.table1[0].banghash
);
},
}
},
{
title: "Other special characters",
@@ -122,7 +122,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
res.table1[0][9] === moreSpecialCharData.table1[0].serbian &&
res.table1[0][10] === moreSpecialCharData.table1[0].dollar
);
},
}
},
{
title: "Wide table with sendArr",
@@ -138,7 +138,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
result && res.table1[0][i] === data.table1[0]["col" + (i + 1)];
}
return result;
},
}
},
{
title: "Wide table with sendObj",
@@ -155,7 +155,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
res.table1[0]["COL" + (i + 1)] === data.table1[0]["col" + (i + 1)];
}
return result;
},
}
},
{
title: "Multiple tables",
@@ -175,7 +175,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
res.table50[0][2] === data.table50[0].col3 &&
res.table50[0][3] === data.table50[0].col4
);
},
}
},
{
title: "Large dataset with sendObj",
@@ -190,7 +190,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
result = result && res.table1[i][0] === data.table1[i][0];
}
return result;
},
}
},
{
title: "Large dataset with sendArr",
@@ -206,7 +206,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
result && res.table1[i][0] === Object.values(data.table1[i])[0];
}
return result;
},
}
},
{
title: "Error and _csrf tables with sendArr",
@@ -225,7 +225,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
res._csrf[0][2] === errorAndCsrfData._csrf[0].col3 &&
res._csrf[0][3] === errorAndCsrfData._csrf[0].col4
);
},
}
},
{
title: "Error and _csrf tables with sendObj",
@@ -244,7 +244,7 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
res._csrf[0].COL3 === errorAndCsrfData._csrf[0].col3 &&
res._csrf[0].COL4 === errorAndCsrfData._csrf[0].col4
);
},
},
],
}
}
]
});

View File

@@ -1,97 +1,100 @@
import { isLogInRequired, needsRetry } from "./utils";
import { CsrfToken } from "./types/CsrfToken";
import { UploadFile } from "./types/UploadFile";
import { isLogInRequired, needsRetry } from './utils'
import { CsrfToken } from './types/CsrfToken'
import { UploadFile } from './types/UploadFile'
const requestRetryLimit = 5;
const requestRetryLimit = 5
export class FileUploader {
constructor(
private appLoc: string,
private serverUrl: string,
private jobsPath: string,
private setCsrfTokenWeb: any,
private csrfToken: CsrfToken | null = null
) {}
private retryCount = 0;
private retryCount = 0
public uploadFile(sasJob: string, files: UploadFile[], params: any) {
if (files?.length < 1) throw new Error("Atleast one file must be provided");
if (files?.length < 1) throw new Error('Atleast one file must be provided')
let paramsString = "";
let paramsString = ''
for (let param in params) {
if (params.hasOwnProperty(param)) {
paramsString += `&${param}=${params[param]}`;
paramsString += `&${param}=${params[param]}`
}
}
const program = this.appLoc
? this.appLoc.replace(/\/?$/, "/") + sasJob.replace(/^\//, "")
: sasJob;
? this.appLoc.replace(/\/?$/, '/') + sasJob.replace(/^\//, '')
: sasJob
const uploadUrl = `${this.serverUrl}${this.jobsPath}/?${
"_program=" + program
}${paramsString}`;
'_program=' + program
}${paramsString}`
const headers = {
"cache-control": "no-cache",
};
'cache-control': 'no-cache'
}
return new Promise((resolve, reject) => {
const formData = new FormData();
const formData = new FormData()
for (let file of files) {
formData.append("file", file.file, file.fileName);
formData.append('file', file.file, file.fileName)
}
if (this.csrfToken) formData.append("_csrf", this.csrfToken.value);
if (this.csrfToken) formData.append('_csrf', this.csrfToken.value)
fetch(uploadUrl, {
method: "POST",
method: 'POST',
body: formData,
referrerPolicy: "same-origin",
headers,
referrerPolicy: 'same-origin',
headers
})
.then(async (response) => {
if (!response.ok) {
if (response.status === 403) {
const tokenHeader = response.headers.get("X-CSRF-HEADER");
const tokenHeader = response.headers.get('X-CSRF-HEADER')
if (tokenHeader) {
const token = response.headers.get(tokenHeader);
const token = response.headers.get(tokenHeader)
this.csrfToken = {
headerName: tokenHeader,
value: token || "",
};
value: token || ''
}
this.setCsrfTokenWeb(this.csrfToken)
}
}
}
return response.text();
return response.text()
})
.then((responseText) => {
if (isLogInRequired(responseText))
reject("You must be logged in to upload a fle");
reject('You must be logged in to upload a fle')
if (needsRetry(responseText)) {
if (this.retryCount < requestRetryLimit) {
this.retryCount++;
this.retryCount++
this.uploadFile(sasJob, files, params).then(
(res: any) => resolve(res),
(err: any) => reject(err)
);
)
} else {
this.retryCount = 0;
reject(responseText);
this.retryCount = 0
reject(responseText)
}
} else {
this.retryCount = 0;
this.retryCount = 0
try {
resolve(JSON.parse(responseText));
resolve(JSON.parse(responseText))
} catch (e) {
reject(e);
reject(e)
}
}
});
});
})
})
}
}

View File

@@ -10,8 +10,8 @@ export class SAS9ApiClient {
*/
public getConfig() {
return {
serverUrl: this.serverUrl,
};
serverUrl: this.serverUrl
}
}
/**
@@ -19,7 +19,7 @@ export class SAS9ApiClient {
* @param serverUrl - the URL of the server.
*/
public setConfig(serverUrl: string) {
if (serverUrl) this.serverUrl = serverUrl;
if (serverUrl) this.serverUrl = serverUrl
}
/**
@@ -33,19 +33,19 @@ export class SAS9ApiClient {
serverName: string,
repositoryName: string
) {
const requestPayload = linesOfCode.join("\n");
const requestPayload = linesOfCode.join('\n')
const executeScriptRequest = {
method: "PUT",
method: 'PUT',
headers: {
Accept: "application/json",
Accept: 'application/json'
},
body: `command=${requestPayload}`,
};
body: `command=${requestPayload}`
}
const executeScriptResponse = await fetch(
`${this.serverUrl}/sas/servers/${serverName}/cmd?repositoryName=${repositoryName}`,
executeScriptRequest
).then((res) => res.text());
).then((res) => res.text())
return executeScriptResponse;
return executeScriptResponse
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,36 +1,36 @@
import SASjs from "./index";
import SASjs from './index'
const adapter = new SASjs();
const adapter = new SASjs()
it("should parse SAS9 source code", async done => {
expect(sampleResponse).toBeTruthy();
const parsedSourceCode = (adapter as any).parseSAS9SourceCode(sampleResponse);
expect(parsedSourceCode).toBeTruthy();
const sourceCodeLines = parsedSourceCode.split("\r\n");
expect(sourceCodeLines.length).toEqual(5);
expect(sourceCodeLines[0].startsWith("6")).toBeTruthy();
expect(sourceCodeLines[1].startsWith("7")).toBeTruthy();
expect(sourceCodeLines[2].startsWith("8")).toBeTruthy();
expect(sourceCodeLines[3].startsWith("9")).toBeTruthy();
expect(sourceCodeLines[4].startsWith("10")).toBeTruthy();
done();
});
it('should parse SAS9 source code', async (done) => {
expect(sampleResponse).toBeTruthy()
const parsedSourceCode = (adapter as any).parseSAS9SourceCode(sampleResponse)
expect(parsedSourceCode).toBeTruthy()
const sourceCodeLines = parsedSourceCode.split('\r\n')
expect(sourceCodeLines.length).toEqual(5)
expect(sourceCodeLines[0].startsWith('6')).toBeTruthy()
expect(sourceCodeLines[1].startsWith('7')).toBeTruthy()
expect(sourceCodeLines[2].startsWith('8')).toBeTruthy()
expect(sourceCodeLines[3].startsWith('9')).toBeTruthy()
expect(sourceCodeLines[4].startsWith('10')).toBeTruthy()
done()
})
it("should parse generated code", async done => {
expect(sampleResponse).toBeTruthy();
it('should parse generated code', async (done) => {
expect(sampleResponse).toBeTruthy()
const parsedGeneratedCode = (adapter as any).parseGeneratedCode(
sampleResponse
);
expect(parsedGeneratedCode).toBeTruthy();
const generatedCodeLines = parsedGeneratedCode.split("\r\n");
expect(generatedCodeLines.length).toEqual(5);
expect(generatedCodeLines[0].startsWith("MPRINT(MM_WEBIN)")).toBeTruthy();
expect(generatedCodeLines[1].startsWith("MPRINT(MM_WEBLEFT)")).toBeTruthy();
expect(generatedCodeLines[2].startsWith("MPRINT(MM_WEBOUT)")).toBeTruthy();
expect(generatedCodeLines[3].startsWith("MPRINT(MM_WEBRIGHT)")).toBeTruthy();
expect(generatedCodeLines[4].startsWith("MPRINT(MM_WEBOUT)")).toBeTruthy();
done();
});
)
expect(parsedGeneratedCode).toBeTruthy()
const generatedCodeLines = parsedGeneratedCode.split('\r\n')
expect(generatedCodeLines.length).toEqual(5)
expect(generatedCodeLines[0].startsWith('MPRINT(MM_WEBIN)')).toBeTruthy()
expect(generatedCodeLines[1].startsWith('MPRINT(MM_WEBLEFT)')).toBeTruthy()
expect(generatedCodeLines[2].startsWith('MPRINT(MM_WEBOUT)')).toBeTruthy()
expect(generatedCodeLines[3].startsWith('MPRINT(MM_WEBRIGHT)')).toBeTruthy()
expect(generatedCodeLines[4].startsWith('MPRINT(MM_WEBOUT)')).toBeTruthy()
done()
})
/* tslint:disable */
const sampleResponse = `<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"/>
@@ -44,5 +44,5 @@ MPRINT(MM_WEBLEFT): filename _temp temp lrecl=999999;
MPRINT(MM_WEBOUT): data _null_;
MPRINT(MM_WEBRIGHT): file _temp;
MPRINT(MM_WEBOUT): if upcase(symget('_debug'))='LOG' then put '&gt;&gt;weboutBEGIN&lt;&lt;';
`;
`
/* tslint:enable */

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
import { Session, Context, CsrfToken } from "./types";
import { asyncForEach, makeRequest } from "./utils";
import { Session, Context, CsrfToken } from './types'
import { asyncForEach, makeRequest } from './utils'
const MAX_SESSION_COUNT = 1;
const MAX_SESSION_COUNT = 1
export class SessionManager {
constructor(
@@ -9,79 +9,103 @@ export class SessionManager {
private contextName: string,
private setCsrfToken: (csrfToken: CsrfToken) => void
) {}
private sessions: Session[] = [];
private currentContext: Context | null = null;
private csrfToken: CsrfToken | null = null;
private sessions: Session[] = []
private currentContext: Context | null = null
private csrfToken: CsrfToken | null = null
async getSession(accessToken?: string) {
await this.createSessions(accessToken);
this.createAndWaitForSession(accessToken);
return this.sessions.pop();
await this.createSessions(accessToken)
this.createAndWaitForSession(accessToken)
const session = this.sessions.pop()
const secondsSinceSessionCreation =
(new Date().getTime() - new Date(session!.creationTimeStamp).getTime()) /
1000
if (
secondsSinceSessionCreation >= session!.attributes.sessionInactiveTimeout
) {
await this.createSessions(accessToken)
const freshSession = this.sessions.pop()
return freshSession
}
return session
}
async clearSession(id: string, accessToken?: string) {
const deleteSessionRequest = {
method: 'DELETE',
headers: this.getHeaders(accessToken)
}
return await this.request<Session>(
`${this.serverUrl}/compute/sessions/${id}`,
deleteSessionRequest
).then(() => {
this.sessions = this.sessions.filter((s) => s.id !== id)
})
}
private async createSessions(accessToken?: string) {
if (!this.sessions.length) {
if (!this.currentContext) {
await this.setCurrentContext(accessToken);
await this.setCurrentContext(accessToken)
}
await asyncForEach(new Array(MAX_SESSION_COUNT), async () => {
const createdSession = await this.createAndWaitForSession(accessToken);
this.sessions.push(createdSession);
});
const createdSession = await this.createAndWaitForSession(accessToken)
this.sessions.push(createdSession)
})
}
}
private async createAndWaitForSession(accessToken?: string) {
const createSessionRequest = {
method: "POST",
headers: this.getHeaders(accessToken),
};
method: 'POST',
headers: this.getHeaders(accessToken)
}
const { result: createdSession, etag } = await this.request<Session>(
`${this.serverUrl}/compute/contexts/${this.currentContext!.id}/sessions`,
createSessionRequest
);
)
await this.waitForSession(createdSession, etag);
this.sessions.push(createdSession);
return createdSession;
await this.waitForSession(createdSession, etag)
this.sessions.push(createdSession)
return createdSession
}
private async setCurrentContext(accessToken?: string) {
if (!this.currentContext) {
const { result: contexts } = await this.request<{
items: Context[];
items: Context[]
}>(`${this.serverUrl}/compute/contexts`, {
headers: this.getHeaders(accessToken),
});
headers: this.getHeaders(accessToken)
})
const contextsList =
contexts && contexts.items && contexts.items.length
? contexts.items
: [];
: []
const currentContext = contextsList.find(
(c: any) => c.name === this.contextName
);
)
if (!currentContext) {
throw new Error(
`The context ${this.contextName} was not found on the server ${this.serverUrl}`
);
)
}
this.currentContext = currentContext;
this.currentContext = currentContext
}
}
private getHeaders(accessToken?: string) {
const headers: any = {
"Content-Type": "application/json",
};
'Content-Type': 'application/json'
}
if (accessToken) {
headers.Authorization = `Bearer ${accessToken}`;
headers.Authorization = `Bearer ${accessToken}`
}
return headers;
return headers
}
private async waitForSession(
@@ -90,57 +114,57 @@ export class SessionManager {
accessToken?: string,
silent = false
) {
let sessionState = session.state;
let sessionState = session.state
const headers: any = {
...this.getHeaders(accessToken),
"If-None-Match": etag,
};
const stateLink = session.links.find((l: any) => l.rel === "state");
'If-None-Match': etag
}
const stateLink = session.links.find((l: any) => l.rel === 'state')
return new Promise(async (resolve, _) => {
if (sessionState === "pending") {
if (sessionState === 'pending') {
if (stateLink) {
if (!silent) {
console.log("Polling session status... \n");
console.log('Polling session status... \n')
}
const { result: state } = await this.request<string>(
`${this.serverUrl}${stateLink.href}?wait=30`,
{
headers,
headers
},
"text"
);
'text'
)
sessionState = state.trim();
sessionState = state.trim()
if (!silent) {
console.log(`Current state: ${sessionState}\n`);
console.log(`Current state: ${sessionState}\n`)
}
resolve(sessionState);
resolve(sessionState)
}
} else {
resolve(sessionState);
resolve(sessionState)
}
});
})
}
private async request<T>(
url: string,
options: RequestInit,
contentType: "text" | "json" = "json"
contentType: 'text' | 'json' = 'json'
) {
if (this.csrfToken) {
options.headers = {
...options.headers,
[this.csrfToken.headerName]: this.csrfToken.value,
};
[this.csrfToken.headerName]: this.csrfToken.value
}
}
return await makeRequest<T>(
url,
options,
(token) => {
this.csrfToken = token;
this.setCsrfToken(token);
this.csrfToken = token
this.setCsrfToken(token)
},
contentType
);
)
}
}

View File

@@ -1,5 +1,5 @@
import SASjs from "./SASjs";
export * from "./types";
export * from "./SASViyaApiClient";
export * from "./SAS9ApiClient";
export default SASjs;
import SASjs from './SASjs'
export * from './types'
export * from './SASViyaApiClient'
export * from './SAS9ApiClient'
export default SASjs

View File

@@ -1,6 +1,6 @@
export interface Context {
name: string;
id: string;
createdBy: string;
version: number;
name: string
id: string
createdBy: string
version: number
}

View File

@@ -1,4 +1,4 @@
export interface CsrfToken {
headerName: string;
value: string;
headerName: string
value: string
}

View File

@@ -1,7 +1,7 @@
import { Link } from "./Link";
import { Link } from './Link'
export interface Folder {
id: string;
uri: string;
links: Link[];
id: string
uri: string
links: Link[]
}

View File

@@ -1,12 +1,13 @@
import { Link } from "./Link";
import { JobResult } from "./JobResult";
import { Link } from './Link'
import { JobResult } from './JobResult'
export interface Job {
id: string;
name: string;
uri: string;
createdBy: string;
links: Link[];
results: JobResult;
error?: any;
id: string
name: string
uri: string
createdBy: string
code?: string
links: Link[]
results: JobResult
error?: any
}

View File

@@ -1,3 +1,3 @@
export interface JobDefinition {
code: string;
code: string
}

View File

@@ -1,3 +1,3 @@
export interface JobResult {
"_webout.json": string;
'_webout.json': string
}

View File

@@ -1,7 +1,7 @@
export interface Link {
method: string;
rel: string;
href: string;
uri: string;
type: string;
method: string
rel: string
href: string
uri: string
type: string
}

View File

@@ -1,4 +1,4 @@
import { ServerType } from "./ServerType";
import { ServerType } from './ServerType'
/**
* Specifies the configuration for the SASjs instance.
@@ -10,22 +10,22 @@ export class SASjsConfig {
* Can be omitted, eg if serving directly from the SAS Web Server or being
* streamed.
*/
serverUrl: string = "";
pathSAS9: string = "";
pathSASViya: string = "";
serverUrl: string = ''
pathSAS9: string = ''
pathSASViya: string = ''
/**
* The appLoc is the parent folder under which the SAS services (STPs or Job
* Execution Services) are stored.
*/
appLoc: string = "";
appLoc: string = ''
/**
* Can be SAS9 or SASVIYA
*/
serverType: ServerType | null = null;
serverType: ServerType | null = null
/**
* Set to `true` to enable additional debugging.
*/
debug: boolean = true;
contextName: string = "";
useComputeApi = false;
debug: boolean = true
contextName: string = ''
useComputeApi = false
}

View File

@@ -3,10 +3,10 @@
*
*/
export interface SASjsRequest {
serviceLink: string;
timestamp: Date;
sourceCode: string;
generatedCode: string;
logFile: string;
SASWORK: any;
serviceLink: string
timestamp: Date
sourceCode: string
generatedCode: string
logFile: string
SASWORK: any
}

View File

@@ -4,11 +4,11 @@
*/
export interface SASjsWaitingRequest {
requestPromise: {
promise: any;
resolve: any;
reject: any;
};
SASjob: string;
data: any;
config?: any;
promise: any
resolve: any
reject: any
}
SASjob: string
data: any
config?: any
}

View File

@@ -3,6 +3,6 @@
*
*/
export enum ServerType {
SASViya = "SASVIYA",
SAS9 = "SAS9",
SASViya = 'SASVIYA',
SAS9 = 'SAS9'
}

View File

@@ -1,7 +1,11 @@
import { Link } from "./Link";
import { Link } from './Link'
export interface Session {
id: string;
state: string;
links: Link[];
id: string
state: string
links: Link[]
attributes: {
sessionInactiveTimeout: number
}
creationTimeStamp: string
}

View File

@@ -3,7 +3,6 @@
*
*/
export interface UploadFile {
file: File;
fileName: string;
file: File
fileName: string
}

View File

@@ -1,11 +1,11 @@
export * from "./Context";
export * from "./CsrfToken";
export * from "./Folder";
export * from "./Job";
export * from "./Link";
export * from "./SASjsConfig";
export * from "./SASjsRequest";
export * from "./SASjsWaitingRequest";
export * from "./ServerType";
export * from "./Session";
export * from "./UploadFile";
export * from './Context'
export * from './CsrfToken'
export * from './Folder'
export * from './Job'
export * from './Link'
export * from './SASjsConfig'
export * from './SASjsRequest'
export * from './SASjsWaitingRequest'
export * from './ServerType'
export * from './Session'
export * from './UploadFile'

View File

@@ -1,5 +1,5 @@
export async function asyncForEach(array: any[], callback: any) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
await callback(array[index], index, array)
}
}

View File

@@ -1,9 +1,9 @@
import { SASjsRequest } from "../types/SASjsRequest";
import { SASjsRequest } from '../types/SASjsRequest'
/**
* Comparator for SASjs request timestamps
*
*/
export const compareTimestamps = (a: SASjsRequest, b: SASjsRequest) => {
return b.timestamp.getTime() - a.timestamp.getTime();
};
return b.timestamp.getTime() - a.timestamp.getTime()
}

View File

@@ -3,131 +3,131 @@
* @param data - the JSON object to convert.
*/
export const convertToCSV = (data: any) => {
const replacer = (key: any, value: any) => (value === null ? "" : value);
const headerFields = Object.keys(data[0]);
let csvTest;
let invalidString = false;
const replacer = (key: any, value: any) => (value === null ? '' : value)
const headerFields = Object.keys(data[0])
let csvTest
let invalidString = false
const headers = headerFields.map((field) => {
let firstFoundType: string | null = null;
let hasMixedTypes: boolean = false;
let rowNumError: number = -1;
let firstFoundType: string | null = null
let hasMixedTypes: boolean = false
let rowNumError: number = -1
const longestValueForField = data
.map((row: any, index: number) => {
if (row[field] || row[field] === "") {
if (row[field] || row[field] === '') {
if (firstFoundType) {
let currentFieldType =
row[field] === "" || typeof row[field] === "string"
? "chars"
: "number";
row[field] === '' || typeof row[field] === 'string'
? 'chars'
: 'number'
if (!hasMixedTypes) {
hasMixedTypes = currentFieldType !== firstFoundType;
rowNumError = hasMixedTypes ? index + 1 : -1;
hasMixedTypes = currentFieldType !== firstFoundType
rowNumError = hasMixedTypes ? index + 1 : -1
}
} else {
if (row[field] === "") {
firstFoundType = "chars";
if (row[field] === '') {
firstFoundType = 'chars'
} else {
firstFoundType =
typeof row[field] === "string" ? "chars" : "number";
typeof row[field] === 'string' ? 'chars' : 'number'
}
}
let byteSize;
let byteSize
if (typeof row[field] === "string") {
if (typeof row[field] === 'string') {
let doubleQuotesFound = row[field]
.split("")
.filter((char: any) => char === '"');
.split('')
.filter((char: any) => char === '"')
byteSize = getByteSize(row[field]);
byteSize = getByteSize(row[field])
if (doubleQuotesFound.length > 0) {
byteSize += doubleQuotesFound.length;
byteSize += doubleQuotesFound.length
}
}
return byteSize;
return byteSize
}
})
.sort((a: number, b: number) => b - a)[0];
.sort((a: number, b: number) => b - a)[0]
if (longestValueForField && longestValueForField > 32765) {
invalidString = true;
invalidString = true
}
if (hasMixedTypes) {
console.error(
`Row (${rowNumError}), Column (${field}) has mixed types: ERROR`
);
)
}
return `${field}:${firstFoundType === "chars" ? "$" : ""}${
return `${field}:${firstFoundType === 'chars' ? '$' : ''}${
longestValueForField
? longestValueForField
: firstFoundType === "chars"
? "1"
: "best"
}.`;
});
: firstFoundType === 'chars'
? '1'
: 'best'
}.`
})
if (invalidString) {
return "ERROR: LARGE STRING LENGTH";
return 'ERROR: LARGE STRING LENGTH'
}
csvTest = data.map((row: any) => {
const fields = Object.keys(row).map((fieldName, index) => {
let value;
let containsSpecialChar = false;
const currentCell = row[fieldName];
let value
let containsSpecialChar = false
const currentCell = row[fieldName]
if (JSON.stringify(currentCell).search(/(\\t|\\n|\\r)/gm) > -1) {
value = currentCell.toString();
containsSpecialChar = true;
value = currentCell.toString()
containsSpecialChar = true
} else {
value = JSON.stringify(currentCell, replacer);
value = JSON.stringify(currentCell, replacer)
}
value = value.replace(/\\\\/gm, "\\");
value = value.replace(/\\\\/gm, '\\')
if (containsSpecialChar) {
if (value.includes(",") || value.includes('"')) {
value = '"' + value + '"';
if (value.includes(',') || value.includes('"')) {
value = '"' + value + '"'
}
} else {
if (
!value.includes(",") &&
!value.includes(',') &&
value.includes('"') &&
!value.includes('\\"')
) {
value = value.substring(1, value.length - 1);
value = value.substring(1, value.length - 1)
}
value = value.replace(/\\"/gm, '""');
value = value.replace(/\\"/gm, '""')
}
value = value.replace(/\r\n/gm, "\n");
value = value.replace(/\r\n/gm, '\n')
if (value === "" && headers[index].includes("best")) {
value = ".";
if (value === '' && headers[index].includes('best')) {
value = '.'
}
return value;
});
return fields.join(",");
});
return value
})
return fields.join(',')
})
let finalCSV =
headers.join(",").replace(/,/g, " ") + "\r\n" + csvTest.join("\r\n");
headers.join(',').replace(/,/g, ' ') + '\r\n' + csvTest.join('\r\n')
return finalCSV;
};
return finalCSV
}
const getByteSize = (str: string) => {
let byteSize = str.length;
let byteSize = str.length
for (let i = str.length - 1; i >= 0; i--) {
const code = str.charCodeAt(i);
if (code > 0x7f && code <= 0x7ff) byteSize++;
else if (code > 0x7ff && code <= 0xffff) byteSize += 2;
if (code >= 0xdc00 && code <= 0xdfff) i--; //trail surrogate
const code = str.charCodeAt(i)
if (code > 0x7f && code <= 0x7ff) byteSize++
else if (code > 0x7ff && code <= 0xffff) byteSize += 2
if (code >= 0xdc00 && code <= 0xdfff) i-- //trail surrogate
}
return byteSize;
};
return byteSize
}

View File

@@ -1,33 +1,33 @@
import { convertToCSV } from "./convertToCsv";
import { splitChunks } from "./splitChunks";
import { convertToCSV } from './convertToCsv'
import { splitChunks } from './splitChunks'
export const formatDataForRequest = (data: any) => {
const sasjsTables = [];
let tableCounter = 0;
const result: any = {};
const sasjsTables = []
let tableCounter = 0
const result: any = {}
for (const tableName in data) {
tableCounter++;
sasjsTables.push(tableName);
const csv = convertToCSV(data[tableName]);
if (csv === "ERROR: LARGE STRING LENGTH") {
tableCounter++
sasjsTables.push(tableName)
const csv = convertToCSV(data[tableName])
if (csv === 'ERROR: LARGE STRING LENGTH') {
throw new Error(
"The max length of a string value in SASjs is 32765 characters."
);
'The max length of a string value in SASjs is 32765 characters.'
)
}
// if csv has length more then 16k, send in chunks
if (csv.length > 16000) {
const csvChunks = splitChunks(csv);
const csvChunks = splitChunks(csv)
// append chunks to form data with same key
result[`sasjs${tableCounter}data0`] = csvChunks.length;
result[`sasjs${tableCounter}data0`] = csvChunks.length
csvChunks.forEach((chunk, index) => {
result[`sasjs${tableCounter}data${index + 1}`] = chunk;
});
result[`sasjs${tableCounter}data${index + 1}`] = chunk
})
} else {
result[`sasjs${tableCounter}data`] = csv;
result[`sasjs${tableCounter}data`] = csv
}
}
result["sasjs_tables"] = sasjsTables.join(" ");
result['sasjs_tables'] = sasjsTables.join(' ')
return result;
};
return result
}

View File

@@ -1,14 +1,15 @@
export * from "./asyncForEach";
export * from "./compareTimestamps";
export * from "./convertToCsv";
export * from "./isAuthorizeFormRequired";
export * from "./isLoginRequired";
export * from "./isLoginSuccess";
export * from "./makeRequest";
export * from "./needsRetry";
export * from "./parseAndSubmitAuthorizeForm";
export * from "./parseGeneratedCode";
export * from "./parseSourceCode";
export * from "./parseSasViyaLog";
export * from "./serialize";
export * from "./splitChunks";
export * from './asyncForEach'
export * from './compareTimestamps'
export * from './convertToCsv'
export * from './isAuthorizeFormRequired'
export * from './isLoginRequired'
export * from './isLoginSuccess'
export * from './makeRequest'
export * from './needsRetry'
export * from './parseAndSubmitAuthorizeForm'
export * from './parseGeneratedCode'
export * from './parseSourceCode'
export * from './parseSasViyaLog'
export * from './serialize'
export * from './splitChunks'
export * from './parseWeboutResponse'

View File

@@ -1,3 +1,3 @@
export const isAuthorizeFormRequired = (response: string): boolean => {
return /<form.+action="(.*Logon\/oauth\/authorize[^"]*).*>/gm.test(response);
};
return /<form.+action="(.*Logon\/oauth\/authorize[^"]*).*>/gm.test(response)
}

34
src/utils/isIeOrEdge.ts Normal file
View File

@@ -0,0 +1,34 @@
export function isIEorEdgeOrOldFirefox() {
if (typeof window === 'undefined') {
return false
}
const ua = window.navigator.userAgent
if (ua.indexOf('Firefox') > 0) {
const version = parseInt(
ua.substring(ua.lastIndexOf('Firefox/') + 8, ua.length),
10
)
return version <= 60
}
const msie = ua.indexOf('MSIE ')
if (msie > 0) {
// IE 10 or older => return version number
return true
}
const trident = ua.indexOf('Trident/')
if (trident > 0) {
return true
}
const edge = ua.indexOf('Edge/')
if (edge > 0) {
// Edge (IE 12+) => return version number
return true
}
// other browser
return false
}

View File

@@ -1,5 +1,5 @@
export const isLogInRequired = (response: string): boolean => {
const pattern: RegExp = /<form.+action="(.*Logon[^"]*).*>/gm;
const matches = pattern.test(response);
return matches;
};
const pattern: RegExp = /<form.+action="(.*Logon[^"]*).*>/gm
const matches = pattern.test(response)
return matches
}

View File

@@ -1,2 +1,2 @@
export const isLogInSuccess = (response: string): boolean =>
/You have signed in/gm.test(response);
/You have signed in/gm.test(response)

View File

@@ -1,49 +1,107 @@
import { CsrfToken } from "../types";
import { CsrfToken } from '../types'
import { needsRetry } from './needsRetry'
let retryCount: number = 0
let retryLimit: number = 5
export async function makeRequest<T>(
url: string,
request: RequestInit,
callback: (value: CsrfToken) => any,
contentType: "text" | "json" = "json"
contentType: 'text' | 'json' = 'json'
): Promise<{ result: T; etag: string | null }> {
let retryRequest: any = null
const responseTransform =
contentType === "json"
contentType === 'json'
? (res: Response) => res.json()
: (res: Response) => res.text();
let etag = null;
: (res: Response) => res.text()
let etag = null
const result = await fetch(url, request).then(async (response) => {
if (response.redirected && response.url.includes('SASLogon/login')) {
return Promise.reject({ status: 401 })
}
if (!response.ok) {
if (response.status === 403) {
const tokenHeader = response.headers.get("X-CSRF-HEADER");
const tokenHeader = response.headers.get('X-CSRF-HEADER')
if (tokenHeader) {
const token = response.headers.get(tokenHeader);
const token = response.headers.get(tokenHeader)
callback({
headerName: tokenHeader,
value: token || "",
});
value: token || ''
})
const retryRequest = {
retryRequest = {
...request,
headers: { ...request.headers, [tokenHeader]: token },
};
headers: { ...request.headers, [tokenHeader]: token }
}
return fetch(url, retryRequest).then((res) => {
etag = res.headers.get("ETag");
return responseTransform(res);
});
etag = res.headers.get('ETag')
return responseTransform(res)
})
}
} else {
const body = await response.text();
return Promise.reject({ status: response.status, body });
const body = await response.text()
if (needsRetry(body)) {
if (retryCount < retryLimit) {
retryCount++
let retryResponse = await makeRequest(
url,
retryRequest || request,
callback,
contentType
)
retryCount = 0
etag = retryResponse.etag
return retryResponse.result
} else {
retryCount = 0
throw new Error('Request retry limit exceeded')
}
}
return Promise.reject({ status: response.status, body })
}
} else {
if (response.redirected && response.url.includes("SASLogon/login")) {
const body = await response.text();
return Promise.reject({ status: 401, body });
if (response.status === 204) {
return Promise.resolve()
}
etag = response.headers.get("ETag");
return responseTransform(response);
const responseTransformed = await responseTransform(response)
let responseText = ''
if (typeof responseTransformed === 'string') {
responseText = responseTransformed
} else {
responseText = JSON.stringify(responseTransformed)
}
if (needsRetry(responseText)) {
if (retryCount < retryLimit) {
retryCount++
const retryResponse = await makeRequest(
url,
retryRequest || request,
callback,
contentType
)
retryCount = 0
etag = retryResponse.etag
return retryResponse.result
} else {
retryCount = 0
throw new Error('Request retry limit exceeded')
}
}
etag = response.headers.get('ETag')
return responseTransformed
}
});
return { result, etag };
})
return { result, etag }
}

View File

@@ -1,11 +1,14 @@
export const needsRetry = (responseText: string): boolean => {
return (
(responseText.includes('"errorCode":403') &&
responseText.includes("_csrf") &&
responseText.includes("X-CSRF-TOKEN")) ||
(responseText.includes('"status":403') &&
responseText.includes('"error":"Forbidden"')) ||
(responseText.includes('"status":449') &&
responseText.includes("Authentication success, retry original request"))
);
};
!!responseText &&
((responseText.includes('"errorCode":403') &&
responseText.includes('_csrf') &&
responseText.includes('X-CSRF-TOKEN')) ||
(responseText.includes('"status":403') &&
responseText.includes('"error":"Forbidden"')) ||
(responseText.includes('"status":449') &&
responseText.includes(
'Authentication success, retry original request'
)))
)
}

View File

@@ -2,48 +2,48 @@ export const parseAndSubmitAuthorizeForm = async (
response: string,
serverUrl: string
) => {
let authUrl: string | null = null;
const params: any = {};
let authUrl: string | null = null
const params: any = {}
const responseBody = response.split("<body>")[1].split("</body>")[0];
const bodyElement = document.createElement("div");
bodyElement.innerHTML = responseBody;
const responseBody = response.split('<body>')[1].split('</body>')[0]
const bodyElement = document.createElement('div')
bodyElement.innerHTML = responseBody
const form = bodyElement.querySelector("#application_authorization");
authUrl = form ? serverUrl + form.getAttribute("action") : null;
const form = bodyElement.querySelector('#application_authorization')
authUrl = form ? serverUrl + form.getAttribute('action') : null
const inputs: any = form?.querySelectorAll("input");
const inputs: any = form?.querySelectorAll('input')
for (const input of inputs) {
if (input.name === "user_oauth_approval") {
input.value = "true";
if (input.name === 'user_oauth_approval') {
input.value = 'true'
}
params[input.name] = input.value;
params[input.name] = input.value
}
const formData = new FormData();
const formData = new FormData()
for (const key in params) {
if (params.hasOwnProperty(key)) {
formData.append(key, params[key]);
formData.append(key, params[key])
}
}
return new Promise((resolve, reject) => {
if (authUrl) {
fetch(authUrl, {
method: "POST",
credentials: "include",
method: 'POST',
credentials: 'include',
body: formData,
referrerPolicy: "same-origin",
referrerPolicy: 'same-origin'
})
.then((res) => res.text())
.then((res) => {
resolve(res);
});
resolve(res)
})
} else {
reject("Auth form url is null");
reject('Auth form url is null')
}
});
};
})
}

View File

@@ -1,7 +1,7 @@
export const parseGeneratedCode = (log: string) => {
const startsWith = "MPRINT";
const startsWith = 'MPRINT'
const isGeneratedCodeLine = (line: string) =>
line.trim().startsWith(startsWith);
const logLines = log.split("\n").filter(isGeneratedCodeLine);
return logLines.join("\r\n");
};
line.trim().startsWith(startsWith)
const logLines = log.split('\n').filter(isGeneratedCodeLine)
return logLines.join('\r\n')
}

View File

@@ -1,12 +1,12 @@
export const parseSasViyaLog = (logResponse: { items: any[] }) => {
let log;
let log
try {
log = logResponse.items
? logResponse.items.map((i) => i.line).join("\n")
: JSON.stringify(logResponse);
? logResponse.items.map((i) => i.line).join('\n')
: JSON.stringify(logResponse)
} catch (e) {
console.error("An error has occurred while parsing the log response", e);
log = logResponse;
console.error('An error has occurred while parsing the log response', e)
log = logResponse
}
return log;
};
return log
}

View File

@@ -1,6 +1,6 @@
export const parseSourceCode = (log: string): string => {
const isSourceCodeLine = (line: string) =>
line.trim().substring(0, 10).trimStart().match(/^\d/);
const logLines = log.split("\n").filter(isSourceCodeLine);
return logLines.join("\r\n");
};
line.trim().substring(0, 10).trimStart().match(/^\d/)
const logLines = log.split('\n').filter(isSourceCodeLine)
return logLines.join('\r\n')
}

View File

@@ -0,0 +1,16 @@
export const parseWeboutResponse = (response: string) => {
let sasResponse = ''
if (response.includes('>>weboutBEGIN<<')) {
try {
sasResponse = response
.split('>>weboutBEGIN<<')[1]
.split('>>weboutEND<<')[0]
} catch (e) {
sasResponse = ''
console.error(e)
}
}
return sasResponse
}

View File

@@ -1,15 +1,15 @@
export const serialize = (obj: any) => {
const str: any[] = [];
const str: any[] = []
for (const p in obj) {
if (obj.hasOwnProperty(p)) {
if (obj[p] instanceof Array) {
for (let i = 0, n = obj[p].length; i < n; i++) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p][i]));
str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p][i]))
}
} else {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]))
}
}
}
return str.join("&");
};
return str.join('&')
}

View File

@@ -1,12 +1,12 @@
export const splitChunks = (content: string) => {
const size = 16000;
const size = 16000
const numChunks = Math.ceil(content.length / size);
const chunks = new Array(numChunks);
const numChunks = Math.ceil(content.length / size)
const chunks = new Array(numChunks)
for (let i = 0, o = 0; i < numChunks; ++i, o += size) {
chunks[i] = content.substr(o, size);
chunks[i] = content.substr(o, size)
}
return chunks;
};
return chunks
}