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

Compare commits

..

137 Commits

Author SHA1 Message Date
Krishna Acondy
2c9cce8bf8 Merge pull request #71 from sasjs/issue70
fix: executeJobViaWeb retry requests missing params
2020-09-08 18:47:16 +01:00
Mihajlo Medjedovic
d5791a75cd fix: executeJobViaWeb retry requests missing params 2020-09-08 15:48:08 +02:00
Krishna Acondy
1f970e1102 Merge pull request #61 from sasjs/allanbowe-patch-1
chore: Update README.md
2020-09-04 12:47:14 +01:00
Krishna Acondy
1552762d28 Merge branch 'master' into allanbowe-patch-1 2020-09-04 12:45:55 +01:00
Krishna Acondy
ef39252b0b chore(doc): fix broken link 2020-09-04 12:43:57 +01:00
Allan Bowe
3f3f8f4531 Update README.md 2020-09-02 20:56:03 +02:00
Allan Bowe
33879ee998 Merge pull request #60 from sasjs/update-documentation
chore(*): update documentation, bump adapter version
2020-09-02 12:47:12 +02:00
Krishna Acondy
cc8271438b chore(doc): add documentation on creating tests 2020-09-02 11:36:15 +01:00
Krishna Acondy
a064bc086d chore(*): update documentation, bump adapter version 2020-09-02 11:29:39 +01:00
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
Krishna Acondy
e241a47c23 Merge pull request #11 from sasjs/api-execution
feat(*): Job Execution Approaches
2020-08-03 19:52:59 +01:00
Krishna Acondy
ed7f36dbed Merge branch 'api-execution' of https://github.com/sasjs/adapter into api-execution 2020-08-03 19:51:21 +01:00
Krishna Acondy
74f0c263db Merge branch 'master' of https://github.com/sasjs/adapter into api-execution 2020-08-03 19:50:30 +01:00
Krishna Acondy
5a2a4bf39c Merge pull request #19 from sasjs/issue17
File upload function and separate WEB and API csrf tokens
2020-08-01 19:10:29 +01:00
Mihajlo Medjedovic
0ea91ddd3b Merge branch 'api-execution' into issue17 2020-07-31 19:08:39 +02:00
Mihajlo Medjedovic
4422c37827 fix: retry requests with API approach 2020-07-30 00:43:12 +02:00
Krishna Acondy
5fad9d01bc chore(sasjs-tests): remove unnecessary code, bump test framework version 2020-07-29 22:52:17 +01:00
Mihajlo Medjedovic
fb02c77a3a fix: login required callback 2020-07-29 13:20:03 +02:00
Mihajlo Medjedovic
5de84c07a8 fix: computeApi approach loginRequired callback 2020-07-29 12:33:18 +02:00
Mihajlo Medjedovic
54e2319183 fix: public upload file params 2020-07-28 14:10:46 +02:00
Mihajlo Medjedovic
261913d2d7 fix: uploadFile accept multiple files 2020-07-28 13:23:57 +02:00
Allan Bowe
187917cb32 Merge pull request #20 from sasjs/use-test-framework
chore(sasjs-tests): use test framework in SASjs Tests
2020-07-23 10:15:12 +02:00
Allan Bowe
d22b6c77b3 Merge branch 'master' into use-test-framework 2020-07-23 10:14:30 +02:00
Allan Bowe
785b276741 chore: doc update 2020-07-22 23:34:41 +02:00
Krishna Acondy
2ce3669b10 Merge branch 'api-execution' of https://github.com/sasjs/adapter into issue17 2020-07-22 20:00:19 +01:00
Krishna Acondy
3a1ea1614f Merge branch 'master' of https://github.com/sasjs/adapter into issue17 2020-07-22 19:57:10 +01:00
Krishna Acondy
3c5988aacf chore(sasjs-tests): add missing dependency 2020-07-22 19:55:14 +01:00
Krishna Acondy
1a25c354fa Merge pull request #13 from sasjs/requestConfigOverride
Request config override
2020-07-22 19:53:33 +01:00
Krishna Acondy
eb1668d6c1 Merge pull request #15 from sasjs/dependabot/npm_and_yarn/types/jest-26.0.5
chore(deps-dev): bump @types/jest from 26.0.4 to 26.0.5
2020-07-22 19:46:32 +01:00
Krishna Acondy
69d088a9c6 chore(sasjs-tests): use test framework in SASjs Tests 2020-07-22 19:42:34 +01:00
Krishna Acondy
ccd44c31c7 feat(file-upload): move functionality into FileUploader 2020-07-22 07:14:31 +01:00
Krishna Acondy
dec7c18ecb Merge branch 'api-execution' of https://github.com/sasjs/adapter into api-execution 2020-07-22 06:42:53 +01:00
Krishna Acondy
c8a5eb5993 chore(deploy): update script to deploy tests 2020-07-22 06:42:50 +01:00
Mihajlo Medjedovic
700a67a600 feat: file upload function, WEB and API csrf tokens 2020-07-21 22:35:43 +02:00
Mihajlo Medjedovic
a2778bed52 docs: request() update 2020-07-20 23:03:05 +02:00
Mihajlo Medjedovic
2b04fe0c3e docs: update 2020-07-20 21:31:54 +02:00
Mihajlo Medjedovic
1c1e6b8efd fix: waiting requests passing config 2020-07-20 20:44:30 +02:00
Mihajlo Medjedovic
c92a0a53f2 Merge pull request #16 from sasjs/csrfTokenGetter
feat: csrf token main file sync
2020-07-20 20:35:43 +02:00
Mihajlo Medjedovic
ee024c67ab feat: csrf token main file sync 2020-07-20 20:34:12 +02:00
dependabot-preview[bot]
27301651be chore(deps-dev): bump @types/jest from 26.0.4 to 26.0.5
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.4 to 26.0.5.
- [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-07-20 08:58:58 +00:00
Krishna Acondy
9742f53dde Merge pull request #12 from sasjs/dependabot/npm_and_yarn/typescript-3.9.7
chore(deps-dev): bump typescript from 3.9.6 to 3.9.7
2020-07-20 08:19:25 +01:00
dependabot-preview[bot]
38a950a036 chore(deps-dev): bump typescript from 3.9.6 to 3.9.7
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 3.9.6 to 3.9.7.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v3.9.6...v3.9.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-20 07:17:13 +00:00
Allan Bowe
e41c54e37e docs: adding link attributes due to security vulnerability 2020-07-19 19:57:03 +02:00
Mihajlo Medjedovic
405a19f0cf fix: local config 2020-07-19 17:44:29 +02:00
Mihajlo Medjedovic
c7d6c66093 feat: request config override 2020-07-19 16:10:56 +02:00
Krishna Acondy
8bf74d17e9 fix(*): handle login required state, fix session creation logic 2020-07-18 15:08:40 +01:00
Krishna Acondy
a12244cf78 feat(session-manager): Manage a pool of sessions for job execution 2020-07-17 08:31:27 +01:00
Krishna Acondy
a579f481c5 fix(context): cache contexts after first request 2020-07-16 17:31:03 +01:00
Krishna Acondy
2ecd57169f fix(context): cache contexts after first request 2020-07-16 17:27:28 +01:00
Krishna Acondy
7d84033ad4 feat(compute-api): implement job execution via compute API 2020-07-16 09:06:24 +01:00
Allan Bowe
c4109b225b chore: readme update 2020-07-15 16:00:05 +02:00
Allan Bowe
2ec8696615 chore: readme update 2020-07-15 15:40:58 +02:00
Allan Bowe
c5339276e4 chore: readme update 2020-07-15 15:38:16 +02:00
Allan Bowe
97db960f62 chore: updating example.html to be consistent with README 2020-07-15 13:33:04 +02:00
Krishna Acondy
0ac7f8892e fix(build): switch default branch to master 2020-07-15 12:25:20 +01:00
Krishna Acondy
68f5e5bec5 Merge pull request #10 from sasjs/main
Merge main into master
2020-07-15 12:21:45 +01:00
Krishna Acondy
69a14ff6d7 Merge branch 'master' into main 2020-07-15 12:20:08 +01:00
Krishna Acondy
70e461224a Merge pull request #8 from sasjs/allanbowe-patch-1
Update npmpublish.yml
2020-07-15 12:18:46 +01:00
Krishna Acondy
4da22ee6b4 Merge pull request #9 from sasjs/dependabot/npm_and_yarn/ts-loader-8.0.1
chore(deps-dev): bump ts-loader from 7.0.5 to 8.0.1
2020-07-15 12:18:22 +01:00
Allan Bowe
2c8ba09578 docs: viya test routine in CONTRIBUTING.md 2020-07-15 11:12:46 +02:00
dependabot-preview[bot]
c12d6f1c9c chore(deps-dev): bump ts-loader from 7.0.5 to 8.0.1
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 7.0.5 to 8.0.1.
- [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/v7.0.5...v8.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-15 08:35:48 +00:00
Krishna Acondy
92504b0c16 feat(jes-api): implement job execution via API to JES 2020-07-13 18:38:30 +01:00
Allan Bowe
e5fb7a7698 Update npmpublish.yml 2020-07-11 18:23:19 +02:00
Krishna Acondy
c22b9066d8 chore(sasjs-tests): update package version 2020-07-11 11:39:59 +01:00
127 changed files with 17946 additions and 3442 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

@@ -6,7 +6,7 @@ name: SASjs Build and Publish
on:
push:
branches:
- main
- master
jobs:
build:
@@ -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

@@ -1,82 +1,14 @@
# Contributing
Contributions to SASjs are very welcome! When making a PR, test cases should be included. To help in unit testing, be sure to run the following when making changes:
Contributions to SASjs are very welcome! When making a PR, test cases should be included.
```
# the following creates a tarball in the build folder of SASjs
npm run-script package:lib
This repository contains a suite of tests built using [@sasjs/test-framework](https://github.com/sasjs/test-framework).
# now go to your app and run:
npm install ../sasjs/build/<tarball filename>
```
Detailed instructions for creating and running the tests can be found [here](https://github.com/sasjs/adapter/blob/master/sasjs-tests/README.md).
Tests are run using cypress. Before running tests, you need to define the following backend services:
If you'd like to test your changes in an app that uses the adapter, you can do so as follows:
# SAS 9
```
filename mc url "https://raw.githubusercontent.com/macropeople/macrocore/main/mc_all.sas?_=1";
%inc mc;
filename ft15f001 temp;
parmcards4;
%webout(OPEN)
%macro x();
%do i=1 %to &_webin_file_count; %webout(OBJ,&&_webin_name&i) %end;
%mend; %x()
%webout(CLOSE)
;;;;
%mm_createwebservice(path=/Public/app/common,name=sendObj)
parmcards4;
%webout(OPEN)
%macro x();
%do i=1 %to &_webin_file_count; %webout(ARR,&&_webin_name&i) %end;
%mend; %x()
%webout(CLOSE)
;;;;
%mm_createwebservice(path=/Public/app/common,name=sendArr)
```
# Viya
```
filename mc url "https://raw.githubusercontent.com/macropeople/macrocore/main/mc_all.sas";
%inc mc;
filename ft15f001 temp;
parmcards4;
%webout(OPEN)
%global sasjs_tables;
%let sasjs_tables=&sasjs_tables;
%put &=sasjs_tables;
%let sasjs_tables=&sasjs_tables;
%macro x();
%global sasjs_tables;
%do i=1 %to %sysfunc(countw(&sasjs_tables));
%let table=%scan(&sasjs_tables,&i);
%webout(OBJ,&table)
%end;
%mend;
%x()
%webout(CLOSE)
;;;;
%mv_createwebservice(path=/Public/app/common,name=sendObj)
filename ft15f001 temp;
parmcards4;
%webout(OPEN)
%global sasjs_tables;
%let sasjs_tables=&sasjs_tables;
%put &=sasjs_tables;
%macro x();
%do i=1 %to %sysfunc(countw(&sasjs_tables));
%let table=%scan(&sasjs_tables,&i);
%webout(ARR,&table)
%end;
%mend;
%x()
%webout(CLOSE)
;;;;
%mv_createwebservice(path=/Public/app/common,name=sendArr)
```
The above services will return anything you send. To run the tests simply launch `npm run cypress`.
1. Run `npm run package:lib` from the root folder in this repository.
This creates a tarball in the `/build` folder.
2. In your app's root folder, run `npm install <path/to/tarball>`.
This will install the changed version of the adapter in your app.

View File

@@ -10,13 +10,13 @@ SASjs is a open-source framework for building Web Apps on SAS® platforms. You c
3 - Reference directly from the CDN - in which case click [here](https://www.jsdelivr.com/package/npm/@sasjs/adapter?tab=collection) and select "SRI" to get the script tag with the integrity hash.
If you are short on time and just need to build an app quickly, then check out [this video](https://vimeo.com/393161794) and the [react-seed-app](https://github.com/macropeople/react-seed-app) which provides some boilerplate.
If you are short on time and just need to build an app quickly, then check out [this video](https://vimeo.com/393161794) and the [react-seed-app](https://github.com/sasjs/react-seed-app) which provides some boilerplate.
For more information on building web apps with SAS, check out [sasjs.io](https://sasjs.io)
## None of this makes sense. How do I build an app with it?
Ok ok. Deploy this [example.html](https://github.com/sasjs/adapter/blob/main/example.html) file to your web server, and update `servertype` to `SAS9` or `SASVIYA` depending on your backend.
Ok ok. Deploy this [example.html](https://raw.githubusercontent.com/sasjs/adapter/master/example.html) file to your web server, and update `servertype` to `SAS9` or `SASVIYA` depending on your backend.
The backend part can be deployed as follows:
@@ -43,6 +43,6 @@ You now have a simple web app with a backend service!
# More resources
For more information specific to this adapter you can check out this [user guide](https://sasjs.io/sasjs/sasjs-adapter/) or the [technical](http://adapter.sasjs.io/) documentation.
For more information and examples specific to this adapter you can check out the [user guide](https://sasjs.io/sasjs-adapter/) or the [technical](http://adapter.sasjs.io/) documentation.
For more information on building web apps in general, check out these [resources](https://sasjs.io/training/resources/) or contact the [author](https://www.linkedin.com/in/allanbowe/) directly.

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

View File

@@ -1,12 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<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: "/Products/demo/readme"
,serverType:"SAS9", debug: "false"
var sasJs = new SASjs.default({
appLoc: "/Public/app/readme"
,serverType:"SAS9"
,debug: false
});
function initSasJs() {
$('#loading-spinner').show()
@@ -70,6 +70,8 @@
});
}
</script>
<meta charset="utf-8" http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
</head>
<body>
<div class="container-fluid" style="text-align: center; margin-top: 10px;">
@@ -104,4 +106,4 @@
<canvas id="myChart" style="display: none;"></canvas>
</div>
</body>
</head>
</head>

1723
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",
@@ -22,9 +22,6 @@
{
"pkgRoot": "/build"
}
],
"branches": [
"main"
]
},
"keywords": [
@@ -40,23 +37,22 @@
"license": "ISC",
"devDependencies": {
"@types/isomorphic-fetch": "0.0.35",
"@types/jest": "^26.0.4",
"@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": "^7.0.5",
"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.6",
"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

@@ -1,68 +1,139 @@
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
`sasjs-tests` is a test suite for the SASjs adapter.
## Available Scripts
It is a React app bootstrapped using [Create React App](https://github.com/facebook/create-react-app) and [@sasjs/test-framework](https://github.com/sasjs/test-framework).
In the project directory, you can run:
When developing on `@sasjs/adapter`, it's good practice to run the test suite against your changed version of the adapter to ensure that existing functionality has not been impacted.
### `npm start`
You can use the provided `update:adapter` NPM script for this.
Runs the app in the development mode.<br />
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
```
npm run update:adapter
```
The page will reload if you make edits.<br />
You will also see any lint errors in the console.
This scripts builds a new version of the adapter and installs it in the `sasjs-tests` project.
### `npm test`
## Running tests
Launches the test runner in the interactive watch mode.<br />
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
There are three prerequisites to be able to run the tests:
### `npm run build`
1. Correct server configuration for the SASjs adapter.
2. `sasjs-tests` deployed to your SAS server.
3. The required SAS services created on the same server.
Builds the app for production to the `build` folder.<br />
It correctly bundles React in production mode and optimizes the build for the best performance.
### 1. Configuring the SASjs adapter
The build is minified and the filenames include the hashes.<br />
Your app is ready to be deployed!
There is a `config.json` file in the `/public` folder which specifies the configuration for the SASjs adapter. You can set the values within the `sasjsConfig` property in this file to match your SAS server configuration.
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### 2. Deploying to your SAS server
### `npm run eject`
There is a `deploy` NPM script provided in the `sasjs-tests` project's `package.json`.
**Note: this is a one-way operation. Once you `eject`, you cant go back!**
It updates `sasjs-tests` to use the latest version of the adapter, and deploys to a specified server via SSH using the `rsync` command.
If you arent satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
To be able to run the `deploy` script, two environment variables need to be set:
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point youre on your own.
- `SSH_ACCOUNT` - your SSH account, this is of the form username@domain.com
- `DEPLOY_PATH` - the path on the server where `sasjs-tests` will be deployed to, typically `/var/www/html/<some-subfolder>`.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt customize it when you are ready for it.
So you can run the script like so:
## Learn More
```
SSH_ACCOUNT=me@my-sas-server.com DEPLOY_PATH=/var/www/html/my-folder/sasjs-tests npm run deploy
```
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
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.
To learn React, check out the [React documentation](https://reactjs.org/).
## 3. Creating the required SAS services
### Code Splitting
The below services need to be created on your SAS server, at the location specified as the `appLoc` in the SASjs configuration.
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
### SAS 9
### Analyzing the Bundle Size
```
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc;
filename ft15f001 temp;
parmcards4;
%webout(OPEN)
%macro x();
%do i=1 %to &_webin_file_count; %webout(OBJ,&&_webin_name&i) %end;
%mend; %x()
%webout(CLOSE)
;;;;
%mm_createwebservice(path=/Public/app/common,name=sendObj)
parmcards4;
%webout(OPEN)
%macro x();
%do i=1 %to &_webin_file_count; %webout(ARR,&&_webin_name&i) %end;
%mend; %x()
%webout(CLOSE)
;;;;
%mm_createwebservice(path=/Public/app/common,name=sendArr)
```
### Making a Progressive Web App
### SAS Viya
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
```
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc;
filename ft15f001 temp;
parmcards4;
%webout(FETCH)
%webout(OPEN)
%macro x();
%do i=1 %to %sysfunc(countw(&sasjs_tables));
%let table=%scan(&sasjs_tables,&i);
%webout(OBJ,&table)
%end;
%mend;
%x()
%webout(CLOSE)
;;;;
%mp_createwebservice(path=/Public/app/common,name=sendObj)
filename ft15f001 temp;
parmcards4;
%webout(FETCH)
%webout(OPEN)
%macro x();
%do i=1 %to %sysfunc(countw(&sasjs_tables));
%let table=%scan(&sasjs_tables,&i);
%webout(ARR,&table)
%end;
%mend;
%x()
%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)
```
### Advanced Configuration
You should now be able to access the tests in your browser at the deployed path on your server.
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
## Creating new tests
### Deployment
The `src/testSuites` folder contains all the test suites currently available.
Each suite contains a set of specs, each of which looks like this:
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
```javascript
{
title: "Your test title",
description: "A slightly more detailed description",
test: async () => {
// typically makes a request using the adapter and returns a promise
},
assertion: (response: any) =>
// receives the response when the test promise resolves, runs an assertion and returns a boolean
}
```
### `npm run build` fails to minify
A test suite is an array of such objects, along with a `name` property.
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
You can add your test to one of the existing suites if suitable, or create a new file that specifies a new test suite.

View File

@@ -1357,9 +1357,9 @@
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
},
"@sasjs/adapter": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-1.0.4.tgz",
"integrity": "sha512-wjABkVqPQ0AaSoizCrLInG3HX1o+4f98UsHRGDHLHvlQzIIOUtUNElIAPYoy5Xxu+CPk030jXkgCls7n0k/3cw==",
"version": "1.3.13",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-1.3.13.tgz",
"integrity": "sha512-dWcDxgY3FB7Yx1I5dPpeQeyJDu4lezhIFrjn6lbdwRhV15aqOt4l9o9qZP+VbgOXqyi9gN0Y+p+vs2chBDFQqg==",
"requires": {
"es6-promise": "^4.2.8",
"form-data": "^3.0.0",
@@ -1378,11 +1378,59 @@
}
}
},
"@sasjs/test-framework": {
"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": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@semantic-ui-react/event-stack/-/event-stack-3.1.1.tgz",
"integrity": "sha512-SA7VOu/tY3OkooR++mm9voeQrJpYXjJaMHO1aFCcSouS2xhqMR9Gnz0LEGLOR0h9ueWPBKaQzKIrx3FTTJZmUQ==",
"requires": {
"exenv": "^1.2.2",
"prop-types": "^15.6.2"
}
},
"@sheerun/mutationobserver-shim": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz",
"integrity": "sha512-DetpxZw1fzPD5xUBrIAoplLChO2VB8DlL5Gg+I1IR9b2wPqYIca2WSUxL5g1vLeR4MsQq1NeWriXAVffV+U1Fw=="
},
"@stardust-ui/react-component-event-listener": {
"version": "0.38.0",
"resolved": "https://registry.npmjs.org/@stardust-ui/react-component-event-listener/-/react-component-event-listener-0.38.0.tgz",
"integrity": "sha512-sIP/e0dyOrrlb8K7KWumfMxj/gAifswTBC4o68Aa+C/GA73ccRp/6W1VlHvF/dlOR4KLsA+5SKnhjH36xzPsWg==",
"requires": {
"@babel/runtime": "^7.1.2",
"prop-types": "^15.7.2"
}
},
"@stardust-ui/react-component-ref": {
"version": "0.38.0",
"resolved": "https://registry.npmjs.org/@stardust-ui/react-component-ref/-/react-component-ref-0.38.0.tgz",
"integrity": "sha512-xjs6WnvJVueSIXMWw0C3oWIgAPpcD03qw43oGOjUXqFktvpNkB73JoKIhS4sCrtQxBdct75qqr4ZL6JiyPcESw==",
"requires": {
"@babel/runtime": "^7.1.2",
"prop-types": "^15.7.2",
"react-is": "^16.6.3"
}
},
"@svgr/babel-plugin-add-jsx-attribute": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz",
@@ -1858,6 +1906,14 @@
"@types/react": "*"
}
},
"@types/react-highlight.js": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/react-highlight.js/-/react-highlight.js-1.0.0.tgz",
"integrity": "sha512-5VXEuo2O9L66y/2GDQSGFTggQkpOvDc/p2ma1KHadu7o/H720HK3Fr83epd4wtQky7B/RoCPat0SKyhlhiUo7A==",
"requires": {
"@types/react": "*"
}
},
"@types/react-router": {
"version": "5.1.8",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.8.tgz",
@@ -3776,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",
@@ -4117,6 +4178,15 @@
"sha.js": "^2.4.8"
}
},
"create-react-context": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz",
"integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==",
"requires": {
"gud": "^1.0.0",
"warning": "^4.0.3"
}
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -5641,6 +5711,11 @@
"strip-eof": "^1.0.0"
}
},
"exenv": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
"integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50="
},
"exit": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
@@ -6510,6 +6585,11 @@
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
},
"gud": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
},
"gzip-size": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
@@ -6654,6 +6734,11 @@
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
},
"highlight.js": {
"version": "9.18.3",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.3.tgz",
"integrity": "sha512-zBZAmhSupHIl5sITeMqIJnYCDfAEc3Gdkqj65wC1lpI468MMQeeQkhcIAvk+RylAkxrCcI9xy9piHiXeQ1BdzQ=="
},
"history": {
"version": "4.10.1",
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
@@ -8006,6 +8091,11 @@
}
}
},
"jquery": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz",
"integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg=="
},
"js-base64": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.2.tgz",
@@ -8155,6 +8245,11 @@
"object.assign": "^4.1.0"
}
},
"keyboard-key": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/keyboard-key/-/keyboard-key-1.1.0.tgz",
"integrity": "sha512-qkBzPTi3rlAKvX7k0/ub44sqOfXeLc/jcnGGmj5c7BJpU8eDrEVPyhCvNYAaoubbsLm9uGWwQJO1ytQK1a9/dQ=="
},
"killable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
@@ -8919,6 +9014,11 @@
"minimist": "^1.2.5"
}
},
"moment": {
"version": "2.27.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
},
"move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
@@ -9912,6 +10012,11 @@
"ts-pnp": "^1.1.6"
}
},
"popper.js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ=="
},
"portfinder": {
"version": "1.0.26",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.26.tgz",
@@ -11353,11 +11458,34 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz",
"integrity": "sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA=="
},
"react-highlight.js": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/react-highlight.js/-/react-highlight.js-1.0.7.tgz",
"integrity": "sha512-OVPKnV0ZvU+V//HExwbV8M9CWy49Eo/9y9pBN2OsNWUFPN6dE4YZBLmJW/5sM2DxI5v/QQLyxOnTnSSfGCP+9Q==",
"requires": {
"highlight.js": "^9.3.0",
"prop-types": "^15.6.0"
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"react-popper": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.7.tgz",
"integrity": "sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww==",
"requires": {
"@babel/runtime": "^7.1.2",
"create-react-context": "^0.3.0",
"deep-equal": "^1.1.1",
"popper.js": "^1.14.4",
"prop-types": "^15.6.1",
"typed-styles": "^0.0.7",
"warning": "^4.0.2"
}
},
"react-router": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
@@ -12097,6 +12225,47 @@
"node-forge": "0.9.0"
}
},
"semantic-ui-css": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/semantic-ui-css/-/semantic-ui-css-2.4.1.tgz",
"integrity": "sha512-Pkp0p9oWOxlH0kODx7qFpIRYpK1T4WJOO4lNnpNPOoWKCrYsfHqYSKgk5fHfQtnWnsAKy7nLJMW02bgDWWFZFg==",
"requires": {
"jquery": "x.*"
}
},
"semantic-ui-react": {
"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.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",
"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.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": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
@@ -12286,6 +12455,11 @@
}
}
},
"shallowequal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
@@ -13454,6 +13628,11 @@
"mime-types": "~2.1.24"
}
},
"typed-styles": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz",
"integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q=="
},
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
@@ -13755,6 +13934,14 @@
"makeerror": "1.0.x"
}
},
"warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
"requires": {
"loose-envify": "^1.0.0"
}
},
"watchpack": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz",

