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

Compare commits

..

248 Commits

Author SHA1 Message Date
Allan Bowe
5a478c8936 Merge pull request #594 from sasjs/issue-588
fix: sas9JobExecutor issues
2021-12-03 11:13:15 +00:00
c9ecc1dde4 chore: requestClient naming 2021-12-03 11:54:52 +01:00
bdf9e2fd5b fix: sas9JobExecutor not returning response 2021-11-26 15:32:10 +01:00
Vladislav Parhomchik
0b795b26c0 Merge pull request #589 from sasjs/issue-587
Pinned typedoc version to 0.19.2
2021-11-26 12:47:21 +03:00
96aac0cfa2 fix: Sas9JobExecutor appendRequest 2021-11-24 20:26:02 +01:00
Yury Shkoda
a82e1f33e3 docs: update docs 2021-11-24 11:23:27 +03:00
Yury Shkoda
058c887cd3 chore(docs): pinned typedoc version 2021-11-24 11:19:41 +03:00
Yury Shkoda
cb88376bda Merge pull request #576 from sasjs/update-dependencies
chore(deps): update dependencies
2021-11-01 15:50:51 +03:00
Yury Shkoda
4c8ddeca25 chore(deps): regenerate package-lock with --legacy-peer-deps option 2021-11-01 15:26:31 +03:00
Yury Shkoda
d264a3f239 chore(ci/cd): used node lts/fermium for GH actions 2021-11-01 14:58:27 +03:00
Yury Shkoda
840b1aa1bf chore(deps): regenerate package-lock.json 2021-11-01 14:47:48 +03:00
Yury Shkoda
e26fd307c8 chore(git): merge branch 'master' into update-dependencies 2021-11-01 14:45:38 +03:00
Allan Bowe
62deaf9f03 Merge pull request #581 from sasjs/package-lock-and-node-version
fix: updated package-lock + updated node version for workflow
2021-10-29 09:35:50 +01:00
Saad Jutt
865bf71f7d fix: updated package-lock + updated node version for workflow 2021-10-29 13:32:59 +05:00
Allan Bowe
734c5bccaa Merge pull request #564 from sasjs/issue-555
BREAKING CHANGE: boolean allowInsecure is replaced with Https Agent Config
2021-10-29 09:12:56 +01:00
Saad Jutt
bc82cb5f5e chore: TSDoc comment updated 2021-10-29 00:29:14 +05:00
Allan Bowe
f25c76fdfd fix: merge 2021-10-28 13:50:29 +00:00
Allan Bowe
e649b41e9e Merge pull request #579 from sasjs/sasjs/server-support
chore: removed legacy code
2021-10-28 14:02:17 +01:00
Yury Shkoda
a962979765 chore: removed legacy code 2021-10-28 15:44:17 +03:00
Yury Shkoda
01d76fa66f Merge pull request #574 from sasjs/sasjs/server
Add sasjs/server support
2021-10-28 15:36:45 +03:00
Yury Shkoda
49cfde9f7d chore(sasjs/server): fix deploy endpoint 2021-10-28 15:13:14 +03:00
Yury Shkoda
ce04ffea05 fix(SASjsApiClient): change SASjs Server endpoints 2021-10-28 10:21:15 +03:00
9dc0499f66 chore: allow insecure remove 2021-10-27 13:20:59 +02:00
Allan Bowe
bc1a7dc54f chore: lint fix 2021-10-27 09:53:42 +00:00
Allan Bowe
93c267fd4e Update AuthManager.ts 2021-10-27 12:20:40 +03:00
Yury Shkoda
0457eb6663 fix: fix calls to SASjsApi endpoints 2021-10-27 11:16:35 +03:00
Yury Shkoda
519494718b chore: address PR comments 2021-10-27 11:15:28 +03:00
Vladislav Parhomchik
de5c38f0fb chore(deps): versions fix, authmanager default returns 2021-10-26 15:46:51 +03:00
Vladislav Parhomchik
208470e7d9 chore(deps): update dependencies 2021-10-26 13:06:03 +03:00
Yury Shkoda
0321f77451 chore: update dependencies 2021-10-20 15:08:58 +03:00
Yury Shkoda
6c5fdc01eb chore: merge branch 'sasjs/server' of https://github.com/sasjs/adapter into sasjs/server 2021-10-20 15:05:03 +03:00
Yury Shkoda
2aa0cd8d7a chore: remove tmp utilities 2021-10-20 15:01:21 +03:00
Yury Shkoda
397bc4524f chore: change 'SASBase' to 'SASjs' 2021-10-20 15:00:52 +03:00
Yury Shkoda
8dce9f3e48 chore(npm): update @sasjs/utils version 2021-10-20 14:59:02 +03:00
8e9f1df1ce chore: fixing the error when using in angular, added isNode check for imports 2021-10-12 16:45:39 +02:00
Saad Jutt
ff4915f7f3 chore: comment file removed 2021-10-07 13:47:28 +05:00
Saad Jutt
6ff8eece7b chore: removed httpsAgent type + clean up 2021-10-07 13:45:50 +05:00
Saad Jutt
2849e6ed07 test(RequestClient): updated 2021-10-06 14:06:00 +05:00
Yury Shkoda
90b11fe3fa feat(deploy): add appLoc 2021-10-04 17:01:25 +03:00
Yury Shkoda
147609842d Merge pull request #565 from sasjs/modifying-npmignore
chore: add .all-contributorsrc to .npmignore
2021-10-04 09:17:54 +03:00
Yury Shkoda
dd6f9cd617 chore(npm): add empty line to the end of .npmignore 2021-10-04 09:10:37 +03:00
Saad Jutt
7f590c35da test(RequestClient): fixed to use actual axios 2021-10-01 15:42:04 +05:00
Vladislav Parhomchik
a38de108e3 chore: add .all-contributorsrc to .npmignore 2021-10-01 10:31:30 +03:00
Saad Jutt
e975e7de97 test(RequestClient): specs for communicating with self-signed server 2021-10-01 12:03:44 +05:00
Yury Shkoda
d418a7e971 fix(http): extend valid responce statuses up to 400 2021-09-30 14:33:33 +03:00
Yury Shkoda
a5b5052a5f fix(baseSAS): removed sasjs/server logic 2021-09-30 14:31:54 +03:00
Yury Shkoda
7638595523 chore(git): fix .gitignore 2021-09-30 14:30:47 +03:00
Allan Bowe
70d64f6eec Merge pull request #556 from sasjs/test-framework
chore: bump up test-framework
2021-09-29 22:42:54 +01:00
Saad Jutt
f0ecfa57e5 BREAKING CHANGE: boolean allowInsecure is replaced configuration of Https Agent 2021-09-28 16:02:12 +05:00
Yury Shkoda
5f3416ecd7 chore(utils): add tmpFolder utils 2021-09-28 10:39:54 +03:00
Yury Shkoda
d8b1a72da2 chore(types): add FileTree types 2021-09-28 10:39:18 +03:00
Yury Shkoda
7e64819eb2 feat(sasjs/server): add SASBaseApiClient class 2021-09-28 10:38:29 +03:00
Yury Shkoda
2f1d403af4 chore(deps): use @sasjs/utils tarball 2021-09-28 10:33:11 +03:00
Yury Shkoda
075d410f7d chore(git): ignore tmp folder 2021-09-28 10:31:36 +03:00
Muhammad Saad
f964bcef9e Merge pull request #559 from sasjs/job-executor-bugs
fix: Job executor bugs
2021-09-22 20:08:17 +05:00
Allan Bowe
5784232d4e chore: updating readme 2021-09-20 17:50:18 +00:00
Yury Shkoda
70ecc8b50e Merge pull request #553 from sasjs/update-deps
chore(deps): update dependencies
2021-09-20 15:48:57 +03:00
Saad Jutt
369a035e8a fix(webJobExecutor): append resend request for viya's getJobUri authentication 2021-09-20 13:28:41 +05:00
Saad Jutt
e5655033c1 fix(jobExecutor): appending resend requests before login call 2021-09-20 12:52:49 +05:00
Yury Shkoda
c7af30bfa3 chore(deps): add caret to axios version 2021-09-17 16:36:22 +03:00
Yury Shkoda
c8da3a54cf chore(axios): override default request transformer 2021-09-17 16:29:15 +03:00
Yury Shkoda
100da16803 chore(deps): bump axios to 0.21.4 2021-09-17 16:28:44 +03:00
Allan Bowe
dc91679040 Merge pull request #558 from sasjs/issue-554
fix: viya debug response null due to wrong content-type
2021-09-17 15:18:58 +03:00
28c8ebfc65 fix: viya debug response null due to wrong content-type 2021-09-16 14:58:18 +02:00
Yury Shkoda
0c4d30afe3 Merge pull request #557 from sasjs/dependabot-upd
chore(dependabot): change schedule interval
2021-09-16 14:18:27 +03:00
Yury Shkoda
bc015b72b6 chore(dependabot): change schedule interval 2021-09-16 14:08:53 +03:00
085a3f84e9 chore: bump up test-framework 2021-09-16 10:37:56 +02:00
Yury Shkoda
f241d75f0a chore(deps): pin axios version to 0.21.1 2021-09-14 17:23:19 +03:00
Muhammad Saad
8a883c09f6 Merge pull request #549 from sasjs/improvements-file-uploader
fix: FileUploader extends BaseJobExecutor
2021-09-14 18:47:26 +05:00
Vladislav Parhomchik
42d01b4044 chore(deps): update dependencies 2021-09-14 11:09:47 +03:00
Saad Jutt
15ff90025a fix(fileUploader): added loginCallback 2021-09-14 05:58:40 +05:00
Saad Jutt
10cf4998f5 fix(loginPrompt): z-index added 2021-09-13 18:02:26 +05:00
Saad Jutt
f714f20f29 chore(fileUploader): support loginCallback and re-submit request 2021-09-13 17:45:35 +05:00
Saad Jutt
19adcc3115 chore: FileUploader extends BaseJobExecutor 2021-09-13 17:42:41 +05:00
Muhammad Saad
bb6b25bac7 Merge pull request #548 from sasjs/code-improvements
chore(imprvements): code changes for fileUploader
2021-09-13 17:40:00 +05:00
Saad Jutt
ec9dbd7ad6 chore(imprvements): code changes for fileUploader 2021-09-13 07:30:26 +05:00
Allan Bowe
2cfba99bda Merge pull request #540 from sasjs/issue-532
fix: move SASjsRequest array from BaseJobExecutor class to RequestClient class
2021-09-11 17:17:33 +03:00
Allan Bowe
a181914c36 Merge pull request #522 from sasjs/redirected-login
feat(auth): redirected login
2021-09-10 18:03:24 +03:00
Saad Jutt
539405e249 chore: fixed sasjs-tests build 2021-09-09 15:30:12 +05:00
Krishna Acondy
d9c27efa8d chore(auth-manager): rename variable 2021-09-09 07:51:07 +01:00
Krishna Acondy
2ccc7b5499 chore(request-client): rename method 2021-09-09 07:41:20 +01:00
Saad Jutt
4623b9665b chore: login prompt dialog bug fixed 2021-09-09 11:29:12 +05:00
9c099b899c fix: If the request client has already been instantiated, update config 2021-09-09 11:03:48 +05:00
Saad Jutt
3ae0809ee5 test(AuthManager): improved coverage 2021-09-09 04:37:30 +05:00
d52c5b26a0 chore: merge master into issue-532 2021-09-08 15:16:06 +05:00
Saad Jutt
0ea6e839ac test: added for verifySasLogin 2021-09-08 15:08:55 +05:00
46ef7b19f6 fix: before instantiating RequestClient check if its already instantiated 2021-09-08 15:02:22 +05:00
Saad Jutt
a00bf5ba67 test(AuthManager): specs added for redirected login 2021-09-08 13:04:03 +05:00
Saad Jutt
e0b09adbba chore: code refactor renamed variables/functions 2021-09-08 06:30:53 +05:00
Saad Jutt
19a57dbf6e chore: code refactor renamed variables/functions 2021-09-08 05:49:24 +05:00
Saad Jutt
cd2b32f2f4 test(checkSession): extract username from server response 2021-09-07 06:08:17 +05:00
Saad Jutt
a1f5355d6a chore: fetch username for Redirected-Login and return 2021-09-07 05:26:42 +05:00
Saad Jutt
0972c0deaa chore(merge): Merge branch 'master' into redirected-login 2021-09-07 05:05:51 +05:00
Allan Bowe
e1a5cc9e45 Merge pull request #504 from sasjs/extract-username-while-check-session
fix: while checking session extract username also
2021-09-06 16:22:04 +03:00
73f50c0435 chore: merge master into issue-532 2021-09-06 13:39:58 +05:00
Saad Jutt
351a22cb3c fix: deriving username with full name 2021-09-06 12:52:40 +05:00
Saad Jutt
3ccd35a4e2 fix: if username is not present in SAS9, derive it with full name 2021-09-06 12:42:26 +05:00
Saad Jutt
e4956cc1d4 chore: test corrected for AuthManager 2021-09-05 12:06:27 +05:00
Saad Jutt
291ba51b07 fix(request): handled error case for sas9 server 2021-09-05 11:55:17 +05:00
5ee57f3d07 chore: added jsdoc header 2021-09-03 14:54:35 +05:00
Allan Bowe
ed72c5c48c Merge pull request #536 from sasjs/update-dependencies
chore(deps): update dependencies
2021-09-03 12:18:46 +03:00
146b0715bf fix: set debug: false in config of fileUploader tests 2021-09-03 13:57:39 +05:00
dfc1d567a5 fix: append sasjs requests array from uploadFile 2021-09-03 13:55:49 +05:00
779200f5fc fix: throw error if null or undefined is passed to getValidJson 2021-09-03 13:54:02 +05:00
cf4c4cfca9 fix: move SASjsRequest array from BaseJobExecutor class to RequestClient class 2021-09-03 13:51:58 +05:00
Vladislav Parhomchik
ad4eead4ca chore(deps): update dependencies 2021-09-02 12:47:23 +03:00
Saad Jutt
f40a86f0f6 chore(redirectLogin): onLoggedOut callback should be an async 2021-09-02 13:43:07 +05:00
Muhammad Saad
aa9383a483 Merge pull request #534 from sasjs/package-script-fix
chore(deps): pin typescript version to fix packaging
2021-09-01 06:20:25 +05:00
Saad Jutt
867422f4cc fix: extraResponseAttributes for WebJobExecutor + sasjs-tests 2021-09-01 03:50:55 +05:00
Saad Jutt
2a6e29b5b8 fix: username returns from checkSession 2021-09-01 03:49:44 +05:00
Yury Shkoda
ba105f609c chore(deps): pin typescript version to fix packaging 2021-08-31 14:51:14 +03:00
Saad Jutt
e4d669f9b6 chore: typo fixed userName 2021-08-31 12:58:47 +05:00
Saad Jutt
5edf09e0a7 fix: usernames to lower case 2021-08-31 12:50:04 +05:00
Saad Jutt
5a695f495c chore: login returns username 2021-08-31 12:39:04 +05:00
Saad Jutt
f231edb4a6 chore: redirect login with onLoggedOut callback 2021-08-31 12:36:20 +05:00
Saad Jutt
389ef94cd5 feat(login): redirect mechanism - in page link to open popup 2021-08-28 10:01:20 +05:00
Saad Jutt
4c90f66dbc chore(merge): Merge branch 'master' into redirected-login 2021-08-27 23:34:52 +05:00
ab8643a89a chore: merge master into extract-username-while-check-session 2021-08-27 14:17:20 +05:00
Allan Bowe
b831b93133 Merge pull request #523 from sasjs/checkNodeVersion
chore: check node version before installing packages
2021-08-26 15:17:15 +03:00
Yury Shkoda
0c3aab673a Merge pull request #521 from sasjs/npmignore
chore: updating .npmignore to reduce bundle size (currently 7.7mb)
2021-08-26 15:12:20 +03:00
Saad Jutt
83353326fb test(AuthManager): fixed 2021-08-26 10:44:06 +05:00
Saad Jutt
db7a5d601e fix(login): code refactor + sasjs-tests updated 2021-08-26 10:33:00 +05:00
Saad Jutt
ee977f4fab chore(merge): Merge branch 'master' into extract-username-while-check-session 2021-08-26 09:02:40 +05:00
33e7564e8f chore: add cross icon at the start of error message 2021-08-25 21:26:49 +05:00
Saad Jutt
1a59f95be7 fix: split code to files + corrected usage of loginCallback 2021-08-25 07:55:04 +05:00
77c4c473c1 chore: address requested changes 2021-08-24 11:51:18 +05:00
47ff1a2293 chore: check node version before installing packages 2021-08-23 13:42:37 +05:00
Saad Jutt
97918f301b chore(redirectLogin): centered popup + verifying sas9 login + sasviya login fixes 2021-08-22 03:57:23 +05:00
Krishna Acondy
830a907bd1 feat(login): add redirected login mechanism 2021-08-21 21:36:50 +01:00
Allan Bowe
ffae344476 chore: updating .npmignore to reduce bundle size (currently 7.7mb) 2021-08-20 23:18:56 +03:00
Allan Bowe
4f62cd0148 Merge pull request #518 from sasjs/loginFix
fix: web request and sas9 login
2021-08-18 19:17:43 +03:00
bd92c1925e chore: merge main into loginFix, conflicts resolved 2021-08-18 20:37:24 +05:00
Allan Bowe
6c29d7823b Merge pull request #517 from sasjs/issue-506
fix: double parsing issue in sas9 when debug is enabled
2021-08-18 18:22:54 +03:00
3c9f133374 fix: throw error from parseWeboutResponse function if unable to find webout response 2021-08-18 16:33:26 +05:00
e72195ca5d fix: predefine jsonParseArrayError message 2021-08-18 16:09:51 +05:00
3e7ddf59b4 style: lint 2021-08-18 11:43:09 +02:00
cd67fb38dc fix: web request and login 2021-08-18 11:42:34 +02:00
78149e6c54 chore: remove console log statement 2021-08-18 00:27:10 +05:00
63e220c5be fix: double parsing issue in sas9 debug mode fixed 2021-08-18 00:05:52 +05:00
8464e506e0 fix: check for valid json while parsing sas viya debug response 2021-08-18 00:04:30 +05:00
0bc69401e5 chore: refactor code for getValidJson function 2021-08-18 00:02:48 +05:00
47fe7686cb chore: introduced new error types: InvalidJsonError, JsonParseArrayError, WeboutResponseError 2021-08-18 00:01:28 +05:00
Allan Bowe
dd2b3671fd Merge pull request #513 from sasjs/issue-508
fix: handle context name when it's undefined/null or empty string
2021-08-16 14:34:40 +03:00
bd03b2b06d fix: when contextName is falsy value, do not add it to apiUrl in web approach and fallback to default in jes approach 2021-08-15 16:11:50 +05:00
Allan Bowe
2b2b8e6429 Merge pull request #505 from sasjs/fileuploader-quick-fix
fix(fileUploader): parsing debug response for SASVIYA
2021-08-09 18:22:46 +03:00
Allan Bowe
5375d0a208 Update FileUploader.ts 2021-08-09 15:42:29 +03:00
Saad Jutt
f2da84829e fix(fileUploader): parsing debug response for SASVIYA 2021-08-09 17:28:55 +05:00
Saad Jutt
fc1c93957c fix: while checking session extract username also 2021-08-07 07:32:48 +05:00
Yury Shkoda
f172ad66bc Merge pull request #501 from sasjs/cli-issue-862
Allow self-signed certificates in requests to SAS9
2021-08-06 09:25:32 +03:00
Yury Shkoda
046c58bb80 chore(deps): restore package-lock 2021-08-05 15:57:47 +03:00
Yury Shkoda
bf825a4f65 chore(deps): discard versions bump 2021-08-05 15:55:45 +03:00
Yury Shkoda
d58cff9081 chore(deps): bump ts-jest, ts-loader, typedoc, webpack 2021-08-04 16:59:55 +03:00
Yury Shkoda
7ab1964746 feat(insecureRequests): allow self-signed certificates for SAS9 2021-08-04 16:59:03 +03:00
Yury Shkoda
b118280a77 Merge pull request #491 from sasjs/session-state-fix
fix(session): remove retry limit if could not get state
2021-07-29 10:34:50 +03:00
Yury Shkoda
5317c14d54 test(sessionManager): improve test coverage of 'waitForSession' 2021-07-29 10:24:03 +03:00
Yury Shkoda
85fed5cd76 chore(git): Merge branch 'master' of https://github.com/sasjs/adapter into session-state-fix 2021-07-28 11:54:21 +03:00
Yury Shkoda
6f9196c690 refactor(session): make loggedErrors a private property 2021-07-28 09:39:52 +03:00
Allan Bowe
2d0a73e74d Merge pull request #480 from sasjs/issue-477
fix: update error message when folder not found
2021-07-28 08:37:26 +03:00
Yury Shkoda
ac8821baec test(session): add assertion of get request quantity 2021-07-27 16:06:43 +03:00
Yury Shkoda
0b9284e481 refactor(session): improve waitForSession method 2021-07-27 16:03:41 +03:00
Krishna Acondy
7b7a80c502 chore(root-folder-not-found): add test 2021-07-27 08:20:30 +01:00
Krishna Acondy
1ace15a308 fix(root-folder-not-found): create RootFolderNotFoundError class 2021-07-27 07:52:19 +01:00
Allan Bowe
e1b3ef7c8c Merge pull request #495 from sasjs/contributors
chore: contributors
2021-07-26 20:26:15 +03:00
710056bded fix: create a utility throwError and add test case for it 2021-07-26 15:30:19 +05:00
Yury Shkoda
fb7a0f43e1 test(session): added test to cover 304 response 2021-07-26 12:17:19 +03:00
Yury Shkoda
6c901f1c21 chore(session): change log level from error to info 2021-07-26 10:40:15 +03:00
26f008d527 chore: remove console log statement 2021-07-26 11:09:31 +05:00
56ebc7be3b chore: merge master into issue-477 2021-07-26 11:06:13 +05:00
Allan Bowe
0ea66f6d37 Merge pull request #494 from sasjs/fix-browser-issue
fix(browser): only import file I/O functions when running in Node.js environments
2021-07-25 10:00:51 +03:00
Allan Bowe
cb30ed2b98 Merge branch 'master' into contributors 2021-07-24 23:14:16 +03:00
Allan Bowe
dfbe2d8f94 chore: contributors 2021-07-24 21:31:51 +03:00
Krishna Acondy
eac9da22bf chore(test): fix assertion 2021-07-24 10:27:31 +01:00
Krishna Acondy
626fc2e15f fix(path): make log file path platform-agnostic 2021-07-24 09:53:39 +01:00
Krishna Acondy
87e2edbd6c chore(test): fix long poll count 2021-07-24 00:12:11 +01:00
Krishna Acondy
7cf681bea3 chore(tests): fix tests 2021-07-23 22:24:48 +01:00
Krishna Acondy
281a145bef fix(node): only create and write file stream if running in node 2021-07-23 22:24:41 +01:00
Krishna Acondy
15d5f9ec91 chore(paths): fix import paths 2021-07-23 22:24:21 +01:00
Krishna Acondy
0a6c5a0ec4 fix(fs): replace fs imports with locally defined WriteStream interface 2021-07-23 22:24:04 +01:00
Krishna Acondy
2a9526d056 fix(node): add util to check if running in node 2021-07-23 22:23:05 +01:00
Allan Bowe
c2ff28c323 Update PULL_REQUEST_TEMPLATE.md 2021-07-23 13:04:38 +03:00
Yury Shkoda
fbaa2327c6 fix(session): remove retry limit if could not get state 2021-07-23 12:44:34 +03:00
Allan Bowe
50710ee1df Merge pull request #476 from sasjs/issue-170
fix: file upload with debug enabled
2021-07-23 11:41:06 +03:00
Krishna Acondy
062ba91c17 Merge pull request #486 from sasjs/fix-poll-logic
fix(poll): add default poll options
2021-07-22 14:53:03 +01:00
6dd1d47bb2 fix: merge main into issue-477 and fixed conflicts 2021-07-22 16:13:46 +05:00
e70a9645ef fix: remove jwtDecode import statement 2021-07-22 15:56:22 +05:00
aeabc29e55 fix: remove serverurl argument from createFolder method and move decode token to utils project 2021-07-22 15:47:37 +05:00
Krishna Acondy
9600fa2512 fix(poll): add default poll options 2021-07-22 11:31:10 +01:00
Krishna Acondy
7951817480 Merge pull request #485 from sasjs/log-file-paths
fix(stream-log): use filepath if provided
2021-07-22 09:57:31 +01:00
Krishna Acondy
405eea1d6c chore(infra): set minimum node version to 15 2021-07-22 09:41:30 +01:00
Krishna Acondy
e3f189eed4 chore(test): fix test 2021-07-22 09:31:32 +01:00
Krishna Acondy
0bb42c5e3c fix(streamlog): use filepath if provided 2021-07-22 09:25:55 +01:00
Allan Bowe
c02eac196e Merge pull request #483 from sasjs/all-contributors/add-medjedovicm
docs: add medjedovicm as a contributor for code
2021-07-21 18:55:43 +03:00
Allan Bowe
3fb0d863e9 Update README.md 2021-07-21 18:55:01 +03:00
allcontributors[bot]
6d573d3897 docs: create .all-contributorsrc [skip ci] 2021-07-21 15:53:39 +00:00
allcontributors[bot]
33280d7a5b docs: update README.md [skip ci] 2021-07-21 15:53:38 +00:00
Allan Bowe
507722da0d Merge pull request #465 from sasjs/stream-job-logs
feat(stream-logs): Save logs to file during job status poll
2021-07-21 18:49:50 +03:00
Krishna Acondy
c8e029cff4 chore(deps): bump utils 2021-07-21 08:37:45 +01:00
Krishna Acondy
7bd2e31f3b chore(cleanup): remove console logs 2021-07-21 08:13:45 +01:00
Krishna Acondy
cfa0c8b9af chore(refactor): only fetch job if streaming logs, fix tests, add JSDoc comments 2021-07-21 08:12:34 +01:00
Krishna Acondy
df9c1c643f chore(merge): pull in changes from master 2021-07-20 09:26:34 +01:00
Krishna Acondy
5c8d311ae8 chore(streamlog): optimise polling mechanism 2021-07-20 09:25:39 +01:00
e1a76bc45a fix: update error message when folder not found 2021-07-19 21:53:58 +05:00
85e5ade93a fix: handle the case when array is passed in getValidJson method 2021-07-19 13:01:18 +05:00
4a61fb8f7f chore: update variable name from config to ovverrideSasjsConfig 2021-07-19 13:00:06 +05:00
5347aeba09 fix: replace isValidJson with getValidJson 2021-07-18 23:24:22 +05:00
Sabir Hassan
7ac7c5e52b Merge branch 'master' into issue-170 2021-07-18 21:56:33 +05:00
5098342dfe fix: retrieve content from the iframe in first response when viya Web approach used with debug enabled 2021-07-18 21:39:57 +05:00
c69be8ffc3 fix: move parseSasViyaDebugResponse method to utils folder 2021-07-18 21:37:08 +05:00
69999d8e8b fix: update fileUpload method to override existing config 2021-07-18 21:34:16 +05:00
Muhammad Saad
bec4180dcf Merge pull request #467 from sasjs/removed-url-package
fix: removed url package
2021-07-16 17:02:24 +05:00
Saad Jutt
1bb7807c25 chore(merge): Merge branch 'master' into removed-url-package 2021-07-16 04:12:20 +05:00
Allan Bowe
816f1d19d4 Merge pull request #471 from sasjs/windows-tests
Windows tests
2021-07-15 16:22:49 +03:00
d38d032309 chore: readme updates 2021-07-15 13:01:12 +02:00
d2a90c77fd chore: readme update 2021-07-15 10:57:44 +02:00
8a0f14b780 chore: windows fallback 2021-07-15 10:43:30 +02:00
f6cb2c4fac chore: sasjs-tests windows 2021-07-15 10:41:10 +02:00
Krishna Acondy
1594f0c7db chore(merge): pull in changes from master 2021-07-15 07:33:44 +01:00
Allan Bowe
7cb2a43f95 Merge pull request #462 from sasjs/json-fix
fix: invalid json checking
2021-07-14 20:46:16 +03:00
Allan Bowe
6e85c7a588 Merge branch 'master' into json-fix 2021-07-14 20:44:06 +03:00
Allan Bowe
a68f6962fd Merge pull request #450 from sasjs/allanbowe-patch-1
Update README.md
2021-07-14 20:42:09 +03:00
Allan Bowe
a650ba15dd Merge branch 'master' into allanbowe-patch-1 2021-07-14 20:41:51 +03:00
Allan Bowe
6ca1b489fc Merge pull request #466 from sasjs/session-state-fix
fix(session): provide more info if could not get session state
2021-07-14 20:41:25 +03:00
Yury Shkoda
a5c9f11c75 test(session): cover case when could not get session state 2021-07-14 14:17:20 +03:00
Krishna Acondy
1ff3937d11 chore(deps): update dependencies 2021-07-14 08:03:54 +01:00
Krishna Acondy
d4725d2e54 chore(refactor): change property name in PollOptions 2021-07-14 07:50:25 +01:00
Saad Jutt
db578564ba fix: removed url package 2021-07-13 17:11:49 +05:00
Yury Shkoda
d4ebef4290 fix(session): provide more info if could not get session state 2021-07-13 14:50:46 +03:00
Krishna Acondy
b9f368193d chore(refactor): add more tests 2021-07-13 08:12:15 +01:00
Krishna Acondy
4257ec78aa chore(ci): add coverage report to build workflow 2021-07-12 20:45:09 +01:00
Krishna Acondy
a0fbe1a740 chore(ci): add coverage report action 2021-07-12 20:42:49 +01:00
Krishna Acondy
123b9fb535 chore(refactor): split up and add tests for core functionality 2021-07-12 20:31:17 +01:00
Krishna Acondy
f57c7b8f7d chore(deps): up utils version 2021-07-12 20:30:42 +01:00
89590f9a37 chore: only removed 2021-07-12 14:42:19 +02:00
5d61bebc9e fix: isValidJson function returns the JSON parsed 2021-07-12 14:29:43 +02:00
99afa6e7e4 style: lint 2021-07-12 12:58:27 +02:00
b590a9f41b chore(tests): testing the isValidJson function 2021-07-12 12:58:04 +02:00
4466ee30d2 chore: preventing double parse of invalid json check 2021-07-12 11:02:01 +02:00
db372950b4 chore: type fix 2021-07-09 15:17:33 +02:00
46f5e07f11 fix: invalid json checking 2021-07-09 13:36:12 +02:00
Krishna Acondy
1c90f4f455 chore(*): remove log 2021-07-09 09:29:57 +01:00
Krishna Acondy
0114a80e38 chore(execute): add tests for executeScript 2021-07-09 09:17:49 +01:00
Krishna Acondy
13be2f9c70 chore(*): remove unused dependencies and variables, fix imports 2021-07-09 09:17:26 +01:00
Krishna Acondy
e396091aa7 chore(merge): pull in changes from master 2021-07-08 09:04:49 +01:00
Krishna Acondy
a00cb1ebec Merge pull request #461 from sasjs/fix-utils-imports
fix(imports): change imports from main barrel into internal barrels
2021-07-08 08:58:14 +01:00
Krishna Acondy
7b1264d140 fix(imports): change imports from main barrel into internal barrels 2021-07-08 08:46:28 +01:00
Krishna Acondy
04ccbf6843 feat(log): write logs to file when polling for job status 2021-07-07 10:02:14 +01:00
Allan Bowe
369b9fb023 Merge branch 'master' into allanbowe-patch-1 2021-07-05 12:02:01 +03:00
Allan Bowe
929ec6eb1c Update README.md 2021-07-02 12:50:03 +03:00
168 changed files with 24802 additions and 31673 deletions