View File

@@ -4,7 +4,8 @@
"homepage": ".",
"private": true,
"dependencies": {
"@sasjs/adapter": "*",
"@sasjs/adapter": "^1.3.13",
"@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",
@@ -24,7 +25,9 @@
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"deploy": "npm run build && rsync -avhe ssh ./build/* --delete kriaco@sas.analytium.co.uk:/var/www/html/kriaco/sasjs-tests"
"update:adapter": "cd .. && npm run package:lib && cd sasjs-tests && npm i ../build/sasjs-adapter-5.0.0.tgz",
"deploy:tests": "npm run build && rsync -avhe ssh ./build/* --delete $SSH_ACCOUNT:$DEPLOY_PATH",
"deploy": "npm run update:adapter && npm run deploy:tests"
},
"eslintConfig": {
"extends": "react-app"

View File

@@ -6,6 +6,7 @@
"appLoc": "/Public/app",
"serverType": "SASVIYA",
"debug": false,
"contextName": null
"contextName": "SharedCompute",
"useComputeApi": true
}
}

View File

@@ -1,102 +0,0 @@
.app {
padding: 16px;
.controls {
display: flex;
align-items: center;
.debug-toggle,
.app-loc-input,
.submit-button {
margin: 16px 0;
}
.row {
margin: 16px;
&.app-loc {
width: 20vw;
}
}
.submit-button {
padding: 16px;
font-size: 1.25em;
}
.app-loc-input {
width: 100%;
}
}
.debug-toggle {
display: inline-flex;
justify-content: center;
align-items: center;
.label {
padding: 0 8px;
font-size: 1.25em;
}
}
$height: 40px;
$width: 70px;
.switch {
position: relative;
display: inline-flex;
width: $width;
height: $height;
input[type="checkbox"] {
display: none;
}
input:checked + .knob {
animation: colorChange 0.4s linear forwards;
}
input:checked + .knob:before {
animation: turnON 0.4s linear forwards;
}
}
@keyframes colorChange {
from {
background-color: #ccc;
}
50% {
background-color: #a4d9ad;
}
to {
background-color: #4bd663;
}
}
@keyframes turnON {
from {
transform: translateX(0px);
}
to {
transform: translateX($width - ($height * 0.99));
box-shadow: -10px 0px 44px 0px #434343;
}
}
.knob {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
border-radius: $height;
}
.knob:before {
position: absolute;
background-color: white;
content: "";
left: $height * 0.1;
top: $height * 0.1;
width: ($height * 0.8);
height: ($height * 0.8);
border-radius: 50%;
}
}

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

@@ -1,56 +1,30 @@
import React, { ReactElement, useState, useContext, useEffect } from "react";
import "./App.scss";
import TestSuiteRunner from "./TestSuiteRunner";
import { AppContext } from "./context/AppContext";
import { TestSuiteRunner, TestSuite, AppContext } from "@sasjs/test-framework";
import { basicTests } from "./testSuites/Basic";
import { sendArrTests, sendObjTests } from "./testSuites/RequestData";
import { specialCaseTests } from "./testSuites/SpecialCases";
import { sasjsRequestTests } from "./testSuites/SasjsRequests";
import "@sasjs/test-framework/dist/index.css";
const App = (): ReactElement<{}> => {
const [appLoc, setAppLoc] = useState("");
const [debug, setDebug] = useState(false);
const { adapter } = useContext(AppContext);
const { adapter, config } = useContext(AppContext);
const [testSuites, setTestSuites] = useState<TestSuite[]>([]);
useEffect(() => {
if (adapter) {
adapter.setDebugState(debug);
setTestSuites([
basicTests(adapter, config.userName, config.password),
sendArrTests(adapter),
sendObjTests(adapter),
specialCaseTests(adapter),
sasjsRequestTests(adapter)
]);
}
}, [debug, adapter]);
useEffect(() => {
if (appLoc && adapter) {
adapter.setSASjsConfig({ ...adapter.getSasjsConfig(), appLoc });
}
}, [appLoc, adapter]);
useEffect(() => {
setAppLoc(adapter.getSasjsConfig().appLoc);
}, [adapter]);
}, [adapter, config]);
return (
<div className="app">
<div className="controls">
<div className="row">
<label>Debug</label>
<div className="debug-toggle">
<label className="switch">
<input
type="checkbox"
onChange={(e) => setDebug(e.target.checked)}
/>
<span className="knob"></span>
</label>
</div>
</div>
<div className="row app-loc">
<label>App Loc</label>
<input
type="text"
className="app-loc-input"
value={appLoc}
onChange={(e) => setAppLoc(e.target.value)}
placeholder="AppLoc"
/>
</div>
</div>
{adapter && <TestSuiteRunner adapter={adapter} />}
{adapter && testSuites && <TestSuiteRunner testSuites={testSuites} />}
</div>
);
};