103
.all-contributorsrc Normal file
View File

@@ -0,0 +1,103 @@
{
"projectName": "adapter",
"projectOwner": "sasjs",
"repoType": "github",
"repoHost": "https://github.com",
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"commitConvention": "angular",
"contributors": [
{
"login": "krishna-acondy",
"name": "Krishna Acondy",
"avatar_url": "https://avatars.githubusercontent.com/u/2980428?v=4",
"profile": "https://krishna-acondy.io/",
"contributions": [
"code",
"infra",
"blog",
"content",
"ideas",
"video"
]
},
{
"login": "YuryShkoda",
"name": "Yury Shkoda",
"avatar_url": "https://avatars.githubusercontent.com/u/25773492?v=4",
"profile": "https://www.erudicat.com/",
"contributions": [
"code",
"infra",
"ideas",
"test",
"video"
]
},
{
"login": "medjedovicm",
"name": "Mihajlo Medjedovic",
"avatar_url": "https://avatars.githubusercontent.com/u/18329105?v=4",
"profile": "https://github.com/medjedovicm",
"contributions": [
"code",
"infra",
"test",
"review"
]
},
{
"login": "allanbowe",
"name": "Allan Bowe",
"avatar_url": "https://avatars.githubusercontent.com/u/4420615?v=4",
"profile": "https://github.com/allanbowe",
"contributions": [
"code",
"review",
"test",
"mentoring",
"maintenance"
]
},
{
"login": "saadjutt01",
"name": "Muhammad Saad ",
"avatar_url": "https://avatars.githubusercontent.com/u/8914650?v=4",
"profile": "https://github.com/saadjutt01",
"contributions": [
"code",
"review",
"test",
"mentoring",
"infra"
]
},
{
"login": "sabhas",
"name": "Sabir Hassan",
"avatar_url": "https://avatars.githubusercontent.com/u/82647447?v=4",
"profile": "https://github.com/sabhas",
"contributions": [
"code",
"review",
"test",
"ideas"
]
},
{
"login": "VladislavParhomchik",
"name": "VladislavParhomchik",
"avatar_url": "https://avatars.githubusercontent.com/u/83717836?v=4",
"profile": "https://github.com/VladislavParhomchik",
"contributions": [
"test",
"review"
]
}
],
"contributorsPerLine": 7,
"skipCi": true
}