View File

@@ -1,6 +1,6 @@
import React, { ReactElement, useState, useCallback, useContext } from "react";
import "./Login.scss";
import { AppContext } from "./context/AppContext";
import { AppContext } from "@sasjs/test-framework";
import { Redirect } from "react-router-dom";
const Login = (): ReactElement<{}> => {
@@ -11,8 +11,8 @@ const Login = (): ReactElement<{}> => {
const handleSubmit = useCallback(
(e) => {
e.preventDefault();
appContext.adapter.logIn(username, password).then(() => {
appContext.setIsLoggedIn(true);
appContext.adapter.logIn(username, password).then((res) => {
appContext.setIsLoggedIn(res.isLoggedIn);
});
},
[username, password, appContext]

View File

@@ -1,6 +1,6 @@
import React, { ReactElement, useContext, FunctionComponent } from "react";
import { Redirect, Route } from "react-router-dom";
import { AppContext } from "./context/AppContext";
import { AppContext } from "@sasjs/test-framework";
interface PrivateRouteProps {
component: FunctionComponent;

View File

@@ -1,19 +0,0 @@
.button-container {
display: flex;
justify-content: center;
align-items: center;
padding: 16px;
.loading-spinner {
margin: 0 8px;
}
.submit-button {
padding: 10px;
min-height: 80px;
font-size: 2em;
display: flex;
justify-content: center;
align-items: center;
}
}

View File

@@ -1,126 +0,0 @@
import React, { useEffect, useState, ReactElement, useContext } from "react";
import TestSuiteComponent from "./components/TestSuite";
import TestSuiteCard from "./components/TestSuiteCard";
import { TestSuite, Test } from "./types";
import { basicTests } from "./testSuites/Basic";
import "./TestSuiteRunner.scss";
import SASjs from "@sasjs/adapter";
import { AppContext } from "./context/AppContext";
import { sendArrTests, sendObjTests } from "./testSuites/RequestData";
import { specialCaseTests } from "./testSuites/SpecialCases";
import { sasjsRequestTests } from "./testSuites/SasjsRequests";
interface TestSuiteRunnerProps {
adapter: SASjs;
}
const TestSuiteRunner = (
props: TestSuiteRunnerProps
): ReactElement<TestSuiteRunnerProps> => {
const { adapter } = props;
const { config } = useContext(AppContext);
const [testSuites, setTestSuites] = useState<TestSuite[]>([]);
const [runTests, setRunTests] = useState(false);
const [completedTestSuites, setCompletedTestSuites] = useState<
{
name: string;
completedTests: {
test: Test;
result: boolean;
error: Error | null;
executionTime: number;
}[];
}[]
>([]);
const [currentTestSuite, setCurrentTestSuite] = useState<TestSuite | null>(
(null as unknown) as TestSuite
);
useEffect(() => {
if (adapter) {
setTestSuites([
basicTests(adapter, config.userName, config.password),
sendArrTests(adapter),
sendObjTests(adapter),
specialCaseTests(adapter),
sasjsRequestTests(adapter),
]);
setCompletedTestSuites([]);
}
}, [adapter]);
useEffect(() => {
if (testSuites.length) {
setCurrentTestSuite(testSuites[0]);
}
}, [testSuites]);
useEffect(() => {
if (runTests) {
setCompletedTestSuites([]);
setCurrentTestSuite(testSuites[0]);
}
}, [runTests, testSuites]);
return (
<>
<div className="button-container">
<button
className={runTests ? "submit-button disabled" : "submit-button"}
onClick={() => setRunTests(true)}
disabled={runTests}
>
{runTests ? (
<>
<div className="loading-spinner"></div>Running tests...
</>
) : (
"Run tests!"
)}
</button>
</div>
{completedTestSuites.map((completedTestSuite, index) => {
return (
<TestSuiteCard
key={index}
tests={completedTestSuite.completedTests}
name={completedTestSuite.name}
/>
);
})}
{currentTestSuite && runTests && (
<TestSuiteComponent
{...currentTestSuite}
onCompleted={(
name,
completedTests: {
test: Test;
result: boolean;
error: Error | null;
executionTime: number;
}[]
) => {
const currentIndex = testSuites.indexOf(currentTestSuite);
const nextIndex =
currentIndex < testSuites.length - 1 ? currentIndex + 1 : -1;
if (nextIndex >= 0) {
setCurrentTestSuite(testSuites[nextIndex]);
} else {
setCurrentTestSuite(null);
}
const newCompletedTestSuites = [
...completedTestSuites,
{ name, completedTests },
];
setCompletedTestSuites(newCompletedTestSuites);
if (newCompletedTestSuites.length === testSuites.length) {
setRunTests(false);
}
}}
/>
)}
</>
);
};
export default TestSuiteRunner;

View File

@@ -1,79 +0,0 @@
import React, { ReactElement, useEffect, useState } from "react";
import TestCard from "./TestCard";
import { start } from "repl";
interface TestProps {
title: string;
description: string;
beforeTest?: (...args: any) => Promise<any>;
afterTest?: (...args: any) => Promise<any>;
test: (context: any) => Promise<any>;
assertion: (...args: any) => boolean;
onCompleted: (payload: {
result: boolean;
error: Error | null;
executionTime: number;
}) => void;
context: any;
}
const getStatus = (isRunning: boolean, isPassed: boolean): string => {
return isRunning ? "running" : isPassed ? "passed" : "failed";
};
const Test = (props: TestProps): ReactElement<TestProps> => {
const {
title,
description,
test,
beforeTest,
afterTest,
assertion,
onCompleted,
context,
} = props;
const beforeTestFunction = beforeTest ? beforeTest : () => Promise.resolve();
const afterTestFunction = afterTest ? afterTest : () => Promise.resolve();
const [isRunning, setIsRunning] = useState(false);
const [isPassed, setIsPassed] = useState(false);
useEffect(() => {
if (test && assertion) {
const startTime = new Date().valueOf();
setIsRunning(true);
setIsPassed(false);
beforeTestFunction()
.then(() => test(context))
.then((res) => {
setIsRunning(false);
setIsPassed(assertion(res, context));
return Promise.resolve(assertion(res, context));
})
.then((testResult) => {
afterTestFunction();
const endTime = new Date().valueOf();
const executionTime = (endTime - startTime) / 1000;
onCompleted({ result: testResult, error: null, executionTime });
})
.catch((e) => {
setIsRunning(false);
setIsPassed(false);
console.error(e);
const endTime = new Date().valueOf();
const executionTime = (endTime - startTime) / 1000;
onCompleted({ result: false, error: e, executionTime });
});
}
}, [test, assertion]);
return (
<TestCard
title={title}
description={description}
status={getStatus(isRunning, isPassed)}
error={null}
/>
);
};
export default Test;

View File

@@ -1,62 +0,0 @@
.test {
display: inline-flex;
padding: 8px;
margin: 8px;
flex-direction: column;
border: 1px solid #ddd;
border-radius: 5px;
width: 20%;
.title {
font-weight: bold;
color: #eee;
font-size: 1em;
}
.description,
.execution-time {
color: #c6c0c0;
padding: 8px 0;
font-size: 0.8em;
}
.description {
min-height: 50px;
}
.execution-time {
color: #f9e804;
}
.icon {
border-radius: 50%;
width: 12px;
height: 12px;
margin-right: 8px;
display: inline-block;
&.running {
background-color: yellow;
}
&.passed {
background-color: green;
}
&.failed {
background-color: red;
}
}
}
@media only screen and (max-width: 900px) {
.test {
width: 90%;
}
}
@media only screen and (min-width: 901px) and (max-width: 1280px) {
.test {
width: 30%;
}
}

View File

@@ -1,43 +0,0 @@
import React, { ReactElement } from "react";
import "./TestCard.scss";
interface TestCardProps {
title: string;
description: string;
status: string;
error: Error | null;
executionTime?: number;
}
const TestCard = (props: TestCardProps): ReactElement<TestCardProps> => {
const { title, description, status, error, executionTime } = props;
return (
<div className="test">
<code className="title">{title}</code>
<span className="description">{description}</span>
<span className="execution-time">
{executionTime ? executionTime.toFixed(2) + "s" : ""}
</span>
{status === "running" && (
<div>
<span className="icon running"></span>Running...
</div>
)}
{status === "passed" && (
<div>
<span className="icon passed"></span>Passed
</div>
)}
{status === "failed" && (
<>
<div>
<span className="icon failed"></span>Failed
</div>
{!!error && <code>{error.message}</code>}
</>
)}
</div>
);
};
export default TestCard;

View File

@@ -1,106 +0,0 @@
import React, { ReactElement, useState, useEffect } from "react";
import "./TestSuiteCard.scss";
import { Test } from "../types";
import TestComponent from "./Test";
import TestCard from "./TestCard";
interface TestSuiteProps {
name: string;
tests: Test[];
beforeAll?: (...args: any) => Promise<any>;
afterAll?: (...args: any) => Promise<any>;
onCompleted: (
name: string,
completedTests: {
test: Test;
result: boolean;
error: Error | null;
executionTime: number;
}[]
) => void;
}
const TestSuite = (props: TestSuiteProps): ReactElement<TestSuiteProps> => {
const { name, tests, beforeAll, afterAll, onCompleted } = props;
const [context, setContext] = useState<any>(null);
const [completedTests, setCompletedTests] = useState<
{
test: Test;
result: boolean;
error: Error | null;
executionTime: number;
}[]
>([]);
const [currentTest, setCurrentTest] = useState<Test | null>(
(null as unknown) as Test
);
useEffect(() => {
if (beforeAll) {
beforeAll().then((data) => setContext({ data }));
}
}, [beforeAll]);
useEffect(() => {
if (tests.length) {
setCurrentTest(tests[0]);
}
setCompletedTests([]);
setContext(null);
}, [tests]);
return (!!beforeAll && !!context) || !beforeAll ? (
<div className="test-suite">
<div className="test-suite-name running">{name}</div>
{currentTest && (
<TestComponent
{...currentTest}
context={context}
onCompleted={(completedTest) => {
const newCompleteTests = [
...completedTests,
{
test: currentTest,
result: completedTest.result,
error: completedTest.error,
executionTime: completedTest.executionTime,
},
];
setCompletedTests(newCompleteTests);
const currentIndex = tests.indexOf(currentTest);
const nextIndex =
currentIndex < tests.length - 1 ? currentIndex + 1 : -1;
if (nextIndex >= 0) {
setCurrentTest(tests[nextIndex]);
} else {
setCurrentTest(null);
}
if (newCompleteTests.length === tests.length) {
if (afterAll) {
afterAll().then(() => onCompleted(name, newCompleteTests));
} else {
onCompleted(name, newCompleteTests);
}
}
}}
/>
)}
{completedTests.map((completedTest, index) => {
const { test, result, error } = completedTest;
const { title, description } = test;
return (
<TestCard
key={index}
title={title}
description={description}
status={result === true ? "passed" : "failed"}
error={error}
/>
);
})}
</div>
) : (
<></>
);
};
export default TestSuite;

View File

@@ -1,19 +0,0 @@
.test-suite {
.test-suite-name {
font-size: 1.5em;
font-weight: bold;
color: #1f2027;
&.passed {
color: green;
}
&.failed {
color: red;
}
&.running {
color: yellow;
}
}
}

View File

@@ -1,44 +0,0 @@
import React, { ReactElement } from "react";
import "./TestSuiteCard.scss";
import { Test } from "../types";
import TestCard from "./TestCard";
interface TestSuiteCardProps {
name: string;
tests: {
test: Test;
result: boolean;
error: Error | null;
executionTime: number;
}[];
}
const TestSuiteCard = (
props: TestSuiteCardProps
): ReactElement<TestSuiteCardProps> => {
const { name, tests } = props;
const overallStatus = tests.map((t) => t.result).reduce((x, y) => x && y);
return (
<div className="test-suite">
<div className={`test-suite-name ${overallStatus ? "passed" : "failed"}`}>
{name}
</div>
{tests.map((completedTest, index) => {
const { test, result, error, executionTime } = completedTest;
const { title, description } = test;
return (
<TestCard
key={index}
title={title}
description={description}
status={result === true ? "passed" : "failed"}
error={error}
executionTime={executionTime}
/>
);
})}
</div>
);
};
export default TestSuiteCard;

View File

@@ -1,53 +0,0 @@
import React, { createContext, useState, useEffect, ReactNode } from "react";
import SASjs from "@sasjs/adapter";
export const AppContext = createContext<{
config: any;
sasJsConfig: any;
isLoggedIn: boolean;
setIsLoggedIn: (value: boolean) => void;
adapter: SASjs;
}>({
config: null,
sasJsConfig: null,
isLoggedIn: false,
setIsLoggedIn: (null as unknown) as (value: boolean) => void,
adapter: (null as unknown) as SASjs,
});
export const AppProvider = (props: { children: ReactNode }) => {
const [config, setConfig] = useState<{ sasJsConfig: any }>({
sasJsConfig: null,
});
const [adapter, setAdapter] = useState<SASjs>((null as unknown) as SASjs);
const [isLoggedIn, setIsLoggedIn] = useState(false);
useEffect(() => {
fetch("config.json")
.then((res) => res.json())
.then((configJson: any) => {
setConfig(configJson);
const sasjs = new SASjs(configJson.sasJsConfig);
setAdapter(sasjs);
sasjs.checkSession().then((response) => {
setIsLoggedIn(response.isLoggedIn);
});
});
}, []);
return (
<AppContext.Provider
value={{
config,
sasJsConfig: config.sasJsConfig,
isLoggedIn,
setIsLoggedIn,
adapter,
}}
>
{props.children}
</AppContext.Provider>
);
};

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

@@ -3,7 +3,7 @@ import ReactDOM from "react-dom";
import { Route, HashRouter, Switch } from "react-router-dom";
import "./index.scss";
import * as serviceWorker from "./serviceWorker";
import { AppProvider } from "./context/AppContext";
import { AppProvider } from "@sasjs/test-framework";
import PrivateRoute from "./PrivateRoute";
import Login from "./Login";
import App from "./App";

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

@@ -1,5 +1,5 @@
import SASjs, { ServerType, SASjsConfig } from "@sasjs/adapter";
import { TestSuite } from "../types";
import { TestSuite } from "@sasjs/test-framework";
const defaultConfig: SASjsConfig = {
serverUrl: window.location.origin,
@@ -9,6 +9,7 @@ const defaultConfig: SASjsConfig = {
serverType: ServerType.SASViya,
debug: true,
contextName: "SAS Job Execution compute context",
useComputeApi: false
};
const customConfig = {
@@ -17,7 +18,7 @@ const customConfig = {
pathSASViya: "viya",
appLoc: "/Public/seedapp",
serverType: ServerType.SAS9,
debug: false,
debug: false
};
export const basicTests = (
@@ -34,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",
@@ -53,7 +54,7 @@ export const basicTests = (
sasjsConfig.serverType === defaultConfig.serverType &&
sasjsConfig.debug === defaultConfig.debug
);
},
}
},
{
title: "Custom config",
@@ -71,7 +72,7 @@ export const basicTests = (
sasjsConfig.serverType === customConfig.serverType &&
sasjsConfig.debug === customConfig.debug
);
},
}
},
{
title: "Config overrides",
@@ -91,7 +92,7 @@ export const basicTests = (
sasjsConfig.serverType === defaultConfig.serverType &&
sasjsConfig.debug === false
);
},
},
],
}
}
]
});

View File

@@ -1,10 +1,10 @@
import SASjs from "@sasjs/adapter";
import { TestSuite } from "../types";
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,20 +67,19 @@ 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",
description:
"Should error out with long string values over 32765 characters",
test: () => {
return adapter
.request("common/sendArr", getLongStringData(32767))
.catch((e) => e);
const data = getLongStringData(32767);
return adapter.request("common/sendArr", data).catch((e) => e);
},
assertion: (error: any) => {
return !!error && !!error.MESSAGE;
},
}
},
{
title: "Single numeric value",
@@ -90,7 +89,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
},
assertion: (res: any) => {
return res.table1[0][0] === numericData.table1[0].col1;
},
}
},
{
title: "Multiple columns",
@@ -105,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",
@@ -130,7 +129,7 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
res.table1[index][3] === multipleRowsWithNulls.table1[index].col4;
});
return result;
},
}
},
{
title: "Multiple columns with nulls",
@@ -159,9 +158,9 @@ export const sendArrTests = (adapter: SASjs): TestSuite => ({
(multipleColumnsWithNulls.table1[index].col4 || "");
});
return result;
},
},
],
}
}
]
});
export const sendObjTests = (adapter: SASjs): TestSuite => ({
@@ -172,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",
@@ -186,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",
@@ -198,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",
@@ -211,7 +210,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
},
assertion: (error: any) => {
return !!error && !!error.MESSAGE;
},
}
},
{
title: "Single numeric value",
@@ -221,7 +220,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
},
assertion: (res: any) => {
return res.table1[0].COL1 === numericData.table1[0].col1;
},
}
},
{
@@ -233,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",
@@ -248,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",
@@ -273,7 +272,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
res.table1[index].COL4 === multipleRowsWithNulls.table1[index].col4;
});
return result;
},
}
},
{
title: "Multiple columns with nulls",
@@ -302,7 +301,7 @@ export const sendObjTests = (adapter: SASjs): TestSuite => ({
(multipleColumnsWithNulls.table1[index].col4 || "");
});
return result;
},
},
],
}
}
]
});