View File

@@ -6,7 +6,7 @@ GREEN="\033[1;32m"
# temporary file which holds the message).
commit_message=$(cat "$1")
if (echo "$commit_message" | grep -Eq "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\([a-z0-9 \-\*]+\))?!?: .+$") then
if (echo "$commit_message" | grep -Eq "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\([a-z0-9 -\*]+\))?!?: .+$") then
echo "${GREEN} ✔ Commit message meets Conventional Commit standards"
exit 0
fi

View File

@@ -1,7 +1,7 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10
- package-ecosystem: npm
directory: '/'
schedule:
interval: monthly
open-pull-requests-limit: 10

View File

@@ -13,20 +13,25 @@ jobs:
strategy:
matrix:
node-version: [12.x]
node-version: [lts/fermium]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: npm
- name: Install Dependencies
run: npm ci
- name: Check code style
run: npm run lint
- name: Run unit tests
run: npm test
- name: Generate coverage report
uses: artiomtr/jest-coverage-report-action@v2.0-rc.2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Build Package
run: npm run package:lib
env:

2
.gitignore vendored
View File

@@ -5,4 +5,4 @@ build
/coverage
.DS_Store
.DS_Store

View File

@@ -1,4 +1,6 @@
sasjs-tests/
docs/
.github/
CONTRIBUTING.md
*.md
*.spec.ts
.all-contributorsrc