View File

@@ -1,5 +1,5 @@
import SASjs from "@sasjs/adapter";
import { TestSuite } from "../types";
import { TestSuite } from "@sasjs/test-framework";
const data: any = { table1: [{ col1: "first col value" }] };
@@ -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

@@ -1,5 +1,5 @@
import SASjs from "@sasjs/adapter";
import { TestSuite } from "../types";
import { TestSuite } from "@sasjs/test-framework";
const specialCharData: any = {
table1: [
@@ -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,15 +0,0 @@
export interface Test {
title: string;
description: string;
beforeTest?: (...args: any) => Promise<any>;
afterTest?: (...args: any) => Promise<any>;
test: (context?: any) => Promise<any>;
assertion: (...args: any) => boolean;
}
export interface TestSuite {
name: string;
tests: Test[];
beforeAll?: (...args: any) => Promise<any>;
afterAll?: (...args: any) => Promise<any>;
}

View File

@@ -1,23 +0,0 @@
export const uploadFile = (file: File, fileName: string, url: string) => {
return new Promise((resolve, reject) => {
const data = new FormData();
data.append("file", file);
data.append("filename", fileName);
const xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
let response: any;
try {
response = JSON.parse(this.responseText);
} catch (e) {
reject(e);
}
resolve(response);
}
});
xhr.open("POST", url);
xhr.setRequestHeader("cache-control", "no-cache");
xhr.send(data);
});
};