View File

@@ -12,9 +12,9 @@ What code changes have been made to achieve the intent.
## Checks
No PR (that involves a non-trivial code change) should be merged, unless all four of the items below are confirmed! If an urgent fix is needed - use a tar file.
No PR (that involves a non-trivial code change) should be merged, unless all items below are confirmed! If an urgent fix is needed - use a tar file.
- [ ] Code is formatted correctly (`npm run lint:fix`).
- [ ] All unit tests are passing (`npm test`).
- [ ] All `sasjs-cli` unit tests are passing (`npm test`).
- [ ] 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

View File

@@ -36,7 +36,7 @@ Ok ok. Deploy this [example.html](https://raw.githubusercontent.com/sasjs/adapte
The backend part can be deployed as follows:
```
```sas
%let appLoc=/Public/app/readme; /* Metadata or Viya Folder per SASjs config */
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc; /* compile macros (can also be downloaded & compiled seperately) */
@@ -85,11 +85,11 @@ let sasJs = new SASjs.default(
);
```
If you've installed it via NPM, you can import it as a default import like so:
```
```js
import SASjs from '@sasjs/adapter';
```
You can then instantiate it with:
```
```js
const sasJs = new SASjs({your config})
```
@@ -119,6 +119,7 @@ sasJs.request("/path/to/my/service", dataObject)
console.log(response.tablewith2cols1row[0].COL1.value)
})
```
We supply the path to the SAS service, and a data object. The data object can be null (for services with no input), or can contain one or more tables in the following format:
```javascript
@@ -146,6 +147,7 @@ The adapter will also cache the logs (if debug enabled) and even the work tables
The SAS side is handled by a number of macros in the [macro core](https://github.com/sasjs/core) library.
The following snippet shows the process of SAS tables arriving / leaving:
```sas
/* fetch all input tables sent from frontend - they arrive as work tables */
%webout(FETCH)
@@ -161,7 +163,6 @@ run;
%webout(OBJ,tables,fmt=N) /* unformatted (raw) data */
%webout(OBJ,tables,label=newtable) /* rename tables on export */
%webout(CLOSE) /* close the JSON and send some extra useful variables too */
```
## Configuration
@@ -172,7 +173,8 @@ Configuration on the client side involves passing an object on startup, which ca
* `serverType` - either `SAS9` or `SASVIYA`.
* `serverUrl` - the location (including http protocol and port) of the SAS Server. Can be omitted, eg if serving directly from the SAS Web Server, or in streaming mode.
* `debug` - if `true` then SAS Logs and extra debug information is returned.
* `useComputeApi` - Only relevant when the serverType is `SASVIYA`. If `true` the [Compute API](#using-the-compute-api) is used. If `false` the [JES API](#using-the-jes-api) is used. If `null` or `undefined` the [Web](#using-jes-web-app) approach is used.
* `LoginMechanism` - either `Default` or `Redirected`. If `Redirected` then authentication occurs through the injection of an additional screen, which contains the SASLogon prompt. This allows for more complex authentication flows (such as 2FA) and avoids the need to handle passwords in the application itself. The styling of the redirect flow can also be modified. If left at "Default" then the developer must capture the username and password and use these with the `.login()` method.
* `useComputeApi` - Only relevant when the serverType is `SASVIYA`. If `true` the [Compute API](#using-the-compute-api) is used. If `false` the [JES API](#using-the-jes-api) is used. If `null` or `undefined` the [Web](#using-jes-web-app) approach is used.
* `contextName` - Compute context on which the requests will be called. If missing or not provided, defaults to `Job Execution Compute context`.
The adapter supports a number of approaches for interfacing with Viya (`serverType` is `SASVIYA`). For maximum performance, be sure to [configure your compute context](https://sasjs.io/guide-viya/#shared-account-and-server-re-use) with `reuseServerProcesses` as `true` and a system account in `runServerAs`. This functionality is available since Viya 3.5. This configuration is supported when [creating contexts using the CLI](https://sasjs.io/sasjs-cli-context/#sasjs-context-create).
@@ -194,7 +196,9 @@ Note - to use the web approach, the `useComputeApi` property must be `undefined`
### Using the JES API
Here we are running Jobs using the Job Execution Service except this time we are making the requests directly using the REST API instead of through the JES Web App. This is helpful when we need to call web services outside of a browser (eg with the SASjs CLI or other commandline tools). To save one network request, the adapter prefetches the JOB URIs and passes them in the `__job` parameter. Depending on your network bandwidth, it may or may not be faster than the JES Web approach.
```
This approach (`useComputeApi: false`) also ensures that jobs are displayed in Environment Manager.
```json
{
appLoc:"/Your/Path",
serverType:"SASVIYA",
@@ -206,12 +210,14 @@ Here we are running Jobs using the Job Execution Service except this time we are
### Using the Compute API
This approach is by far the fastest, as a result of the optimisations we have built into the adapter. With this configuration, in the first sasjs request, we take a URI map of the services in the target folder, and create a session manager. This manager will spawn a additional session every time a request is made. Subsequent requests will use the existing 'hot' session, if it exists. Sessions are always deleted after every use, which actually makes this _less_ resource intensive than a typical JES web app, in which all sessions are kept alive by default for 15 minutes.
```
With this approach (`useComputeApi: true`), the requests/logs will _not_ appear in the list in Environment manager.
```json
{
appLoc:"/Your/Path",
serverType:"SASVIYA",
useComputeApi: true,
contextName: 'yourComputeContext'
contextName: "yourComputeContext"
}
```
@@ -230,3 +236,32 @@ If you are a SAS 9 or SAS Viya customer you can also request a copy of [Data Con
If you find this library useful, help us grow our star graph!
![](https://starchart.cc/sasjs/adapter.svg)
## Contributors ✨
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-7-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://krishna-acondy.io/"><img src="https://avatars.githubusercontent.com/u/2980428?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Krishna Acondy</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=krishna-acondy" title="Code">💻</a> <a href="#infra-krishna-acondy" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#blog-krishna-acondy" title="Blogposts">📝</a> <a href="#content-krishna-acondy" title="Content">🖋</a> <a href="#ideas-krishna-acondy" title="Ideas, Planning, & Feedback">🤔</a> <a href="#video-krishna-acondy" title="Videos">📹</a></td>
<td align="center"><a href="https://www.erudicat.com/"><img src="https://avatars.githubusercontent.com/u/25773492?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yury Shkoda</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=YuryShkoda" title="Code">💻</a> <a href="#infra-YuryShkoda" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#ideas-YuryShkoda" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/sasjs/adapter/commits?author=YuryShkoda" title="Tests">⚠️</a> <a href="#video-YuryShkoda" title="Videos">📹</a></td>
<td align="center"><a href="https://github.com/medjedovicm"><img src="https://avatars.githubusercontent.com/u/18329105?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mihajlo Medjedovic</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=medjedovicm" title="Code">💻</a> <a href="#infra-medjedovicm" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/sasjs/adapter/commits?author=medjedovicm" title="Tests">⚠️</a> <a href="https://github.com/sasjs/adapter/pulls?q=is%3Apr+reviewed-by%3Amedjedovicm" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://github.com/allanbowe"><img src="https://avatars.githubusercontent.com/u/4420615?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Allan Bowe</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=allanbowe" title="Code">💻</a> <a href="https://github.com/sasjs/adapter/pulls?q=is%3Apr+reviewed-by%3Aallanbowe" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sasjs/adapter/commits?author=allanbowe" title="Tests">⚠️</a> <a href="#mentoring-allanbowe" title="Mentoring">🧑‍🏫</a> <a href="#maintenance-allanbowe" title="Maintenance">🚧</a></td>
<td align="center"><a href="https://github.com/saadjutt01"><img src="https://avatars.githubusercontent.com/u/8914650?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Muhammad Saad </b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=saadjutt01" title="Code">💻</a> <a href="https://github.com/sasjs/adapter/pulls?q=is%3Apr+reviewed-by%3Asaadjutt01" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sasjs/adapter/commits?author=saadjutt01" title="Tests">⚠️</a> <a href="#mentoring-saadjutt01" title="Mentoring">🧑‍🏫</a> <a href="#infra-saadjutt01" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://github.com/sabhas"><img src="https://avatars.githubusercontent.com/u/82647447?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sabir Hassan</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=sabhas" title="Code">💻</a> <a href="https://github.com/sasjs/adapter/pulls?q=is%3Apr+reviewed-by%3Asabhas" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sasjs/adapter/commits?author=sabhas" title="Tests">⚠️</a> <a href="#ideas-sabhas" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/VladislavParhomchik"><img src="https://avatars.githubusercontent.com/u/83717836?v=4?s=100" width="100px;" alt=""/><br /><sub><b>VladislavParhomchik</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=VladislavParhomchik" title="Tests">⚠️</a> <a href="https://github.com/sasjs/adapter/pulls?q=is%3Apr+reviewed-by%3AVladislavParhomchik" title="Reviewed Pull Requests">👀</a></td>
</tr>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

16
checkNodeVersion.js Normal file
View File

@@ -0,0 +1,16 @@
const result = process.versions
if (result && result.node) {
if (parseInt(result.node) < 14) {
console.log(
'\x1b[31m%s\x1b[0m',
`❌ Process failed due to Node Version,\nPlease install and use Node Version >= 14\nYour current Node Version is: ${result.node}`
)
process.exit(1)
}
} else {
console.log(
'\x1b[31m%s\x1b[0m',
'Something went wrong while checking Node version'
)
process.exit(1)
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

455
docs/modules/api_viya.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

251
docs/modules/test.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

15343
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,8 +3,10 @@
"description": "JavaScript adapter for SAS",
"homepage": "https://adapter.sasjs.io",
"scripts": {
"preinstall": "node checkNodeVersion",
"prebuild": "node checkNodeVersion",
"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 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",
"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}\"",
@@ -38,40 +40,44 @@
},
"license": "ISC",
"devDependencies": {
"@types/jest": "^26.0.23",
"@types/axios": "^0.14.0",
"@types/express": "^4.17.13",
"@types/form-data": "^2.5.0",
"@types/jest": "^27.0.2",
"@types/mime": "^2.0.3",
"@types/tough-cookie": "^4.0.0",
"@types/pem": "^1.9.6",
"@types/tough-cookie": "^4.0.1",
"copyfiles": "^2.4.1",
"cp": "^0.2.0",
"dotenv": "^10.0.0",
"jest": "^27.0.6",
"express": "^4.17.1",
"jest": "^27.2.0",
"jest-extended": "^0.11.5",
"mime": "^2.5.2",
"node-polyfill-webpack-plugin": "^1.1.4",
"path": "^0.12.7",
"pem": "^1.14.4",
"process": "^0.11.10",
"rimraf": "^3.0.2",
"semantic-release": "^17.4.4",
"terser-webpack-plugin": "^5.1.4",
"semantic-release": "^18.0.0",
"terser-webpack-plugin": "^5.2.4",
"ts-jest": "^27.0.3",
"ts-loader": "^9.2.2",
"ts-loader": "^9.2.6",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"typedoc": "^0.21.2",
"typedoc": "0.19.2",
"typedoc-neo-theme": "^1.1.1",
"typedoc-plugin-external-module-name": "^4.0.6",
"typescript": "^4.3.4",
"webpack": "^5.41.1",
"typescript": "4.3.5",
"webpack": "^5.56.0",
"webpack-cli": "^4.7.2"
},
"main": "index.js",
"dependencies": {
"@sasjs/utils": "^2.23.2",
"axios": "^0.21.1",
"@sasjs/utils": "^2.32.0",
"axios": "^0.21.4",
"axios-cookiejar-support": "^1.0.1",
"form-data": "^4.0.0",
"https": "^1.0.0",
"tough-cookie": "^4.0.0",
"url": "^0.11.0"
"tough-cookie": "^4.0.0"
}
}

View File

@@ -41,6 +41,19 @@ So you can run the script like so:
SSH_ACCOUNT=me@my-sas-server.com DEPLOY_PATH=/var/www/html/my-folder/sasjs-tests npm run deploy
```
If you are on `WINDOWS`, you will first need to install one dependency:
```bash
npm i -g copyfiles
```
and then run to build:
```bash
npm run update:adapter && npm run build
```
when it finishes run to deploy:
```bash
scp -rp ./build/* me@my-sas-server.com:/var/www/html/my-folder/sasjs-tests
```
If you'd like to deploy just `sasjs-tests` without changing the adapter version, you can use the `deploy:tests` script, while also setting the same environment variables as above.
## 3. Creating the required SAS services

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
"private": true,
"dependencies": {
"@sasjs/adapter": "file:../build/sasjs-adapter-5.0.0.tgz",
"@sasjs/test-framework": "^1.4.0",
"@sasjs/test-framework": "^1.4.2",
"@types/jest": "^26.0.20",
"@types/node": "^14.14.41",
"@types/react": "^17.0.1",
@@ -23,7 +23,8 @@
"test": "react-scripts test",
"eject": "react-scripts eject",
"update:adapter": "cd .. && npm run package:lib && cd sasjs-tests && npm i ../build/sasjs-adapter-5.0.0.tgz",
"deploy:tests": "rsync -avhe ssh ./build/* --delete $SSH_ACCOUNT:$DEPLOY_PATH",
"deploy:tests": "rsync -avhe ssh ./build/* --delete $SSH_ACCOUNT:$DEPLOY_PATH || npm run deploy:tests-win",
"deploy:tests-win": "scp %DEPLOY_PATH% ./build/*",
"deploy": "npm run update:adapter && npm run build && npm run deploy:tests"
},
"eslintConfig": {

View File

@@ -1,4 +1,4 @@
import SASjs, { SASjsConfig } from '@sasjs/adapter'
import SASjs, { LoginMechanism, SASjsConfig } from '@sasjs/adapter'
import { TestSuite } from '@sasjs/test-framework'
import { ServerType } from '@sasjs/utils/types'
@@ -13,7 +13,7 @@ const defaultConfig: SASjsConfig = {
debug: false,
contextName: 'SAS Job Execution compute context',
useComputeApi: false,
allowInsecureRequests: false
loginMechanism: LoginMechanism.Default
}
const customConfig = {
@@ -41,6 +41,19 @@ export const basicTests = (
assertion: (response: any) =>
response && response.isLoggedIn && response.userName === userName
},
{
title: 'Fetch username for already logged in user',
description: 'Should log the user in',
test: async () => {
await adapter.logIn(userName, password)
const newAdapterIns = new SASjs(adapter.getSasjsConfig())
return await newAdapterIns.checkSession()
},
assertion: (response: any) =>
response?.isLoggedIn && response?.userName === userName
},
{
title: 'Multiple Log in attempts',
description:
@@ -48,7 +61,7 @@ export const basicTests = (
test: async () => {
await adapter.logOut()
await adapter.logIn('invalid', 'invalid')
return adapter.logIn(userName, password)
return await adapter.logIn(userName, password)
},
assertion: (response: any) =>
response && response.isLoggedIn && response.userName === userName
@@ -64,8 +77,8 @@ export const basicTests = (
'common/sendArr',
stringData,
undefined,
() => {
adapter.logIn(userName, password)
async () => {
await adapter.logIn(userName, password)
}
)
},
@@ -151,7 +164,7 @@ export const basicTests = (
description:
'Should complete successful request with extra attributes present in response',
test: async () => {
const config = {
const config: Partial<SASjsConfig> = {
useComputeApi: false
}

View File

@@ -1,86 +0,0 @@
import { isUrl } from './utils'
import { UploadFile } from './types/UploadFile'
import { ErrorResponse, LoginRequiredError } from './types/errors'
import { RequestClient } from './request/RequestClient'
import { ServerType } from '@sasjs/utils/types'
import SASjs from './SASjs'
import { Server } from 'https'
import { SASjsConfig } from './types'
import { config } from 'process'
export class FileUploader {
constructor(
private sasjsConfig: SASjsConfig,
private jobsPath: string,
private requestClient: RequestClient
) {
if (this.sasjsConfig.serverUrl) isUrl(this.sasjsConfig.serverUrl)
}
public uploadFile(sasJob: string, files: UploadFile[], params: any) {
if (files?.length < 1)
return Promise.reject(
new ErrorResponse('At least one file must be provided.')
)
if (!sasJob || sasJob === '')
return Promise.reject(new ErrorResponse('sasJob must be provided.'))
let paramsString = ''
for (let param in params) {
if (params.hasOwnProperty(param)) {
paramsString += `&${param}=${params[param]}`
}
}
const program = this.sasjsConfig.appLoc
? this.sasjsConfig.appLoc.replace(/\/?$/, '/') + sasJob.replace(/^\//, '')
: sasJob
const uploadUrl = `${this.jobsPath}/?${
'_program=' + program
}${paramsString}`
const formData = new FormData()
for (let file of files) {
formData.append('file', file.file, file.fileName)
}
const csrfToken = this.requestClient.getCsrfToken('file')
if (csrfToken) formData.append('_csrf', csrfToken.value)
if (this.sasjsConfig.debug) formData.append('_debug', '131')
if (
this.sasjsConfig.serverType === ServerType.SasViya &&
this.sasjsConfig.contextName
)
formData.append('_contextname', this.sasjsConfig.contextName)
const headers = {
'cache-control': 'no-cache',
Accept: '*/*',
'Content-Type': 'text/plain'
}
return this.requestClient
.post(uploadUrl, formData, undefined, 'application/json', headers)
.then((res) => {
let result
result =
typeof res.result === 'string' ? JSON.parse(res.result) : res.result
return result
//TODO: append to SASjs requests
})
.catch((err: Error) => {
if (err instanceof LoginRequiredError) {
return Promise.reject(
new ErrorResponse('You must be logged in to upload a file.', err)
)
}
return Promise.reject(
new ErrorResponse('File upload request failed.', err)
)
})
}
}

View File

@@ -1,3 +1,4 @@
import * as https from 'https'
import { generateTimestamp } from '@sasjs/utils/time'
import * as NodeFormData from 'form-data'
import { Sas9RequestClient } from './request/Sas9RequestClient'
@@ -10,9 +11,13 @@ import { isUrl } from './utils'
export class SAS9ApiClient {
private requestClient: Sas9RequestClient
constructor(private serverUrl: string, private jobsPath: string) {
constructor(
private serverUrl: string,
private jobsPath: string,
httpsAgentOptions?: https.AgentOptions
) {
if (serverUrl) isUrl(serverUrl)
this.requestClient = new Sas9RequestClient(serverUrl, false)
this.requestClient = new Sas9RequestClient(serverUrl, httpsAgentOptions)
}
/**

View File

@@ -0,0 +1,51 @@
import { Logger, LogLevel } from '@sasjs/utils/logger'
import { RequestClient } from './request/RequestClient'
import { SASViyaApiClient } from './SASViyaApiClient'
import { Folder } from './types'
import { RootFolderNotFoundError } from './types/errors'
const mockFolder: Folder = {
id: '1',
uri: '/folder',
links: [],
memberCount: 1
}
const requestClient = new (<jest.Mock<RequestClient>>RequestClient)()
const sasViyaApiClient = new SASViyaApiClient(
'https://test.com',
'/test',
'test context',
requestClient
)
describe('SASViyaApiClient', () => {
beforeEach(() => {
;(process as any).logger = new Logger(LogLevel.Off)
setupMocks()
})
it('should throw an error when the root folder is not found on the server', async () => {
jest
.spyOn(requestClient, 'get')
.mockImplementation(() => Promise.reject('Not Found'))
const error = await sasViyaApiClient
.createFolder('test', '/foo')
.catch((e) => e)
expect(error).toBeInstanceOf(RootFolderNotFoundError)
})
})
const setupMocks = () => {
jest
.spyOn(requestClient, 'get')
.mockImplementation(() =>
Promise.resolve({ result: mockFolder, etag: '', status: 200 })
)
jest
.spyOn(requestClient, 'post')
.mockImplementation(() =>
Promise.resolve({ result: mockFolder, etag: '', status: 200 })
)
}

View File

@@ -1,10 +1,4 @@
import {
convertToCSV,
isRelativePath,
isUri,
isUrl,
fetchLogByChunks
} from './utils'
import { isRelativePath, isUri, isUrl } from './utils'
import * as NodeFormData from 'form-data'
import {
Job,
@@ -17,28 +11,19 @@ import {
JobDefinition,
PollOptions
} from './types'
import {
ComputeJobExecutionError,
JobExecutionError,
NotFoundError
} from './types/errors'
import { formatDataForRequest } from './utils/formatDataForRequest'
import { JobExecutionError, RootFolderNotFoundError } from './types/errors'
import { SessionManager } from './SessionManager'
import { ContextManager } from './ContextManager'
import {
timestampToYYYYMMDDHHMMSS,
isAccessTokenExpiring,
isRefreshTokenExpiring,
Logger,
LogLevel,
SasAuthResponse,
MacroVar,
AuthConfig
} from '@sasjs/utils'
import { SasAuthResponse, MacroVar, AuthConfig } from '@sasjs/utils/types'
import { isAuthorizeFormRequired } from './auth/isAuthorizeFormRequired'
import { RequestClient } from './request/RequestClient'
import { prefixMessage } from '@sasjs/utils/error'
import * as mime from 'mime'
import { pollJobState } from './api/viya/pollJobState'
import { getTokens } from './auth/getTokens'
import { uploadTables } from './api/viya/uploadTables'
import { executeScript } from './api/viya/executeScript'
import { getAccessToken } from './auth/getAccessToken'
import { refreshTokens } from './auth/refreshTokens'
/**
* A client for interfacing with the SAS Viya REST API.
@@ -66,6 +51,16 @@ export class SASViyaApiClient {
)
private folderMap = new Map<string, Job[]>()
/**
* A helper method used to call appendRequest method of RequestClient
* @param response - response from sasjs request
* @param program - name of program
* @param debug - a boolean that indicates whether debug was enabled or not
*/
public appendRequest(response: any, program: string, debug: boolean) {
this.requestClient!.appendRequest(response, program, debug)
}
public get debug() {
return this._debug
}
@@ -174,13 +169,6 @@ export class SASViyaApiClient {
throw new Error(`Execution context ${contextName} not found.`)
}
const createSessionRequest = {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
}
const { result: createdSession } = await this.requestClient.post<Session>(
`/compute/contexts/${executionContext.id}/sessions`,
{},
@@ -295,249 +283,22 @@ export class SASViyaApiClient {
printPid = false,
variables?: MacroVar
): Promise<any> {
let access_token = (authConfig || {}).access_token
if (authConfig) {
;({ access_token } = await this.getTokens(authConfig))
}
const logger = process.logger || console
try {
let executionSessionId: string
const session = await this.sessionManager
.getSession(access_token)
.catch((err) => {
throw prefixMessage(err, 'Error while getting session. ')
})
executionSessionId = session!.id
if (printPid) {
const { result: jobIdVariable } = await this.sessionManager
.getVariable(executionSessionId, 'SYSJOBID', access_token)
.catch((err) => {
throw prefixMessage(err, 'Error while getting session variable. ')
})
if (jobIdVariable && jobIdVariable.value) {
const relativeJobPath = this.rootFolderName
? jobPath.split(this.rootFolderName).join('').replace(/^\//, '')
: jobPath
const logger = new Logger(debug ? LogLevel.Debug : LogLevel.Info)
logger.info(
`Triggered '${relativeJobPath}' with PID ${
jobIdVariable.value
} at ${timestampToYYYYMMDDHHMMSS()}`
)
}
}
const jobArguments: { [key: string]: any } = {
_contextName: contextName,
_OMITJSONLISTING: true,
_OMITJSONLOG: true,
_OMITSESSIONRESULTS: true,
_OMITTEXTLISTING: true,
_OMITTEXTLOG: true
}
if (debug) {
jobArguments['_OMITTEXTLOG'] = false
jobArguments['_OMITSESSIONRESULTS'] = false
}
let fileName
if (isRelativePath(jobPath)) {
fileName = `exec-${
jobPath.includes('/') ? jobPath.split('/')[1] : jobPath
}`
} else {
const jobPathParts = jobPath.split('/')
fileName = jobPathParts.pop()
}
let jobVariables: any = {
SYS_JES_JOB_URI: '',
_program: isRelativePath(jobPath)
? this.rootFolderName + '/' + jobPath
: jobPath
}
if (variables) jobVariables = { ...jobVariables, ...variables }
if (debug) jobVariables = { ...jobVariables, _DEBUG: 131 }
let files: any[] = []
if (data) {
if (JSON.stringify(data).includes(';')) {
files = await this.uploadTables(data, access_token).catch((err) => {
throw prefixMessage(err, 'Error while uploading tables. ')
})
jobVariables['_webin_file_count'] = files.length
files.forEach((fileInfo, index) => {
jobVariables[
`_webin_fileuri${index + 1}`
] = `/files/files/${fileInfo.file.id}`
jobVariables[`_webin_name${index + 1}`] = fileInfo.tableName
})
} else {
jobVariables = { ...jobVariables, ...formatDataForRequest(data) }
}
}
// Execute job in session
const jobRequestBody = {
name: fileName,
description: 'Powered by SASjs',
code: linesOfCode,
variables: jobVariables,
arguments: jobArguments
}
const { result: postedJob, etag } = await this.requestClient
.post<Job>(
`/compute/sessions/${executionSessionId}/jobs`,
jobRequestBody,
access_token
)
.catch((err) => {
throw prefixMessage(err, 'Error while posting job. ')
})
if (!waitForResult) return session
if (debug) {
logger.info(`Job has been submitted for '${fileName}'.`)
logger.info(
`You can monitor the job progress at '${this.serverUrl}${
postedJob.links.find((l: any) => l.rel === 'state')!.href
}'.`
)
}
const jobStatus = await this.pollJobState(
postedJob,
etag,
authConfig,
pollOptions
).catch(async (err) => {
const error = err?.response?.data
const result = /err=[0-9]*,/.exec(error)
const errorCode = '5113'
if (result?.[0]?.slice(4, -1) === errorCode) {
const sessionLogUrl =
postedJob.links.find((l: any) => l.rel === 'up')!.href + '/log'
const logCount = 1000000
err.log = await fetchLogByChunks(
this.requestClient,
access_token!,
sessionLogUrl,
logCount
)
}
throw prefixMessage(err, 'Error while polling job status. ')
})
if (authConfig) {
;({ access_token } = await this.getTokens(authConfig))
}
const { result: currentJob } = await this.requestClient
.get<Job>(
`/compute/sessions/${executionSessionId}/jobs/${postedJob.id}`,
access_token
)
.catch((err) => {
throw prefixMessage(err, 'Error while getting job. ')
})
let jobResult
let log = ''
const logLink = currentJob.links.find((l) => l.rel === 'log')
if (debug && logLink) {
const logUrl = `${logLink.href}/content`
const logCount = currentJob.logStatistics?.lineCount ?? 1000000
log = await fetchLogByChunks(
this.requestClient,
access_token!,
logUrl,
logCount
)
}
if (jobStatus === 'failed' || jobStatus === 'error') {
return Promise.reject(new ComputeJobExecutionError(currentJob, log))
}
let resultLink
if (expectWebout) {
resultLink = `/compute/sessions/${executionSessionId}/filerefs/_webout/content`
} else {
return { job: currentJob, log }
}
if (resultLink) {
jobResult = await this.requestClient
.get<any>(resultLink, access_token, 'text/plain')
.catch(async (e) => {
if (e instanceof NotFoundError) {
if (logLink) {
const logUrl = `${logLink.href}/content`
const logCount = currentJob.logStatistics?.lineCount ?? 1000000
log = await fetchLogByChunks(
this.requestClient,
access_token!,
logUrl,
logCount
)
return Promise.reject({
status: 500,
log
})
}
}
return {
result: JSON.stringify(e)
}
})
}
await this.sessionManager
.clearSession(executionSessionId, access_token)
.catch((err) => {
throw prefixMessage(err, 'Error while clearing session. ')
})
return { result: jobResult?.result, log }
} catch (e) {
if (e && e.status === 404) {
return this.executeScript(
jobPath,
linesOfCode,
contextName,
authConfig,
data,
debug,
false,
true
)
} else {
throw prefixMessage(e, 'Error while executing script. ')
}
}
return executeScript(
this.requestClient,
this.sessionManager,
this.rootFolderName,
jobPath,
linesOfCode,
contextName,
authConfig,
data,
debug,
expectWebout,
waitForResult,
pollOptions,
printPid,
variables
)
}
/**
@@ -584,9 +345,6 @@ export class SASViyaApiClient {
const formData = new NodeFormData()
formData.append('file', contentBuffer, fileName)
const mimeType =
mime.getType(fileName.match(/\.[0-9a-z]+$/i)?.[0] || '') ?? 'text/plain'
return (
await this.requestClient.post<File>(
`/files/files?parentFolderUri=${parentFolderUri}&typeDefName=file#rawUpload`,
@@ -633,7 +391,11 @@ export class SASViyaApiClient {
)
const newFolderName = `${parentFolderPath.split('/').pop()}`
if (newParentFolderPath === '') {
throw new Error('Root folder has to be present on the server.')
throw new RootFolderNotFoundError(
parentFolderPath,
this.serverUrl,
accessToken
)
}
logger.info(
`Creating parent folder:\n'${newFolderName}' in '${newParentFolderPath}'`
@@ -772,37 +534,7 @@ export class SASViyaApiClient {
clientSecret: string,
authCode: string
): Promise<SasAuthResponse> {
const url = this.serverUrl + '/SASLogon/oauth/token'
let token
if (typeof Buffer === 'undefined') {
token = btoa(clientId + ':' + clientSecret)
} else {
token = Buffer.from(clientId + ':' + clientSecret).toString('base64')
}
const headers = {
Authorization: 'Basic ' + token
}
let formData
if (typeof FormData === 'undefined') {
formData = new NodeFormData()
} else {
formData = new FormData()
}
formData.append('grant_type', 'authorization_code')
formData.append('code', authCode)
const authResponse = await this.requestClient
.post(
url,
formData,
undefined,
'multipart/form-data; boundary=' + (formData as any)._boundary,
headers
)
.then((res) => res.result as SasAuthResponse)
return authResponse
return getAccessToken(this.requestClient, clientId, clientSecret, authCode)
}
/**
@@ -816,39 +548,12 @@ export class SASViyaApiClient {
clientSecret: string,
refreshToken: string
) {
const url = this.serverUrl + '/SASLogon/oauth/token'
let token
if (typeof Buffer === 'undefined') {
token = btoa(clientId + ':' + clientSecret)
} else {
token = Buffer.from(clientId + ':' + clientSecret).toString('base64')
}
const headers = {
Authorization: 'Basic ' + token
}
let formData
if (typeof FormData === 'undefined') {
formData = new NodeFormData()
formData.append('grant_type', 'refresh_token')
formData.append('refresh_token', refreshToken)
} else {
formData = new FormData()
formData.append('grant_type', 'refresh_token')
formData.append('refresh_token', refreshToken)
}
const authResponse = await this.requestClient
.post<SasAuthResponse>(
url,
formData,
undefined,
'multipart/form-data; boundary=' + (formData as any)._boundary,
headers
)
.then((res) => res.result)
return authResponse
return refreshTokens(
this.requestClient,
clientId,
clientSecret,
refreshToken
)
}
/**
@@ -895,7 +600,7 @@ export class SASViyaApiClient {
) {
let access_token = (authConfig || {}).access_token
if (authConfig) {
;({ access_token } = await this.getTokens(authConfig))
;({ access_token } = await getTokens(this.requestClient, authConfig))
}
if (isRelativePath(sasJob) && !this.rootFolderName) {
@@ -991,7 +696,7 @@ export class SASViyaApiClient {
) {
let access_token = (authConfig || {}).access_token
if (authConfig) {
;({ access_token } = await this.getTokens(authConfig))
;({ access_token } = await getTokens(this.requestClient, authConfig))
}
if (isRelativePath(sasJob) && !this.rootFolderName) {
throw new Error(
@@ -1063,18 +768,16 @@ export class SASViyaApiClient {
jobDefinition,
arguments: jobArguments
}
const { result: postedJob, etag } = await this.requestClient.post<Job>(
const { result: postedJob } = await this.requestClient.post<Job>(
`${this.serverUrl}/jobExecution/jobs?_action=wait`,
postJobRequestBody,
access_token
)
const jobStatus = await this.pollJobState(
postedJob,
etag,
authConfig
).catch((err) => {
throw prefixMessage(err, 'Error while polling job status. ')
})
const jobStatus = await this.pollJobState(postedJob, authConfig).catch(
(err) => {
throw prefixMessage(err, 'Error while polling job status. ')
}
)
const { result: currentJob } = await this.requestClient.get<Job>(
`${this.serverUrl}/jobExecution/jobs/${postedJob.id}`,
access_token
@@ -1140,157 +843,22 @@ export class SASViyaApiClient {
this.folderMap.set(path, itemsAtRoot)
}
// REFACTOR: set default value for 'pollOptions' attribute
private async pollJobState(
postedJob: any,
etag: string | null,
postedJob: Job,
authConfig?: AuthConfig,
pollOptions?: PollOptions
) {
const logger = process.logger || console
let POLL_INTERVAL = 300
let MAX_POLL_COUNT = 1000
let MAX_ERROR_COUNT = 5
let access_token = (authConfig || {}).access_token
if (authConfig) {
;({ access_token } = await this.getTokens(authConfig))
}
if (pollOptions) {
POLL_INTERVAL = pollOptions.POLL_INTERVAL || POLL_INTERVAL
MAX_POLL_COUNT = pollOptions.MAX_POLL_COUNT || MAX_POLL_COUNT
}
let postedJobState = ''
let pollCount = 0
let errorCount = 0
const headers: any = {
'Content-Type': 'application/json',
'If-None-Match': etag
}
if (access_token) {
headers.Authorization = `Bearer ${access_token}`
}
const stateLink = postedJob.links.find((l: any) => l.rel === 'state')
if (!stateLink) {
Promise.reject(`Job state link was not found.`)
}
const { result: state } = await this.requestClient
.get<string>(
`${this.serverUrl}${stateLink.href}?_action=wait&wait=300`,
access_token,
'text/plain',
{},
this.debug
)
.catch((err) => {
console.error(
`Error fetching job state from ${this.serverUrl}${stateLink.href}. Starting poll, assuming job to be running.`,
err
)
return { result: 'unavailable' }
})
const currentState = state.trim()
if (currentState === 'completed') {
return Promise.resolve(currentState)
}
return new Promise(async (resolve, _) => {
let printedState = ''
const interval = setInterval(async () => {
if (
postedJobState === 'running' ||
postedJobState === '' ||
postedJobState === 'pending' ||
postedJobState === 'unavailable'
) {
if (authConfig) {
;({ access_token } = await this.getTokens(authConfig))
}
if (stateLink) {
const { result: jobState } = await this.requestClient
.get<string>(
`${this.serverUrl}${stateLink.href}?_action=wait&wait=300`,
access_token,
'text/plain',
{},
this.debug
)
.catch((err) => {
errorCount++
if (
pollCount >= MAX_POLL_COUNT ||
errorCount >= MAX_ERROR_COUNT
) {
throw prefixMessage(
err,
'Error while getting job state after interval. '
)
}
console.error(
`Error fetching job state from ${this.serverUrl}${stateLink.href}. Resuming poll, assuming job to be running.`,
err
)
return { result: 'unavailable' }
})
postedJobState = jobState.trim()
if (postedJobState != 'unavailable' && errorCount > 0) {
errorCount = 0
}
if (this.debug && printedState !== postedJobState) {
logger.info('Polling job status...')
logger.info(`Current job state: ${postedJobState}`)
printedState = postedJobState
}
pollCount++
if (pollCount >= MAX_POLL_COUNT) {
resolve(postedJobState)
}
}
} else {
clearInterval(interval)
resolve(postedJobState)
}
}, POLL_INTERVAL)
})
return pollJobState(
this.requestClient,
postedJob,
this.debug,
authConfig,
pollOptions
)
}
private async uploadTables(data: any, accessToken?: string) {
const uploadedFiles = []
const headers: any = {
'Content-Type': 'application/json'
}
if (accessToken) {
headers.Authorization = `Bearer ${accessToken}`
}
for (const tableName in data) {
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.'
)
}
const uploadResponse = await this.requestClient
.uploadFile(`${this.serverUrl}/files/files#rawUpload`, csv, accessToken)
.catch((err) => {
throw prefixMessage(err, 'Error while uploading file. ')
})
uploadedFiles.push({ tableName, file: uploadResponse.result })
}
return uploadedFiles
return uploadTables(this.requestClient, data, accessToken)
}
private async getFolderDetails(
@@ -1379,14 +947,6 @@ export class SASViyaApiClient {
? sourceFolder
: await this.getFolderUri(sourceFolder, accessToken)
const requestInfo = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + accessToken
}
}
const { result: members } = await this.requestClient.get<{ items: any[] }>(
`${this.serverUrl}${sourceFolderUri}/members?limit=${limit}`,
accessToken
@@ -1493,21 +1053,4 @@ export class SASViyaApiClient {
return movedFolder
}
private async getTokens(authConfig: AuthConfig): Promise<AuthConfig> {
const logger = process.logger || console
let { access_token, refresh_token, client, secret } = authConfig
if (
isAccessTokenExpiring(access_token) ||
isRefreshTokenExpiring(refresh_token)
) {
logger.info('Refreshing access and refresh tokens.')
;({ access_token, refresh_token } = await this.refreshTokens(
client,
secret,
refresh_token
))
}
return { access_token, refresh_token, client, secret }
}
}

View File

@@ -1,20 +1,35 @@
import { compareTimestamps, asyncForEach } from './utils'
import { SASjsConfig, UploadFile, EditContextInput, PollOptions } from './types'
import {
SASjsConfig,
UploadFile,
EditContextInput,
PollOptions,
LoginMechanism,
FolderMember,
ServiceMember,
ExecutionQuery
} from './types'
import { SASViyaApiClient } from './SASViyaApiClient'
import { SAS9ApiClient } from './SAS9ApiClient'
import { FileUploader } from './FileUploader'
import { SASjsApiClient } from './SASjsApiClient'
import { AuthManager } from './auth'
import { ServerType, MacroVar, AuthConfig } from '@sasjs/utils/types'
import {
ServerType,
MacroVar,
AuthConfig,
ExtraResponseAttributes
} from '@sasjs/utils/types'
import { RequestClient } from './request/RequestClient'
import {
JobExecutor,
WebJobExecutor,
ComputeJobExecutor,
JesJobExecutor,
Sas9JobExecutor
Sas9JobExecutor,
FileUploader
} from './job-execution'
import { ErrorResponse } from './types/errors'
import { ExtraResponseAttributes } from '@sasjs/utils/types'
import { LoginOptions, LoginResult } from './types/Login'
const defaultConfig: SASjsConfig = {
serverUrl: '',
@@ -25,7 +40,7 @@ const defaultConfig: SASjsConfig = {
debug: false,
contextName: 'SAS Job Execution compute context',
useComputeApi: null,
allowInsecureRequests: false
loginMechanism: LoginMechanism.Default
}
/**
@@ -37,6 +52,7 @@ export default class SASjs {
private jobsPath: string = ''
private sasViyaApiClient: SASViyaApiClient | null = null
private sas9ApiClient: SAS9ApiClient | null = null
private SASjsApiClient: SASjsApiClient | null = null
private fileUploader: FileUploader | null = null
private authManager: AuthManager | null = null
private requestClient: RequestClient | null = null
@@ -45,7 +61,7 @@ export default class SASjs {
private jesJobExecutor: JobExecutor | null = null
private sas9JobExecutor: JobExecutor | null = null
constructor(config?: any) {
constructor(config?: Partial<SASjsConfig>) {
this.sasjsConfig = {
...defaultConfig,
...config
@@ -495,7 +511,7 @@ export default class SASjs {
...this.sasjsConfig,
...config
}
await this.setupConfiguration()
this.setupConfiguration()
}
/**
@@ -522,8 +538,27 @@ export default class SASjs {
* @param username - a string representing the username.
* @param password - a string representing the password.
*/
public async logIn(username: string, password: string) {
return this.authManager!.logIn(username, password)
public async logIn(
username?: string,
password?: string,
options: LoginOptions = {}
): Promise<LoginResult> {
if (this.sasjsConfig.loginMechanism === LoginMechanism.Default) {
if (!username || !password) {
throw new Error(
'A username and password are required when using the default login mechanism.'
)
}
return this.authManager!.logIn(username, password)
}
if (typeof window === typeof undefined) {
throw new Error(
'The redirected login mechanism is only available for use in the browser.'
)
}
return this.authManager!.redirectedLogIn(options)
}
/**
@@ -540,13 +575,32 @@ export default class SASjs {
* Process). Is prepended at runtime with the value of `appLoc`.
* @param files - array of files to be uploaded, including File object and file name.
* @param params - request URL parameters.
* @param config - provide any changes to the config here, for instance to
* enable/disable `debug`. Any change provided will override the global config,
* for that particular function call.
* @param loginRequiredCallback - a function that is called if the
* user is not logged in (eg to display a login form). The request will be
* resubmitted after successful login.
*/
public uploadFile(sasJob: string, files: UploadFile[], params: any) {
const fileUploader =
this.fileUploader ||
new FileUploader(this.sasjsConfig, this.jobsPath, this.requestClient!)
public async uploadFile(
sasJob: string,
files: UploadFile[],
params: { [key: string]: any } | null,
config: { [key: string]: any } = {},
loginRequiredCallback?: () => any
) {
config = {
...this.sasjsConfig,
...config
}
const data = { files, params }
return fileUploader.uploadFile(sasJob, files, params)
return await this.fileUploader!.execute(
sasJob,
data,
config,
loginRequiredCallback
)
}
/**
@@ -604,6 +658,11 @@ export default class SASjs {
authConfig
)
} else {
if (!config.contextName)
config = {
...config,
contextName: 'SAS Job Execution compute context'
}
return await this.jesJobExecutor!.execute(
sasJob,
data,
@@ -734,7 +793,11 @@ export default class SASjs {
)
sasApiClient.debug = this.sasjsConfig.debug
} else if (this.sasjsConfig.serverType === ServerType.Sas9) {
sasApiClient = new SAS9ApiClient(serverUrl, this.jobsPath)
sasApiClient = new SAS9ApiClient(
serverUrl,
this.jobsPath,
this.sasjsConfig.httpsAgentOptions
)
}
} else {
let sasClientConfig: any = null
@@ -765,6 +828,14 @@ export default class SASjs {
)
}
public async deployToSASjs(members: [FolderMember, ServiceMember]) {
return await this.SASjsApiClient?.deploy(members, this.sasjsConfig.appLoc)
}
public async executeJobSASjs(query: ExecutionQuery) {
return await this.SASjsApiClient?.executeJob(query)
}
/**
* Kicks off execution of the given job via the compute API.
* @returns an object representing the compute session created for the given job.
@@ -823,6 +894,7 @@ export default class SASjs {
await this.webJobExecutor?.resendWaitingRequests()
await this.computeJobExecutor?.resendWaitingRequests()
await this.jesJobExecutor?.resendWaitingRequests()
await this.fileUploader?.resendWaitingRequests()
}
/**
@@ -854,20 +926,18 @@ export default class SASjs {
})
}
/**
* this method returns an array of SASjsRequest
* @returns SASjsRequest[]
*/
public getSasRequests() {
const requests = [
...this.webJobExecutor!.getRequests(),
...this.computeJobExecutor!.getRequests(),
...this.jesJobExecutor!.getRequests()
]
const requests = [...this.requestClient!.getRequests()]
const sortedRequests = requests.sort(compareTimestamps)
return sortedRequests
}
public clearSasRequests() {
this.webJobExecutor!.clearRequests()
this.computeJobExecutor!.clearRequests()
this.jesJobExecutor!.clearRequests()
this.requestClient!.clearRequests()
}
private setupConfiguration() {
@@ -890,10 +960,17 @@ export default class SASjs {
this.sasjsConfig.serverUrl = this.sasjsConfig.serverUrl.slice(0, -1)
}
this.requestClient = new RequestClient(
this.sasjsConfig.serverUrl,
this.sasjsConfig.allowInsecureRequests
)
if (!this.requestClient) {
this.requestClient = new RequestClient(
this.sasjsConfig.serverUrl,
this.sasjsConfig.httpsAgentOptions
)
} else {
this.requestClient.setConfig(
this.sasjsConfig.serverUrl,
this.sasjsConfig.httpsAgentOptions
)
}
this.jobsPath =
this.sasjsConfig.serverType === ServerType.SasViya
@@ -908,33 +985,49 @@ export default class SASjs {
)
if (this.sasjsConfig.serverType === ServerType.SasViya) {
if (this.sasViyaApiClient)
if (this.sasViyaApiClient) {
this.sasViyaApiClient!.setConfig(
this.sasjsConfig.serverUrl,
this.sasjsConfig.appLoc
)
else
} else {
this.sasViyaApiClient = new SASViyaApiClient(
this.sasjsConfig.serverUrl,
this.sasjsConfig.appLoc,
this.sasjsConfig.contextName,
this.requestClient
)
}
this.sasViyaApiClient.debug = this.sasjsConfig.debug
}
if (this.sasjsConfig.serverType === ServerType.Sas9) {
if (this.sas9ApiClient)
if (this.sas9ApiClient) {
this.sas9ApiClient!.setConfig(this.sasjsConfig.serverUrl)
else
} else {
this.sas9ApiClient = new SAS9ApiClient(
this.sasjsConfig.serverUrl,
this.jobsPath
this.jobsPath,
this.sasjsConfig.httpsAgentOptions
)
}
}
if (this.sasjsConfig.serverType === ServerType.Sasjs) {
if (this.SASjsApiClient) {
this.SASjsApiClient.setConfig(this.sasjsConfig.serverUrl)
} else {
this.SASjsApiClient = new SASjsApiClient(
this.sasjsConfig.serverUrl,
this.requestClient
)
}
}
this.fileUploader = new FileUploader(
this.sasjsConfig,
this.sasjsConfig.serverUrl,
this.sasjsConfig.serverType!,
this.jobsPath,
this.requestClient
)
@@ -950,7 +1043,9 @@ export default class SASjs {
this.sas9JobExecutor = new Sas9JobExecutor(
this.sasjsConfig.serverUrl,
this.sasjsConfig.serverType!,
this.jobsPath
this.jobsPath,
this.requestClient,
this.sasjsConfig.httpsAgentOptions
)
this.computeJobExecutor = new ComputeJobExecutor(

39
src/SASjsApiClient.ts Normal file
View File

@@ -0,0 +1,39 @@
import { FolderMember, ServiceMember, ExecutionQuery } from './types'
import { RequestClient } from './request/RequestClient'
export class SASjsApiClient {
constructor(
private serverUrl: string,
private requestClient: RequestClient
) {}
public setConfig(serverUrl: string) {
if (serverUrl) this.serverUrl = serverUrl
}
public async deploy(members: [FolderMember, ServiceMember], appLoc: string) {
const { result } = await this.requestClient.post<{
status: string
message: string
example?: {}
}>(
'SASjsApi/drive/deploy',
{ fileTree: members, appLoc: appLoc },
undefined
)
return Promise.resolve(result)
}
public async executeJob(query: ExecutionQuery) {
const { result } = await this.requestClient.post<{
status: string
message: string
log?: string
logPath?: string
error?: {}
}>('SASjsApi/stp/execute', query, undefined)
return Promise.resolve(result)
}
}

View File

@@ -1,13 +1,14 @@
import { Session, Context, CsrfToken, SessionVariable } from './types'
import { Session, Context, SessionVariable } from './types'
import { NoSessionStateError } from './types/errors'
import { asyncForEach, isUrl } from './utils'
import { prefixMessage } from '@sasjs/utils/error'
import { RequestClient } from './request/RequestClient'
const MAX_SESSION_COUNT = 1
const RETRY_LIMIT: number = 3
let RETRY_COUNT: number = 0
export class SessionManager {
private loggedErrors: NoSessionStateError[] = []
constructor(
private serverUrl: string,
private contextName: string,
@@ -153,27 +154,27 @@ export class SessionManager {
session: Session,
etag: string | null,
accessToken?: string
) {
): Promise<string> {
const logger = process.logger || console
let sessionState = session.state
const stateLink = session.links.find((l: any) => l.rel === 'state')
return new Promise(async (resolve, reject) => {
if (
sessionState === 'pending' ||
sessionState === 'running' ||
sessionState === ''
) {
if (stateLink) {
if (this.debug && !this.printedSessionState.printed) {
logger.info('Polling session status...')
if (
sessionState === 'pending' ||
sessionState === 'running' ||
sessionState === ''
) {
if (stateLink) {
if (this.debug && !this.printedSessionState.printed) {
logger.info('Polling session status...')
this.printedSessionState.printed = true
}
this.printedSessionState.printed = true
}
const state = await this.getSessionState(
const { result: state, responseStatus: responseStatus } =
await this.getSessionState(
`${this.serverUrl}${stateLink.href}?wait=30`,
etag!,
accessToken
@@ -181,33 +182,47 @@ export class SessionManager {
throw prefixMessage(err, 'Error while getting session state.')
})
sessionState = state.trim()
sessionState = state.trim()
if (this.debug && this.printedSessionState.state !== sessionState) {
logger.info(`Current session state is '${sessionState}'`)
if (this.debug && this.printedSessionState.state !== sessionState) {
logger.info(`Current session state is '${sessionState}'`)
this.printedSessionState.state = sessionState
this.printedSessionState.printed = false
}
// There is an internal error present in SAS Viya 3.5
// Retry to wait for a session status in such case of SAS internal error
if (!sessionState) {
if (RETRY_COUNT < RETRY_LIMIT) {
RETRY_COUNT++
resolve(this.waitForSession(session, etag, accessToken))
} else {
reject('Could not get session state.')
}
}
resolve(sessionState)
this.printedSessionState.state = sessionState
this.printedSessionState.printed = false
}
if (!sessionState) {
const stateError = new NoSessionStateError(
responseStatus,
this.serverUrl + stateLink.href,
session.links.find((l: any) => l.rel === 'log')?.href as string
)
if (
!this.loggedErrors.find(
(err: NoSessionStateError) =>
err.serverResponseStatus === stateError.serverResponseStatus
)
) {
this.loggedErrors.push(stateError)
logger.info(stateError.message)
}
return await this.waitForSession(session, etag, accessToken)
}
this.loggedErrors = []
return sessionState
} else {
resolve(sessionState)
throw 'Error while getting session state link.'
}
})
} else {
this.loggedErrors = []
return sessionState
}
}
private async getSessionState(
@@ -217,7 +232,10 @@ export class SessionManager {
) {
return await this.requestClient
.get(url, accessToken, 'text/plain', { 'If-None-Match': etag })
.then((res) => res.result as string)
.then((res) => ({
result: res.result as string,
responseStatus: res.status
}))
.catch((err) => {
throw err
})

View File

@@ -0,0 +1,293 @@
import { timestampToYYYYMMDDHHMMSS } from '@sasjs/utils/time'
import { AuthConfig, MacroVar } from '@sasjs/utils/types'
import { prefixMessage } from '@sasjs/utils/error'
import {
PollOptions,
Job,
ComputeJobExecutionError,
NotFoundError
} from '../..'
import { getTokens } from '../../auth/getTokens'
import { RequestClient } from '../../request/RequestClient'
import { SessionManager } from '../../SessionManager'
import { isRelativePath, fetchLogByChunks } from '../../utils'
import { formatDataForRequest } from '../../utils/formatDataForRequest'
import { pollJobState } from './pollJobState'
import { uploadTables } from './uploadTables'
/**
* Executes code on the current SAS Viya server.
* @param jobPath - the path to the file being submitted for execution.
* @param linesOfCode - an array of code lines to execute.
* @param contextName - the context to execute the code in.
* @param authConfig - an object containing an access token, refresh token, client ID and secret.
* @param data - execution data.
* @param debug - when set to true, the log will be returned.
* @param expectWebout - when set to true, the automatic _webout fileref will be checked for content, and that content returned. This fileref is used when the Job contains a SASjs web request (as opposed to executing arbitrary SAS code).
* @param waitForResult - when set to true, function will return the session
* @param pollOptions - an object that represents poll interval(milliseconds) and maximum amount of attempts. Object example: { MAX_POLL_COUNT: 24 * 60 * 60, POLL_INTERVAL: 1000 }.
* @param printPid - a boolean that indicates whether the function should print (PID) of the started job.
* @param variables - an object that represents macro variables.
*/
export async function executeScript(
requestClient: RequestClient,
sessionManager: SessionManager,
rootFolderName: string,
jobPath: string,
linesOfCode: string[],
contextName: string,
authConfig?: AuthConfig,
data: any = null,
debug: boolean = false,
expectWebout = false,
waitForResult = true,
pollOptions?: PollOptions,
printPid = false,
variables?: MacroVar
): Promise<any> {
let access_token = (authConfig || {}).access_token
if (authConfig) {
;({ access_token } = await getTokens(requestClient, authConfig))
}
const logger = process.logger || console
try {
let executionSessionId: string
const session = await sessionManager
.getSession(access_token)
.catch((err) => {
throw prefixMessage(err, 'Error while getting session. ')
})
executionSessionId = session!.id
if (printPid) {
const { result: jobIdVariable } = await sessionManager
.getVariable(executionSessionId, 'SYSJOBID', access_token)
.catch((err) => {
throw prefixMessage(err, 'Error while getting session variable. ')
})
if (jobIdVariable && jobIdVariable.value) {
const relativeJobPath = rootFolderName
? jobPath.split(rootFolderName).join('').replace(/^\//, '')
: jobPath
const logger = process.logger || console
logger.info(
`Triggered '${relativeJobPath}' with PID ${
jobIdVariable.value
} at ${timestampToYYYYMMDDHHMMSS()}`
)
}
}
const jobArguments: { [key: string]: any } = {
_contextName: contextName,
_OMITJSONLISTING: true,
_OMITJSONLOG: true,
_OMITSESSIONRESULTS: true,
_OMITTEXTLISTING: true,
_OMITTEXTLOG: true
}
if (debug) {
jobArguments['_OMITTEXTLOG'] = false
jobArguments['_OMITSESSIONRESULTS'] = false
}
let fileName
if (isRelativePath(jobPath)) {
fileName = `exec-${
jobPath.includes('/') ? jobPath.split('/')[1] : jobPath
}`
} else {
const jobPathParts = jobPath.split('/')
fileName = jobPathParts.pop()
}
let jobVariables: any = {
SYS_JES_JOB_URI: '',
_program: isRelativePath(jobPath)
? rootFolderName + '/' + jobPath
: jobPath
}
if (variables) jobVariables = { ...jobVariables, ...variables }
if (debug) jobVariables = { ...jobVariables, _DEBUG: 131 }
let files: any[] = []
if (data) {
if (JSON.stringify(data).includes(';')) {
files = await uploadTables(requestClient, data, access_token).catch(
(err) => {
throw prefixMessage(err, 'Error while uploading tables. ')
}
)
jobVariables['_webin_file_count'] = files.length
files.forEach((fileInfo, index) => {
jobVariables[
`_webin_fileuri${index + 1}`
] = `/files/files/${fileInfo.file.id}`
jobVariables[`_webin_name${index + 1}`] = fileInfo.tableName
})
} else {
jobVariables = { ...jobVariables, ...formatDataForRequest(data) }
}
}
// Execute job in session
const jobRequestBody = {
name: fileName,
description: 'Powered by SASjs',
code: linesOfCode,
variables: jobVariables,
arguments: jobArguments
}
const { result: postedJob, etag } = await requestClient
.post<Job>(
`/compute/sessions/${executionSessionId}/jobs`,
jobRequestBody,
access_token
)
.catch((err) => {
throw prefixMessage(err, 'Error while posting job. ')
})
if (!waitForResult) return session
if (debug) {
logger.info(`Job has been submitted for '${fileName}'.`)
logger.info(
`You can monitor the job progress at '${requestClient.getBaseUrl()}${
postedJob.links.find((l: any) => l.rel === 'state')!.href
}'.`
)
}
const jobStatus = await pollJobState(
requestClient,
postedJob,
debug,
authConfig,
pollOptions
).catch(async (err) => {
const error = err?.response?.data
const result = /err=[0-9]*,/.exec(error)
const errorCode = '5113'
if (result?.[0]?.slice(4, -1) === errorCode) {
const sessionLogUrl =
postedJob.links.find((l: any) => l.rel === 'up')!.href + '/log'
const logCount = 1000000
err.log = await fetchLogByChunks(
requestClient,
access_token!,
sessionLogUrl,
logCount
)
}
throw prefixMessage(err, 'Error while polling job status. ')
})
if (authConfig) {
;({ access_token } = await getTokens(requestClient, authConfig))
}
const { result: currentJob } = await requestClient
.get<Job>(
`/compute/sessions/${executionSessionId}/jobs/${postedJob.id}`,
access_token
)
.catch((err) => {
throw prefixMessage(err, 'Error while getting job. ')
})
let jobResult
let log = ''
const logLink = currentJob.links.find((l) => l.rel === 'log')
if (debug && logLink) {
const logUrl = `${logLink.href}/content`
const logCount = currentJob.logStatistics?.lineCount ?? 1000000
log = await fetchLogByChunks(
requestClient,
access_token!,
logUrl,
logCount
)
}
if (jobStatus === 'failed' || jobStatus === 'error') {
throw new ComputeJobExecutionError(currentJob, log)
}
if (!expectWebout) {
return { job: currentJob, log }
}
const resultLink = `/compute/sessions/${executionSessionId}/filerefs/_webout/content`
jobResult = await requestClient
.get<any>(resultLink, access_token, 'text/plain')
.catch(async (e) => {
if (e instanceof NotFoundError) {
if (logLink) {
const logUrl = `${logLink.href}/content`
const logCount = currentJob.logStatistics?.lineCount ?? 1000000
log = await fetchLogByChunks(
requestClient,
access_token!,
logUrl,
logCount
)
return Promise.reject({
status: 500,
log
})
}
}
return {
result: JSON.stringify(e)
}
})
await sessionManager
.clearSession(executionSessionId, access_token)
.catch((err) => {
throw prefixMessage(err, 'Error while clearing session. ')
})
return { result: jobResult?.result, log }
} catch (e) {
if (e && e.status === 404) {
return executeScript(
requestClient,
sessionManager,
rootFolderName,
jobPath,
linesOfCode,
contextName,
authConfig,
data,
debug,
false,
true
)
} else {
throw prefixMessage(e, 'Error while executing script. ')
}
}
}

Some files were not shown because too many files have changed in this diff Show More