100
src/FileUploader.ts Normal file
View File

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

170
src/SessionManager.ts Normal file
View File

@@ -0,0 +1,170 @@
import { Session, Context, CsrfToken } from './types'
import { asyncForEach, makeRequest } from './utils'
const MAX_SESSION_COUNT = 1
export class SessionManager {
constructor(
private serverUrl: string,
private contextName: string,
private setCsrfToken: (csrfToken: CsrfToken) => void
) {}
private sessions: Session[] = []
private currentContext: Context | null = null
private csrfToken: CsrfToken | null = null
async getSession(accessToken?: string) {
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 asyncForEach(new Array(MAX_SESSION_COUNT), async () => {
const createdSession = await this.createAndWaitForSession(accessToken)
this.sessions.push(createdSession)
})
}
}
private async createAndWaitForSession(accessToken?: string) {
const createSessionRequest = {
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
}
private async setCurrentContext(accessToken?: string) {
if (!this.currentContext) {
const { result: contexts } = await this.request<{
items: Context[]
}>(`${this.serverUrl}/compute/contexts`, {
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
}
}
private getHeaders(accessToken?: string) {
const headers: any = {
'Content-Type': 'application/json'
}
if (accessToken) {
headers.Authorization = `Bearer ${accessToken}`
}
return headers
}
private async waitForSession(
session: Session,
etag: string | null,
accessToken?: string,
silent = false
) {
let sessionState = session.state
const headers: any = {
...this.getHeaders(accessToken),
'If-None-Match': etag
}
const stateLink = session.links.find((l: any) => l.rel === 'state')
return new Promise(async (resolve, _) => {
if (sessionState === 'pending') {
if (stateLink) {
if (!silent) {
console.log('Polling session status... \n')
}
const { result: state } = await this.request<string>(
`${this.serverUrl}${stateLink.href}?wait=30`,
{
headers
},
'text'
)
sessionState = state.trim()
if (!silent) {
console.log(`Current state: ${sessionState}\n`)
}
resolve(sessionState)
}
} else {
resolve(sessionState)
}
})
}
private async request<T>(
url: string,
options: RequestInit,
contentType: 'text' | 'json' = 'json'
) {
if (this.csrfToken) {
options.headers = {
...options.headers,
[this.csrfToken.headerName]: this.csrfToken.value
}
}
return await makeRequest<T>(
url,
options,
(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,11 +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;
id: string
name: string
uri: string
createdBy: string
code?: string
links: Link[]
results: JobResult
error?: any
}

View File

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

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