mirror of
https://github.com/sasjs/adapter.git
synced 2026-01-11 06:10:05 +00:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ec6ee2db9 | ||
|
|
e6ab5f918f | ||
|
|
951e119c08 | ||
|
|
d22d9e1039 | ||
|
|
6e276e2e26 | ||
|
|
eec973efa1 | ||
|
|
2c763e38ae | ||
|
|
4957bc5b05 | ||
|
|
f6b1eecb42 | ||
|
|
7ae2a4d2c6 | ||
|
|
f86d20b723 | ||
|
|
7a1cce193e | ||
|
|
05539fff11 | ||
|
|
5f74b6990c | ||
|
|
a53b1ab421 | ||
|
|
3fdc56cdfe | ||
|
|
e318d8816e | ||
|
|
2c9cce8bf8 | ||
|
|
58d69a62d6 | ||
|
|
c9c9754916 | ||
|
|
e056ca21fe | ||
|
|
0a77ebf5c5 | ||
|
|
d5791a75cd | ||
|
|
3da5aba62a | ||
|
|
12835893b1 | ||
|
|
3f40737514 | ||
|
|
df86b2e700 | ||
|
|
0eb9bc43ff | ||
|
|
5fce25d58a | ||
|
|
7ee9335183 | ||
|
|
07695bdb85 | ||
|
|
26c8946fd5 | ||
|
|
fc1d54d105 | ||
|
|
a318d61f83 | ||
|
|
cc5a0cbec3 | ||
|
|
d932d9ea0a | ||
|
|
e3edace882 | ||
|
|
29d9df5792 | ||
|
|
1f970e1102 | ||
|
|
1552762d28 | ||
|
|
ef39252b0b | ||
|
|
3f3f8f4531 | ||
|
|
33879ee998 | ||
|
|
cc8271438b | ||
|
|
a064bc086d | ||
|
|
d5de6b50a9 | ||
|
|
f1184346d8 | ||
|
|
337fe5e988 | ||
|
|
c23e6352e2 | ||
|
|
57ce0ae35f | ||
|
|
115caec761 | ||
|
|
c626c57662 | ||
|
|
82b14fad14 | ||
|
|
755bf7d07c |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
"tabWidth": 2,
|
"tabWidth": 2,
|
||||||
"semi": true,
|
"semi": false,
|
||||||
"singleQuote": false
|
"singleQuote": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,83 +1,14 @@
|
|||||||
# Contributing
|
# 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.
|
||||||
|
|
||||||
```
|
This repository contains a suite of tests built using [@sasjs/test-framework](https://github.com/sasjs/test-framework).
|
||||||
# the following creates a tarball in the build folder of SASjs
|
|
||||||
npm run-script package:lib
|
|
||||||
|
|
||||||
# now go to your app and run:
|
Detailed instructions for creating and running the tests can be found [here](https://github.com/sasjs/adapter/blob/master/sasjs-tests/README.md).
|
||||||
npm install ../sasjs/build/<tarball filename>
|
|
||||||
```
|
|
||||||
|
|
||||||
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
|
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.
|
||||||
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)
|
|
||||||
```
|
|
||||||
|
|
||||||
# Viya
|
|
||||||
|
|
||||||
```
|
|
||||||
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)
|
|
||||||
```
|
|
||||||
|
|
||||||
The above services will return anything you send. To run the tests simply launch `npm run cypress`.
|
|
||||||
|
|||||||
@@ -43,6 +43,6 @@ You now have a simple web app with a backend service!
|
|||||||
|
|
||||||
# More resources
|
# More resources
|
||||||
|
|
||||||
For more information specific to this adapter you can check out this [user guide](https://sasjs.io/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.
|
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
223
docs/classes/reflection-640.reflection-158.fileuploader.html
Normal file
223
docs/classes/reflection-640.reflection-158.fileuploader.html
Normal file
File diff suppressed because one or more lines are too long
296
docs/classes/reflection-640.reflection-158.sas9apiclient.html
Normal file
296
docs/classes/reflection-640.reflection-158.sas9apiclient.html
Normal file
File diff suppressed because one or more lines are too long
1038
docs/classes/reflection-640.reflection-158.sasjs.html
Normal file
1038
docs/classes/reflection-640.reflection-158.sasjs.html
Normal file
File diff suppressed because one or more lines are too long
1006
docs/classes/reflection-640.reflection-158.sasviyaapiclient.html
Normal file
1006
docs/classes/reflection-640.reflection-158.sasviyaapiclient.html
Normal file
File diff suppressed because one or more lines are too long
259
docs/classes/reflection-640.reflection-158.sessionmanager.html
Normal file
259
docs/classes/reflection-640.reflection-158.sessionmanager.html
Normal file
File diff suppressed because one or more lines are too long
223
docs/classes/reflection-641.reflection-158.fileuploader.html
Normal file
223
docs/classes/reflection-641.reflection-158.fileuploader.html
Normal file
File diff suppressed because one or more lines are too long
296
docs/classes/reflection-641.reflection-158.sas9apiclient.html
Normal file
296
docs/classes/reflection-641.reflection-158.sas9apiclient.html
Normal file
File diff suppressed because one or more lines are too long
1038
docs/classes/reflection-641.reflection-158.sasjs.html
Normal file
1038
docs/classes/reflection-641.reflection-158.sasjs.html
Normal file
File diff suppressed because one or more lines are too long
1012
docs/classes/reflection-641.reflection-158.sasviyaapiclient.html
Normal file
1012
docs/classes/reflection-641.reflection-158.sasviyaapiclient.html
Normal file
File diff suppressed because one or more lines are too long
259
docs/classes/reflection-641.reflection-158.sessionmanager.html
Normal file
259
docs/classes/reflection-641.reflection-158.sessionmanager.html
Normal file
File diff suppressed because one or more lines are too long
223
docs/classes/reflection-644.reflection-162.fileuploader.html
Normal file
223
docs/classes/reflection-644.reflection-162.fileuploader.html
Normal file
File diff suppressed because one or more lines are too long
296
docs/classes/reflection-644.reflection-162.sas9apiclient.html
Normal file
296
docs/classes/reflection-644.reflection-162.sas9apiclient.html
Normal file
File diff suppressed because one or more lines are too long
1038
docs/classes/reflection-644.reflection-162.sasjs.html
Normal file
1038
docs/classes/reflection-644.reflection-162.sasjs.html
Normal file
File diff suppressed because one or more lines are too long
1006
docs/classes/reflection-644.reflection-162.sasviyaapiclient.html
Normal file
1006
docs/classes/reflection-644.reflection-162.sasviyaapiclient.html
Normal file
File diff suppressed because one or more lines are too long
259
docs/classes/reflection-644.reflection-162.sessionmanager.html
Normal file
259
docs/classes/reflection-644.reflection-162.sessionmanager.html
Normal file
File diff suppressed because one or more lines are too long
223
docs/classes/reflection-648.reflection-166.fileuploader.html
Normal file
223
docs/classes/reflection-648.reflection-166.fileuploader.html
Normal file
File diff suppressed because one or more lines are too long
296
docs/classes/reflection-648.reflection-166.sas9apiclient.html
Normal file
296
docs/classes/reflection-648.reflection-166.sas9apiclient.html
Normal file
File diff suppressed because one or more lines are too long
1064
docs/classes/reflection-648.reflection-166.sasjs.html
Normal file
1064
docs/classes/reflection-648.reflection-166.sasjs.html
Normal file
File diff suppressed because one or more lines are too long
1006
docs/classes/reflection-648.reflection-166.sasviyaapiclient.html
Normal file
1006
docs/classes/reflection-648.reflection-166.sasviyaapiclient.html
Normal file
File diff suppressed because one or more lines are too long
259
docs/classes/reflection-648.reflection-166.sessionmanager.html
Normal file
259
docs/classes/reflection-648.reflection-166.sessionmanager.html
Normal file
File diff suppressed because one or more lines are too long
223
docs/classes/reflection-678.reflection-166.fileuploader.html
Normal file
223
docs/classes/reflection-678.reflection-166.fileuploader.html
Normal file
File diff suppressed because one or more lines are too long
296
docs/classes/reflection-678.reflection-166.sas9apiclient.html
Normal file
296
docs/classes/reflection-678.reflection-166.sas9apiclient.html
Normal file
File diff suppressed because one or more lines are too long
1170
docs/classes/reflection-678.reflection-166.sasjs.html
Normal file
1170
docs/classes/reflection-678.reflection-166.sasjs.html
Normal file
File diff suppressed because one or more lines are too long
1118
docs/classes/reflection-678.reflection-166.sasviyaapiclient.html
Normal file
1118
docs/classes/reflection-678.reflection-166.sasviyaapiclient.html
Normal file
File diff suppressed because one or more lines are too long
259
docs/classes/reflection-678.reflection-166.sessionmanager.html
Normal file
259
docs/classes/reflection-678.reflection-166.sessionmanager.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
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
106
docs/modules/reflection-640.html
Normal file
106
docs/modules/reflection-640.html
Normal file
File diff suppressed because one or more lines are too long
128
docs/modules/reflection-640.reflection-158.html
Normal file
128
docs/modules/reflection-640.reflection-158.html
Normal file
File diff suppressed because one or more lines are too long
106
docs/modules/reflection-641.html
Normal file
106
docs/modules/reflection-641.html
Normal file
File diff suppressed because one or more lines are too long
128
docs/modules/reflection-641.reflection-158.html
Normal file
128
docs/modules/reflection-641.reflection-158.html
Normal file
File diff suppressed because one or more lines are too long
106
docs/modules/reflection-644.html
Normal file
106
docs/modules/reflection-644.html
Normal file
File diff suppressed because one or more lines are too long
128
docs/modules/reflection-644.reflection-162.html
Normal file
128
docs/modules/reflection-644.reflection-162.html
Normal file
File diff suppressed because one or more lines are too long
106
docs/modules/reflection-648.html
Normal file
106
docs/modules/reflection-648.html
Normal file
File diff suppressed because one or more lines are too long
128
docs/modules/reflection-648.reflection-166.html
Normal file
128
docs/modules/reflection-648.reflection-166.html
Normal file
File diff suppressed because one or more lines are too long
106
docs/modules/reflection-678.html
Normal file
106
docs/modules/reflection-678.html
Normal file
File diff suppressed because one or more lines are too long
128
docs/modules/reflection-678.reflection-166.html
Normal file
128
docs/modules/reflection-678.reflection-166.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
31
package-lock.json
generated
31
package-lock.json
generated
@@ -1103,9 +1103,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"supports-color": {
|
"supports-color": {
|
||||||
"version": "7.1.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||||
"integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
|
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"has-flag": "^4.0.0"
|
"has-flag": "^4.0.0"
|
||||||
@@ -1648,9 +1648,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/jest": {
|
"@types/jest": {
|
||||||
"version": "26.0.10",
|
"version": "26.0.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.13.tgz",
|
||||||
"integrity": "sha512-i2m0oyh8w/Lum7wWK/YOZJakYF8Mx08UaKA1CtbmFeDquVhAEdA7znacsVSf2hJ1OQ/OfVMGN90pw/AtzF8s/Q==",
|
"integrity": "sha512-sCzjKow4z9LILc6DhBvn5AkIfmQzDZkgtVVKmGwVrs5tuid38ws281D4l+7x1kP487+FlKDh5kfMZ8WSPAdmdA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"jest-diff": "^25.2.1",
|
"jest-diff": "^25.2.1",
|
||||||
@@ -5904,9 +5904,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"supports-color": {
|
"supports-color": {
|
||||||
"version": "7.1.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||||
"integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
|
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"has-flag": "^4.0.0"
|
"has-flag": "^4.0.0"
|
||||||
@@ -12494,12 +12494,6 @@
|
|||||||
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
|
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"prettier": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.1.tgz",
|
|
||||||
"integrity": "sha512-9bY+5ZWCfqj3ghYBLxApy2zf6m+NJo5GzmLTpr9FsApsfjriNnS2dahWReHMi7qNPhhHl9SYHJs2cHZLgexNIw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"pretty-format": {
|
"pretty-format": {
|
||||||
"version": "25.5.0",
|
"version": "25.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz",
|
||||||
@@ -14421,11 +14415,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"typedoc-neo-theme": {
|
"typedoc-neo-theme": {
|
||||||
"version": "1.0.9",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/typedoc-neo-theme/-/typedoc-neo-theme-1.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/typedoc-neo-theme/-/typedoc-neo-theme-1.0.10.tgz",
|
||||||
"integrity": "sha512-g6ITZAkRBTVyTQnyq8LfYN3Y2fOoLnND3gG3nK7lcRthQ1kU4pTEK711KCEaceSK93qB5EgtLZkjv7NOg7qRTw==",
|
"integrity": "sha512-PmbqBFNyG8atVHIJ1Wgy8gm+7vR7Anl7cewOxI8qxo7M4Jk7vaY9pqJSnOm9Mfkk7k73Pw3mJVl3jxP4s0r/oA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
"lunr": "^2.3.8",
|
||||||
"typedoc": "~0.17.8"
|
"typedoc": "~0.17.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/isomorphic-fetch": "0.0.35",
|
"@types/isomorphic-fetch": "0.0.35",
|
||||||
"@types/jest": "^26.0.10",
|
"@types/jest": "^26.0.13",
|
||||||
"cp": "^0.2.0",
|
"cp": "^0.2.0",
|
||||||
"jest": "^25.5.4",
|
"jest": "^25.5.4",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
"tslint": "^6.1.3",
|
"tslint": "^6.1.3",
|
||||||
"tslint-config-prettier": "^1.18.0",
|
"tslint-config-prettier": "^1.18.0",
|
||||||
"typedoc": "^0.17.8",
|
"typedoc": "^0.17.8",
|
||||||
"typedoc-neo-theme": "^1.0.9",
|
"typedoc-neo-theme": "^1.0.10",
|
||||||
"typedoc-plugin-external-module-name": "^4.0.3",
|
"typedoc-plugin-external-module-name": "^4.0.3",
|
||||||
"typescript": "^3.9.7",
|
"typescript": "^3.9.7",
|
||||||
"uglifyjs-webpack-plugin": "^2.2.0",
|
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||||
|
|||||||
@@ -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 />
|
This scripts builds a new version of the adapter and installs it in the `sasjs-tests` project.
|
||||||
You will also see any lint errors in the console.
|
|
||||||
|
|
||||||
### `npm test`
|
## Running tests
|
||||||
|
|
||||||
Launches the test runner in the interactive watch mode.<br />
|
There are three prerequisites to be able to run the tests:
|
||||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
|
||||||
|
|
||||||
### `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 />
|
### 1. Configuring the SASjs adapter
|
||||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
|
||||||
|
|
||||||
The build is minified and the filenames include the hashes.<br />
|
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.
|
||||||
Your app is ready to be deployed!
|
|
||||||
|
|
||||||
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 can’t 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 aren’t 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 you’re 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 don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t 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.
|
||||||
|
|||||||
6
sasjs-tests/package-lock.json
generated
6
sasjs-tests/package-lock.json
generated
@@ -1357,9 +1357,9 @@
|
|||||||
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
|
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
|
||||||
},
|
},
|
||||||
"@sasjs/adapter": {
|
"@sasjs/adapter": {
|
||||||
"version": "1.3.6",
|
"version": "1.3.13",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-1.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-1.3.13.tgz",
|
||||||
"integrity": "sha512-d2B+cTII+vabKCU8mJy90mEz3tCWw2pEp4qIBGsDamJiTS0Rx69dgXGHuRUm8KtjLDHHrzwXATsqviU3dnU0QQ==",
|
"integrity": "sha512-dWcDxgY3FB7Yx1I5dPpeQeyJDu4lezhIFrjn6lbdwRhV15aqOt4l9o9qZP+VbgOXqyi9gN0Y+p+vs2chBDFQqg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"es6-promise": "^4.2.8",
|
"es6-promise": "^4.2.8",
|
||||||
"form-data": "^3.0.0",
|
"form-data": "^3.0.0",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"homepage": ".",
|
"homepage": ".",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/adapter": "^1.3.6",
|
"@sasjs/adapter": "^1.3.13",
|
||||||
"@sasjs/test-framework": "^1.4.0",
|
"@sasjs/test-framework": "^1.4.0",
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
"@testing-library/react": "^9.5.0",
|
"@testing-library/react": "^9.5.0",
|
||||||
@@ -25,7 +25,9 @@
|
|||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
"deploy": "cd .. && npm run package:lib && cd sasjs-tests && npm i ../build/sasjs-adapter-5.0.0.tgz && 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": {
|
"eslintConfig": {
|
||||||
"extends": "react-app"
|
"extends": "react-app"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { isLogInRequired, needsRetry } from "./utils";
|
import { isLogInRequired, needsRetry, isUrl } from './utils'
|
||||||
import { CsrfToken } from "./types/CsrfToken";
|
import { CsrfToken } from './types/CsrfToken'
|
||||||
import { UploadFile } from "./types/UploadFile";
|
import { UploadFile } from './types/UploadFile'
|
||||||
|
|
||||||
const requestRetryLimit = 5;
|
const requestRetryLimit = 5
|
||||||
|
|
||||||
export class FileUploader {
|
export class FileUploader {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -11,90 +11,93 @@ export class FileUploader {
|
|||||||
private jobsPath: string,
|
private jobsPath: string,
|
||||||
private setCsrfTokenWeb: any,
|
private setCsrfTokenWeb: any,
|
||||||
private csrfToken: CsrfToken | null = null
|
private csrfToken: CsrfToken | null = null
|
||||||
) {}
|
) {
|
||||||
private retryCount = 0;
|
if (serverUrl) isUrl(serverUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
private retryCount = 0
|
||||||
|
|
||||||
public uploadFile(sasJob: string, files: UploadFile[], params: any) {
|
public uploadFile(sasJob: string, files: UploadFile[], params: any) {
|
||||||
if (files?.length < 1) throw new Error("Atleast one file must be provided");
|
if (files?.length < 1) throw new Error('Atleast one file must be provided')
|
||||||
|
|
||||||
let paramsString = "";
|
let paramsString = ''
|
||||||
|
|
||||||
for (let param in params) {
|
for (let param in params) {
|
||||||
if (params.hasOwnProperty(param)) {
|
if (params.hasOwnProperty(param)) {
|
||||||
paramsString += `&${param}=${params[param]}`;
|
paramsString += `&${param}=${params[param]}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const program = this.appLoc
|
const program = this.appLoc
|
||||||
? this.appLoc.replace(/\/?$/, "/") + sasJob.replace(/^\//, "")
|
? this.appLoc.replace(/\/?$/, '/') + sasJob.replace(/^\//, '')
|
||||||
: sasJob;
|
: sasJob
|
||||||
const uploadUrl = `${this.serverUrl}${this.jobsPath}/?${
|
const uploadUrl = `${this.serverUrl}${this.jobsPath}/?${
|
||||||
"_program=" + program
|
'_program=' + program
|
||||||
}${paramsString}`;
|
}${paramsString}`
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
"cache-control": "no-cache"
|
'cache-control': 'no-cache'
|
||||||
};
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const formData = new FormData();
|
const formData = new FormData()
|
||||||
|
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
formData.append("file", file.file, file.fileName);
|
formData.append('file', file.file, file.fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.csrfToken) formData.append("_csrf", this.csrfToken.value);
|
if (this.csrfToken) formData.append('_csrf', this.csrfToken.value)
|
||||||
|
|
||||||
fetch(uploadUrl, {
|
fetch(uploadUrl, {
|
||||||
method: "POST",
|
method: 'POST',
|
||||||
body: formData,
|
body: formData,
|
||||||
referrerPolicy: "same-origin",
|
referrerPolicy: 'same-origin',
|
||||||
headers
|
headers
|
||||||
})
|
})
|
||||||
.then(async (response) => {
|
.then(async (response) => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 403) {
|
if (response.status === 403) {
|
||||||
const tokenHeader = response.headers.get("X-CSRF-HEADER");
|
const tokenHeader = response.headers.get('X-CSRF-HEADER')
|
||||||
|
|
||||||
if (tokenHeader) {
|
if (tokenHeader) {
|
||||||
const token = response.headers.get(tokenHeader);
|
const token = response.headers.get(tokenHeader)
|
||||||
this.csrfToken = {
|
this.csrfToken = {
|
||||||
headerName: tokenHeader,
|
headerName: tokenHeader,
|
||||||
value: token || ""
|
value: token || ''
|
||||||
};
|
}
|
||||||
|
|
||||||
this.setCsrfTokenWeb(this.csrfToken);
|
this.setCsrfTokenWeb(this.csrfToken)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.text();
|
return response.text()
|
||||||
})
|
})
|
||||||
.then((responseText) => {
|
.then((responseText) => {
|
||||||
if (isLogInRequired(responseText))
|
if (isLogInRequired(responseText))
|
||||||
reject("You must be logged in to upload a fle");
|
reject('You must be logged in to upload a fle')
|
||||||
|
|
||||||
if (needsRetry(responseText)) {
|
if (needsRetry(responseText)) {
|
||||||
if (this.retryCount < requestRetryLimit) {
|
if (this.retryCount < requestRetryLimit) {
|
||||||
this.retryCount++;
|
this.retryCount++
|
||||||
this.uploadFile(sasJob, files, params).then(
|
this.uploadFile(sasJob, files, params).then(
|
||||||
(res: any) => resolve(res),
|
(res: any) => resolve(res),
|
||||||
(err: any) => reject(err)
|
(err: any) => reject(err)
|
||||||
);
|
)
|
||||||
} else {
|
} else {
|
||||||
this.retryCount = 0;
|
this.retryCount = 0
|
||||||
reject(responseText);
|
reject(responseText)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.retryCount = 0;
|
this.retryCount = 0
|
||||||
|
|
||||||
try {
|
try {
|
||||||
resolve(JSON.parse(responseText));
|
resolve(JSON.parse(responseText))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
reject(e);
|
reject(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
|
import { isUrl } from './utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A client for interfacing with the SAS9 REST API
|
* A client for interfacing with the SAS9 REST API
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export class SAS9ApiClient {
|
export class SAS9ApiClient {
|
||||||
constructor(private serverUrl: string) {}
|
constructor(private serverUrl: string) {
|
||||||
|
if (serverUrl) isUrl(serverUrl)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns on object containing the server URL
|
* returns on object containing the server URL
|
||||||
@@ -11,7 +15,7 @@ export class SAS9ApiClient {
|
|||||||
public getConfig() {
|
public getConfig() {
|
||||||
return {
|
return {
|
||||||
serverUrl: this.serverUrl
|
serverUrl: this.serverUrl
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,7 +23,7 @@ export class SAS9ApiClient {
|
|||||||
* @param serverUrl - the URL of the server.
|
* @param serverUrl - the URL of the server.
|
||||||
*/
|
*/
|
||||||
public setConfig(serverUrl: string) {
|
public setConfig(serverUrl: string) {
|
||||||
if (serverUrl) this.serverUrl = serverUrl;
|
if (serverUrl) this.serverUrl = serverUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,19 +37,19 @@ export class SAS9ApiClient {
|
|||||||
serverName: string,
|
serverName: string,
|
||||||
repositoryName: string
|
repositoryName: string
|
||||||
) {
|
) {
|
||||||
const requestPayload = linesOfCode.join("\n");
|
const requestPayload = linesOfCode.join('\n')
|
||||||
const executeScriptRequest = {
|
const executeScriptRequest = {
|
||||||
method: "PUT",
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/json"
|
Accept: 'application/json'
|
||||||
},
|
},
|
||||||
body: `command=${requestPayload}`
|
body: `command=${requestPayload}`
|
||||||
};
|
}
|
||||||
const executeScriptResponse = await fetch(
|
const executeScriptResponse = await fetch(
|
||||||
`${this.serverUrl}/sas/servers/${serverName}/cmd?repositoryName=${repositoryName}`,
|
`${this.serverUrl}/sas/servers/${serverName}/cmd?repositoryName=${repositoryName}`,
|
||||||
executeScriptRequest
|
executeScriptRequest
|
||||||
).then((res) => res.text());
|
).then((res) => res.text())
|
||||||
|
|
||||||
return executeScriptResponse;
|
return executeScriptResponse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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) => {
|
it('should parse SAS9 source code', async (done) => {
|
||||||
expect(sampleResponse).toBeTruthy();
|
expect(sampleResponse).toBeTruthy()
|
||||||
const parsedSourceCode = (adapter as any).parseSAS9SourceCode(sampleResponse);
|
const parsedSourceCode = (adapter as any).parseSAS9SourceCode(sampleResponse)
|
||||||
expect(parsedSourceCode).toBeTruthy();
|
expect(parsedSourceCode).toBeTruthy()
|
||||||
const sourceCodeLines = parsedSourceCode.split("\r\n");
|
const sourceCodeLines = parsedSourceCode.split('\r\n')
|
||||||
expect(sourceCodeLines.length).toEqual(5);
|
expect(sourceCodeLines.length).toEqual(5)
|
||||||
expect(sourceCodeLines[0].startsWith("6")).toBeTruthy();
|
expect(sourceCodeLines[0].startsWith('6')).toBeTruthy()
|
||||||
expect(sourceCodeLines[1].startsWith("7")).toBeTruthy();
|
expect(sourceCodeLines[1].startsWith('7')).toBeTruthy()
|
||||||
expect(sourceCodeLines[2].startsWith("8")).toBeTruthy();
|
expect(sourceCodeLines[2].startsWith('8')).toBeTruthy()
|
||||||
expect(sourceCodeLines[3].startsWith("9")).toBeTruthy();
|
expect(sourceCodeLines[3].startsWith('9')).toBeTruthy()
|
||||||
expect(sourceCodeLines[4].startsWith("10")).toBeTruthy();
|
expect(sourceCodeLines[4].startsWith('10')).toBeTruthy()
|
||||||
done();
|
done()
|
||||||
});
|
})
|
||||||
|
|
||||||
it("should parse generated code", async (done) => {
|
it('should parse generated code', async (done) => {
|
||||||
expect(sampleResponse).toBeTruthy();
|
expect(sampleResponse).toBeTruthy()
|
||||||
const parsedGeneratedCode = (adapter as any).parseGeneratedCode(
|
const parsedGeneratedCode = (adapter as any).parseGeneratedCode(
|
||||||
sampleResponse
|
sampleResponse
|
||||||
);
|
)
|
||||||
expect(parsedGeneratedCode).toBeTruthy();
|
expect(parsedGeneratedCode).toBeTruthy()
|
||||||
const generatedCodeLines = parsedGeneratedCode.split("\r\n");
|
const generatedCodeLines = parsedGeneratedCode.split('\r\n')
|
||||||
expect(generatedCodeLines.length).toEqual(5);
|
expect(generatedCodeLines.length).toEqual(5)
|
||||||
expect(generatedCodeLines[0].startsWith("MPRINT(MM_WEBIN)")).toBeTruthy();
|
expect(generatedCodeLines[0].startsWith('MPRINT(MM_WEBIN)')).toBeTruthy()
|
||||||
expect(generatedCodeLines[1].startsWith("MPRINT(MM_WEBLEFT)")).toBeTruthy();
|
expect(generatedCodeLines[1].startsWith('MPRINT(MM_WEBLEFT)')).toBeTruthy()
|
||||||
expect(generatedCodeLines[2].startsWith("MPRINT(MM_WEBOUT)")).toBeTruthy();
|
expect(generatedCodeLines[2].startsWith('MPRINT(MM_WEBOUT)')).toBeTruthy()
|
||||||
expect(generatedCodeLines[3].startsWith("MPRINT(MM_WEBRIGHT)")).toBeTruthy();
|
expect(generatedCodeLines[3].startsWith('MPRINT(MM_WEBRIGHT)')).toBeTruthy()
|
||||||
expect(generatedCodeLines[4].startsWith("MPRINT(MM_WEBOUT)")).toBeTruthy();
|
expect(generatedCodeLines[4].startsWith('MPRINT(MM_WEBOUT)')).toBeTruthy()
|
||||||
done();
|
done()
|
||||||
});
|
})
|
||||||
|
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
const sampleResponse = `<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"/>
|
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_WEBOUT): data _null_;
|
||||||
MPRINT(MM_WEBRIGHT): file _temp;
|
MPRINT(MM_WEBRIGHT): file _temp;
|
||||||
MPRINT(MM_WEBOUT): if upcase(symget('_debug'))='LOG' then put '>>weboutBEGIN<<';
|
MPRINT(MM_WEBOUT): if upcase(symget('_debug'))='LOG' then put '>>weboutBEGIN<<';
|
||||||
`;
|
`
|
||||||
/* tslint:enable */
|
/* tslint:enable */
|
||||||
|
|||||||
801
src/SASjs.ts
801
src/SASjs.ts
File diff suppressed because it is too large
Load Diff
@@ -1,111 +1,114 @@
|
|||||||
import { Session, Context, CsrfToken } from "./types";
|
import { Session, Context, CsrfToken } from './types'
|
||||||
import { asyncForEach, makeRequest } from "./utils";
|
import { asyncForEach, makeRequest, isUrl } from './utils'
|
||||||
|
|
||||||
const MAX_SESSION_COUNT = 1;
|
const MAX_SESSION_COUNT = 1
|
||||||
|
|
||||||
export class SessionManager {
|
export class SessionManager {
|
||||||
constructor(
|
constructor(
|
||||||
private serverUrl: string,
|
private serverUrl: string,
|
||||||
private contextName: string,
|
private contextName: string,
|
||||||
private setCsrfToken: (csrfToken: CsrfToken) => void
|
private setCsrfToken: (csrfToken: CsrfToken) => void
|
||||||
) {}
|
) {
|
||||||
private sessions: Session[] = [];
|
if (serverUrl) isUrl(serverUrl)
|
||||||
private currentContext: Context | null = null;
|
}
|
||||||
private csrfToken: CsrfToken | null = null;
|
|
||||||
|
private sessions: Session[] = []
|
||||||
|
private currentContext: Context | null = null
|
||||||
|
private csrfToken: CsrfToken | null = null
|
||||||
|
|
||||||
async getSession(accessToken?: string) {
|
async getSession(accessToken?: string) {
|
||||||
await this.createSessions(accessToken);
|
await this.createSessions(accessToken)
|
||||||
this.createAndWaitForSession(accessToken);
|
this.createAndWaitForSession(accessToken)
|
||||||
const session = this.sessions.pop();
|
const session = this.sessions.pop()
|
||||||
const secondsSinceSessionCreation =
|
const secondsSinceSessionCreation =
|
||||||
(new Date().getTime() - new Date(session!.creationTimeStamp).getTime()) /
|
(new Date().getTime() - new Date(session!.creationTimeStamp).getTime()) /
|
||||||
1000;
|
1000
|
||||||
if (
|
if (
|
||||||
secondsSinceSessionCreation >= session!.attributes.sessionInactiveTimeout
|
secondsSinceSessionCreation >= session!.attributes.sessionInactiveTimeout
|
||||||
) {
|
) {
|
||||||
await this.createSessions(accessToken);
|
await this.createSessions(accessToken)
|
||||||
const freshSession = this.sessions.pop();
|
const freshSession = this.sessions.pop()
|
||||||
return freshSession;
|
return freshSession
|
||||||
}
|
}
|
||||||
return session;
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
async clearSession(id: string, accessToken?: string) {
|
async clearSession(id: string, accessToken?: string) {
|
||||||
const deleteSessionRequest = {
|
const deleteSessionRequest = {
|
||||||
method: "DELETE",
|
method: 'DELETE',
|
||||||
headers: this.getHeaders(accessToken)
|
headers: this.getHeaders(accessToken)
|
||||||
};
|
}
|
||||||
return await this.request<Session>(
|
return await this.request<Session>(
|
||||||
`${this.serverUrl}/compute/sessions/${id}`,
|
`${this.serverUrl}/compute/sessions/${id}`,
|
||||||
deleteSessionRequest
|
deleteSessionRequest
|
||||||
).then(() => {
|
).then(() => {
|
||||||
this.sessions = this.sessions.filter((s) => s.id !== id);
|
this.sessions = this.sessions.filter((s) => s.id !== id)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createSessions(accessToken?: string) {
|
private async createSessions(accessToken?: string) {
|
||||||
if (!this.sessions.length) {
|
if (!this.sessions.length) {
|
||||||
if (!this.currentContext) {
|
if (!this.currentContext) {
|
||||||
await this.setCurrentContext(accessToken);
|
await this.setCurrentContext(accessToken)
|
||||||
}
|
}
|
||||||
await asyncForEach(new Array(MAX_SESSION_COUNT), async () => {
|
await asyncForEach(new Array(MAX_SESSION_COUNT), async () => {
|
||||||
const createdSession = await this.createAndWaitForSession(accessToken);
|
const createdSession = await this.createAndWaitForSession(accessToken)
|
||||||
this.sessions.push(createdSession);
|
this.sessions.push(createdSession)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createAndWaitForSession(accessToken?: string) {
|
private async createAndWaitForSession(accessToken?: string) {
|
||||||
const createSessionRequest = {
|
const createSessionRequest = {
|
||||||
method: "POST",
|
method: 'POST',
|
||||||
headers: this.getHeaders(accessToken)
|
headers: this.getHeaders(accessToken)
|
||||||
};
|
}
|
||||||
const { result: createdSession, etag } = await this.request<Session>(
|
const { result: createdSession, etag } = await this.request<Session>(
|
||||||
`${this.serverUrl}/compute/contexts/${this.currentContext!.id}/sessions`,
|
`${this.serverUrl}/compute/contexts/${this.currentContext!.id}/sessions`,
|
||||||
createSessionRequest
|
createSessionRequest
|
||||||
);
|
)
|
||||||
|
|
||||||
await this.waitForSession(createdSession, etag);
|
await this.waitForSession(createdSession, etag)
|
||||||
this.sessions.push(createdSession);
|
this.sessions.push(createdSession)
|
||||||
return createdSession;
|
return createdSession
|
||||||
}
|
}
|
||||||
|
|
||||||
private async setCurrentContext(accessToken?: string) {
|
private async setCurrentContext(accessToken?: string) {
|
||||||
if (!this.currentContext) {
|
if (!this.currentContext) {
|
||||||
const { result: contexts } = await this.request<{
|
const { result: contexts } = await this.request<{
|
||||||
items: Context[];
|
items: Context[]
|
||||||
}>(`${this.serverUrl}/compute/contexts`, {
|
}>(`${this.serverUrl}/compute/contexts`, {
|
||||||
headers: this.getHeaders(accessToken)
|
headers: this.getHeaders(accessToken)
|
||||||
});
|
})
|
||||||
|
|
||||||
const contextsList =
|
const contextsList =
|
||||||
contexts && contexts.items && contexts.items.length
|
contexts && contexts.items && contexts.items.length
|
||||||
? contexts.items
|
? contexts.items
|
||||||
: [];
|
: []
|
||||||
|
|
||||||
const currentContext = contextsList.find(
|
const currentContext = contextsList.find(
|
||||||
(c: any) => c.name === this.contextName
|
(c: any) => c.name === this.contextName
|
||||||
);
|
)
|
||||||
|
|
||||||
if (!currentContext) {
|
if (!currentContext) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`The context ${this.contextName} was not found on the server ${this.serverUrl}`
|
`The context ${this.contextName} was not found on the server ${this.serverUrl}`
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentContext = currentContext;
|
this.currentContext = currentContext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getHeaders(accessToken?: string) {
|
private getHeaders(accessToken?: string) {
|
||||||
const headers: any = {
|
const headers: any = {
|
||||||
"Content-Type": "application/json"
|
'Content-Type': 'application/json'
|
||||||
};
|
}
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
headers.Authorization = `Bearer ${accessToken}`;
|
headers.Authorization = `Bearer ${accessToken}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return headers;
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
private async waitForSession(
|
private async waitForSession(
|
||||||
@@ -114,57 +117,57 @@ export class SessionManager {
|
|||||||
accessToken?: string,
|
accessToken?: string,
|
||||||
silent = false
|
silent = false
|
||||||
) {
|
) {
|
||||||
let sessionState = session.state;
|
let sessionState = session.state
|
||||||
const headers: any = {
|
const headers: any = {
|
||||||
...this.getHeaders(accessToken),
|
...this.getHeaders(accessToken),
|
||||||
"If-None-Match": etag
|
'If-None-Match': etag
|
||||||
};
|
}
|
||||||
const stateLink = session.links.find((l: any) => l.rel === "state");
|
const stateLink = session.links.find((l: any) => l.rel === 'state')
|
||||||
return new Promise(async (resolve, _) => {
|
return new Promise(async (resolve, _) => {
|
||||||
if (sessionState === "pending") {
|
if (sessionState === 'pending') {
|
||||||
if (stateLink) {
|
if (stateLink) {
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
console.log("Polling session status... \n");
|
console.log('Polling session status... \n')
|
||||||
}
|
}
|
||||||
const { result: state } = await this.request<string>(
|
const { result: state } = await this.request<string>(
|
||||||
`${this.serverUrl}${stateLink.href}?wait=30`,
|
`${this.serverUrl}${stateLink.href}?wait=30`,
|
||||||
{
|
{
|
||||||
headers
|
headers
|
||||||
},
|
},
|
||||||
"text"
|
'text'
|
||||||
);
|
)
|
||||||
|
|
||||||
sessionState = state.trim();
|
sessionState = state.trim()
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
console.log(`Current state: ${sessionState}\n`);
|
console.log(`Current state: ${sessionState}\n`)
|
||||||
}
|
}
|
||||||
resolve(sessionState);
|
resolve(sessionState)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resolve(sessionState);
|
resolve(sessionState)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async request<T>(
|
private async request<T>(
|
||||||
url: string,
|
url: string,
|
||||||
options: RequestInit,
|
options: RequestInit,
|
||||||
contentType: "text" | "json" = "json"
|
contentType: 'text' | 'json' = 'json'
|
||||||
) {
|
) {
|
||||||
if (this.csrfToken) {
|
if (this.csrfToken) {
|
||||||
options.headers = {
|
options.headers = {
|
||||||
...options.headers,
|
...options.headers,
|
||||||
[this.csrfToken.headerName]: this.csrfToken.value
|
[this.csrfToken.headerName]: this.csrfToken.value
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
return await makeRequest<T>(
|
return await makeRequest<T>(
|
||||||
url,
|
url,
|
||||||
options,
|
options,
|
||||||
(token) => {
|
(token) => {
|
||||||
this.csrfToken = token;
|
this.csrfToken = token
|
||||||
this.setCsrfToken(token);
|
this.setCsrfToken(token)
|
||||||
},
|
},
|
||||||
contentType
|
contentType
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/index.ts
10
src/index.ts
@@ -1,5 +1,5 @@
|
|||||||
import SASjs from "./SASjs";
|
import SASjs from './SASjs'
|
||||||
export * from "./types";
|
export * from './types'
|
||||||
export * from "./SASViyaApiClient";
|
export * from './SASViyaApiClient'
|
||||||
export * from "./SAS9ApiClient";
|
export * from './SAS9ApiClient'
|
||||||
export default SASjs;
|
export default SASjs
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export interface Context {
|
export interface Context {
|
||||||
name: string;
|
name: string
|
||||||
id: string;
|
id: string
|
||||||
createdBy: string;
|
createdBy: string
|
||||||
version: number;
|
version: number
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface CsrfToken {
|
export interface CsrfToken {
|
||||||
headerName: string;
|
headerName: string
|
||||||
value: string;
|
value: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Link } from "./Link";
|
import { Link } from './Link'
|
||||||
|
|
||||||
export interface Folder {
|
export interface Folder {
|
||||||
id: string;
|
id: string
|
||||||
uri: string;
|
uri: string
|
||||||
links: Link[];
|
links: Link[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Link } from "./Link";
|
import { Link } from './Link'
|
||||||
import { JobResult } from "./JobResult";
|
import { JobResult } from './JobResult'
|
||||||
|
|
||||||
export interface Job {
|
export interface Job {
|
||||||
id: string;
|
id: string
|
||||||
name: string;
|
name: string
|
||||||
uri: string;
|
uri: string
|
||||||
createdBy: string;
|
createdBy: string
|
||||||
code?: string;
|
code?: string
|
||||||
links: Link[];
|
links: Link[]
|
||||||
results: JobResult;
|
results: JobResult
|
||||||
error?: any;
|
error?: any
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export interface JobDefinition {
|
export interface JobDefinition {
|
||||||
code: string;
|
code: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export interface JobResult {
|
export interface JobResult {
|
||||||
"_webout.json": string;
|
'_webout.json': string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export interface Link {
|
export interface Link {
|
||||||
method: string;
|
method: string
|
||||||
rel: string;
|
rel: string
|
||||||
href: string;
|
href: string
|
||||||
uri: string;
|
uri: string
|
||||||
type: string;
|
type: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ServerType } from "./ServerType";
|
import { ServerType } from './ServerType'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the configuration for the SASjs instance.
|
* Specifies the configuration for the SASjs instance.
|
||||||
@@ -10,22 +10,22 @@ export class SASjsConfig {
|
|||||||
* Can be omitted, eg if serving directly from the SAS Web Server or being
|
* Can be omitted, eg if serving directly from the SAS Web Server or being
|
||||||
* streamed.
|
* streamed.
|
||||||
*/
|
*/
|
||||||
serverUrl: string = "";
|
serverUrl: string = ''
|
||||||
pathSAS9: string = "";
|
pathSAS9: string = ''
|
||||||
pathSASViya: string = "";
|
pathSASViya: string = ''
|
||||||
/**
|
/**
|
||||||
* The appLoc is the parent folder under which the SAS services (STPs or Job
|
* The appLoc is the parent folder under which the SAS services (STPs or Job
|
||||||
* Execution Services) are stored.
|
* Execution Services) are stored.
|
||||||
*/
|
*/
|
||||||
appLoc: string = "";
|
appLoc: string = ''
|
||||||
/**
|
/**
|
||||||
* Can be SAS9 or SASVIYA
|
* Can be SAS9 or SASVIYA
|
||||||
*/
|
*/
|
||||||
serverType: ServerType | null = null;
|
serverType: ServerType | null = null
|
||||||
/**
|
/**
|
||||||
* Set to `true` to enable additional debugging.
|
* Set to `true` to enable additional debugging.
|
||||||
*/
|
*/
|
||||||
debug: boolean = true;
|
debug: boolean = true
|
||||||
contextName: string = "";
|
contextName: string = ''
|
||||||
useComputeApi = false;
|
useComputeApi = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export interface SASjsRequest {
|
export interface SASjsRequest {
|
||||||
serviceLink: string;
|
serviceLink: string
|
||||||
timestamp: Date;
|
timestamp: Date
|
||||||
sourceCode: string;
|
sourceCode: string
|
||||||
generatedCode: string;
|
generatedCode: string
|
||||||
logFile: string;
|
logFile: string
|
||||||
SASWORK: any;
|
SASWORK: any
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
*/
|
*/
|
||||||
export interface SASjsWaitingRequest {
|
export interface SASjsWaitingRequest {
|
||||||
requestPromise: {
|
requestPromise: {
|
||||||
promise: any;
|
promise: any
|
||||||
resolve: any;
|
resolve: any
|
||||||
reject: any;
|
reject: any
|
||||||
};
|
}
|
||||||
SASjob: string;
|
SASjob: string
|
||||||
data: any;
|
data: any
|
||||||
config?: any;
|
config?: any
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export enum ServerType {
|
export enum ServerType {
|
||||||
SASViya = "SASVIYA",
|
SASViya = 'SASVIYA',
|
||||||
SAS9 = "SAS9"
|
SAS9 = 'SAS9'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Link } from "./Link";
|
import { Link } from './Link'
|
||||||
|
|
||||||
export interface Session {
|
export interface Session {
|
||||||
id: string;
|
id: string
|
||||||
state: string;
|
state: string
|
||||||
links: Link[];
|
links: Link[]
|
||||||
attributes: {
|
attributes: {
|
||||||
sessionInactiveTimeout: number;
|
sessionInactiveTimeout: number
|
||||||
};
|
}
|
||||||
creationTimeStamp: string;
|
creationTimeStamp: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export interface UploadFile {
|
export interface UploadFile {
|
||||||
file: File;
|
file: File
|
||||||
fileName: string;
|
fileName: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
export * from "./Context";
|
export * from './Context'
|
||||||
export * from "./CsrfToken";
|
export * from './CsrfToken'
|
||||||
export * from "./Folder";
|
export * from './Folder'
|
||||||
export * from "./Job";
|
export * from './Job'
|
||||||
export * from "./Link";
|
export * from './Link'
|
||||||
export * from "./SASjsConfig";
|
export * from './SASjsConfig'
|
||||||
export * from "./SASjsRequest";
|
export * from './SASjsRequest'
|
||||||
export * from "./SASjsWaitingRequest";
|
export * from './SASjsWaitingRequest'
|
||||||
export * from "./ServerType";
|
export * from './ServerType'
|
||||||
export * from "./Session";
|
export * from './Session'
|
||||||
export * from "./UploadFile";
|
export * from './UploadFile'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export async function asyncForEach(array: any[], callback: any) {
|
export async function asyncForEach(array: any[], callback: any) {
|
||||||
for (let index = 0; index < array.length; index++) {
|
for (let index = 0; index < array.length; index++) {
|
||||||
await callback(array[index], index, array);
|
await callback(array[index], index, array)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { SASjsRequest } from "../types/SASjsRequest";
|
import { SASjsRequest } from '../types/SASjsRequest'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Comparator for SASjs request timestamps
|
* Comparator for SASjs request timestamps
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const compareTimestamps = (a: SASjsRequest, b: SASjsRequest) => {
|
export const compareTimestamps = (a: SASjsRequest, b: SASjsRequest) => {
|
||||||
return b.timestamp.getTime() - a.timestamp.getTime();
|
return b.timestamp.getTime() - a.timestamp.getTime()
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -3,131 +3,131 @@
|
|||||||
* @param data - the JSON object to convert.
|
* @param data - the JSON object to convert.
|
||||||
*/
|
*/
|
||||||
export const convertToCSV = (data: any) => {
|
export const convertToCSV = (data: any) => {
|
||||||
const replacer = (key: any, value: any) => (value === null ? "" : value);
|
const replacer = (key: any, value: any) => (value === null ? '' : value)
|
||||||
const headerFields = Object.keys(data[0]);
|
const headerFields = Object.keys(data[0])
|
||||||
let csvTest;
|
let csvTest
|
||||||
let invalidString = false;
|
let invalidString = false
|
||||||
const headers = headerFields.map((field) => {
|
const headers = headerFields.map((field) => {
|
||||||
let firstFoundType: string | null = null;
|
let firstFoundType: string | null = null
|
||||||
let hasMixedTypes: boolean = false;
|
let hasMixedTypes: boolean = false
|
||||||
let rowNumError: number = -1;
|
let rowNumError: number = -1
|
||||||
|
|
||||||
const longestValueForField = data
|
const longestValueForField = data
|
||||||
.map((row: any, index: number) => {
|
.map((row: any, index: number) => {
|
||||||
if (row[field] || row[field] === "") {
|
if (row[field] || row[field] === '') {
|
||||||
if (firstFoundType) {
|
if (firstFoundType) {
|
||||||
let currentFieldType =
|
let currentFieldType =
|
||||||
row[field] === "" || typeof row[field] === "string"
|
row[field] === '' || typeof row[field] === 'string'
|
||||||
? "chars"
|
? 'chars'
|
||||||
: "number";
|
: 'number'
|
||||||
|
|
||||||
if (!hasMixedTypes) {
|
if (!hasMixedTypes) {
|
||||||
hasMixedTypes = currentFieldType !== firstFoundType;
|
hasMixedTypes = currentFieldType !== firstFoundType
|
||||||
rowNumError = hasMixedTypes ? index + 1 : -1;
|
rowNumError = hasMixedTypes ? index + 1 : -1
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (row[field] === "") {
|
if (row[field] === '') {
|
||||||
firstFoundType = "chars";
|
firstFoundType = 'chars'
|
||||||
} else {
|
} else {
|
||||||
firstFoundType =
|
firstFoundType =
|
||||||
typeof row[field] === "string" ? "chars" : "number";
|
typeof row[field] === 'string' ? 'chars' : 'number'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let byteSize;
|
let byteSize
|
||||||
|
|
||||||
if (typeof row[field] === "string") {
|
if (typeof row[field] === 'string') {
|
||||||
let doubleQuotesFound = row[field]
|
let doubleQuotesFound = row[field]
|
||||||
.split("")
|
.split('')
|
||||||
.filter((char: any) => char === '"');
|
.filter((char: any) => char === '"')
|
||||||
|
|
||||||
byteSize = getByteSize(row[field]);
|
byteSize = getByteSize(row[field])
|
||||||
|
|
||||||
if (doubleQuotesFound.length > 0) {
|
if (doubleQuotesFound.length > 0) {
|
||||||
byteSize += doubleQuotesFound.length;
|
byteSize += doubleQuotesFound.length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return byteSize;
|
return byteSize
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.sort((a: number, b: number) => b - a)[0];
|
.sort((a: number, b: number) => b - a)[0]
|
||||||
if (longestValueForField && longestValueForField > 32765) {
|
if (longestValueForField && longestValueForField > 32765) {
|
||||||
invalidString = true;
|
invalidString = true
|
||||||
}
|
}
|
||||||
if (hasMixedTypes) {
|
if (hasMixedTypes) {
|
||||||
console.error(
|
console.error(
|
||||||
`Row (${rowNumError}), Column (${field}) has mixed types: ERROR`
|
`Row (${rowNumError}), Column (${field}) has mixed types: ERROR`
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${field}:${firstFoundType === "chars" ? "$" : ""}${
|
return `${field}:${firstFoundType === 'chars' ? '$' : ''}${
|
||||||
longestValueForField
|
longestValueForField
|
||||||
? longestValueForField
|
? longestValueForField
|
||||||
: firstFoundType === "chars"
|
: firstFoundType === 'chars'
|
||||||
? "1"
|
? '1'
|
||||||
: "best"
|
: 'best'
|
||||||
}.`;
|
}.`
|
||||||
});
|
})
|
||||||
|
|
||||||
if (invalidString) {
|
if (invalidString) {
|
||||||
return "ERROR: LARGE STRING LENGTH";
|
return 'ERROR: LARGE STRING LENGTH'
|
||||||
}
|
}
|
||||||
csvTest = data.map((row: any) => {
|
csvTest = data.map((row: any) => {
|
||||||
const fields = Object.keys(row).map((fieldName, index) => {
|
const fields = Object.keys(row).map((fieldName, index) => {
|
||||||
let value;
|
let value
|
||||||
let containsSpecialChar = false;
|
let containsSpecialChar = false
|
||||||
const currentCell = row[fieldName];
|
const currentCell = row[fieldName]
|
||||||
|
|
||||||
if (JSON.stringify(currentCell).search(/(\\t|\\n|\\r)/gm) > -1) {
|
if (JSON.stringify(currentCell).search(/(\\t|\\n|\\r)/gm) > -1) {
|
||||||
value = currentCell.toString();
|
value = currentCell.toString()
|
||||||
containsSpecialChar = true;
|
containsSpecialChar = true
|
||||||
} else {
|
} else {
|
||||||
value = JSON.stringify(currentCell, replacer);
|
value = JSON.stringify(currentCell, replacer)
|
||||||
}
|
}
|
||||||
|
|
||||||
value = value.replace(/\\\\/gm, "\\");
|
value = value.replace(/\\\\/gm, '\\')
|
||||||
|
|
||||||
if (containsSpecialChar) {
|
if (containsSpecialChar) {
|
||||||
if (value.includes(",") || value.includes('"')) {
|
if (value.includes(',') || value.includes('"')) {
|
||||||
value = '"' + value + '"';
|
value = '"' + value + '"'
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (
|
if (
|
||||||
!value.includes(",") &&
|
!value.includes(',') &&
|
||||||
value.includes('"') &&
|
value.includes('"') &&
|
||||||
!value.includes('\\"')
|
!value.includes('\\"')
|
||||||
) {
|
) {
|
||||||
value = value.substring(1, value.length - 1);
|
value = value.substring(1, value.length - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
value = value.replace(/\\"/gm, '""');
|
value = value.replace(/\\"/gm, '""')
|
||||||
}
|
}
|
||||||
|
|
||||||
value = value.replace(/\r\n/gm, "\n");
|
value = value.replace(/\r\n/gm, '\n')
|
||||||
|
|
||||||
if (value === "" && headers[index].includes("best")) {
|
if (value === '' && headers[index].includes('best')) {
|
||||||
value = ".";
|
value = '.'
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value
|
||||||
});
|
})
|
||||||
return fields.join(",");
|
return fields.join(',')
|
||||||
});
|
})
|
||||||
|
|
||||||
let finalCSV =
|
let finalCSV =
|
||||||
headers.join(",").replace(/,/g, " ") + "\r\n" + csvTest.join("\r\n");
|
headers.join(',').replace(/,/g, ' ') + '\r\n' + csvTest.join('\r\n')
|
||||||
|
|
||||||
return finalCSV;
|
return finalCSV
|
||||||
};
|
}
|
||||||
|
|
||||||
const getByteSize = (str: string) => {
|
const getByteSize = (str: string) => {
|
||||||
let byteSize = str.length;
|
let byteSize = str.length
|
||||||
for (let i = str.length - 1; i >= 0; i--) {
|
for (let i = str.length - 1; i >= 0; i--) {
|
||||||
const code = str.charCodeAt(i);
|
const code = str.charCodeAt(i)
|
||||||
if (code > 0x7f && code <= 0x7ff) byteSize++;
|
if (code > 0x7f && code <= 0x7ff) byteSize++
|
||||||
else if (code > 0x7ff && code <= 0xffff) byteSize += 2;
|
else if (code > 0x7ff && code <= 0xffff) byteSize += 2
|
||||||
if (code >= 0xdc00 && code <= 0xdfff) i--; //trail surrogate
|
if (code >= 0xdc00 && code <= 0xdfff) i-- //trail surrogate
|
||||||
}
|
}
|
||||||
return byteSize;
|
return byteSize
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
import { convertToCSV } from "./convertToCsv";
|
import { convertToCSV } from './convertToCsv'
|
||||||
import { splitChunks } from "./splitChunks";
|
import { splitChunks } from './splitChunks'
|
||||||
|
|
||||||
export const formatDataForRequest = (data: any) => {
|
export const formatDataForRequest = (data: any) => {
|
||||||
const sasjsTables = [];
|
const sasjsTables = []
|
||||||
let tableCounter = 0;
|
let tableCounter = 0
|
||||||
const result: any = {};
|
const result: any = {}
|
||||||
|
|
||||||
for (const tableName in data) {
|
for (const tableName in data) {
|
||||||
tableCounter++;
|
tableCounter++
|
||||||
sasjsTables.push(tableName);
|
sasjsTables.push(tableName)
|
||||||
const csv = convertToCSV(data[tableName]);
|
const csv = convertToCSV(data[tableName])
|
||||||
if (csv === "ERROR: LARGE STRING LENGTH") {
|
if (csv === 'ERROR: LARGE STRING LENGTH') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"The max length of a string value in SASjs is 32765 characters."
|
'The max length of a string value in SASjs is 32765 characters.'
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
// if csv has length more then 16k, send in chunks
|
// if csv has length more then 16k, send in chunks
|
||||||
if (csv.length > 16000) {
|
if (csv.length > 16000) {
|
||||||
const csvChunks = splitChunks(csv);
|
const csvChunks = splitChunks(csv)
|
||||||
// append chunks to form data with same key
|
// append chunks to form data with same key
|
||||||
result[`sasjs${tableCounter}data0`] = csvChunks.length;
|
result[`sasjs${tableCounter}data0`] = csvChunks.length
|
||||||
csvChunks.forEach((chunk, index) => {
|
csvChunks.forEach((chunk, index) => {
|
||||||
result[`sasjs${tableCounter}data${index + 1}`] = chunk;
|
result[`sasjs${tableCounter}data${index + 1}`] = chunk
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
result[`sasjs${tableCounter}data`] = csv;
|
result[`sasjs${tableCounter}data`] = csv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result["sasjs_tables"] = sasjsTables.join(" ");
|
result['sasjs_tables'] = sasjsTables.join(' ')
|
||||||
|
|
||||||
return result;
|
return result
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
export * from "./asyncForEach";
|
export * from './asyncForEach'
|
||||||
export * from "./compareTimestamps";
|
export * from './compareTimestamps'
|
||||||
export * from "./convertToCsv";
|
export * from './convertToCsv'
|
||||||
export * from "./isAuthorizeFormRequired";
|
export * from './isAuthorizeFormRequired'
|
||||||
export * from "./isLoginRequired";
|
export * from './isLoginRequired'
|
||||||
export * from "./isLoginSuccess";
|
export * from './isLoginSuccess'
|
||||||
export * from "./makeRequest";
|
export * from './makeRequest'
|
||||||
export * from "./needsRetry";
|
export * from './needsRetry'
|
||||||
export * from "./parseAndSubmitAuthorizeForm";
|
export * from './parseAndSubmitAuthorizeForm'
|
||||||
export * from "./parseGeneratedCode";
|
export * from './parseGeneratedCode'
|
||||||
export * from "./parseSourceCode";
|
export * from './parseSourceCode'
|
||||||
export * from "./parseSasViyaLog";
|
export * from './parseSasViyaLog'
|
||||||
export * from "./serialize";
|
export * from './serialize'
|
||||||
export * from "./splitChunks";
|
export * from './splitChunks'
|
||||||
export * from "./parseWeboutResponse";
|
export * from './parseWeboutResponse'
|
||||||
|
export * from './isUri'
|
||||||
|
export * from './isUrl'
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export const isAuthorizeFormRequired = (response: string): boolean => {
|
export const isAuthorizeFormRequired = (response: string): boolean => {
|
||||||
return /<form.+action="(.*Logon\/oauth\/authorize[^"]*).*>/gm.test(response);
|
return /<form.+action="(.*Logon\/oauth\/authorize[^"]*).*>/gm.test(response)
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
export function isIEorEdgeOrOldFirefox() {
|
export function isIEorEdgeOrOldFirefox() {
|
||||||
if (typeof window === "undefined") {
|
if (typeof window === 'undefined') {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
const ua = window.navigator.userAgent;
|
const ua = window.navigator.userAgent
|
||||||
|
|
||||||
if (ua.indexOf("Firefox") > 0) {
|
if (ua.indexOf('Firefox') > 0) {
|
||||||
const version = parseInt(
|
const version = parseInt(
|
||||||
ua.substring(ua.lastIndexOf("Firefox/") + 8, ua.length),
|
ua.substring(ua.lastIndexOf('Firefox/') + 8, ua.length),
|
||||||
10
|
10
|
||||||
);
|
)
|
||||||
return version <= 60;
|
return version <= 60
|
||||||
}
|
}
|
||||||
|
|
||||||
const msie = ua.indexOf("MSIE ");
|
const msie = ua.indexOf('MSIE ')
|
||||||
if (msie > 0) {
|
if (msie > 0) {
|
||||||
// IE 10 or older => return version number
|
// IE 10 or older => return version number
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const trident = ua.indexOf("Trident/");
|
const trident = ua.indexOf('Trident/')
|
||||||
if (trident > 0) {
|
if (trident > 0) {
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const edge = ua.indexOf("Edge/");
|
const edge = ua.indexOf('Edge/')
|
||||||
if (edge > 0) {
|
if (edge > 0) {
|
||||||
// Edge (IE 12+) => return version number
|
// Edge (IE 12+) => return version number
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// other browser
|
// other browser
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export const isLogInRequired = (response: string): boolean => {
|
export const isLogInRequired = (response: string): boolean => {
|
||||||
const pattern: RegExp = /<form.+action="(.*Logon[^"]*).*>/gm;
|
const pattern: RegExp = /<form.+action="(.*Logon[^"]*).*>/gm
|
||||||
const matches = pattern.test(response);
|
const matches = pattern.test(response)
|
||||||
return matches;
|
return matches
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export const isLogInSuccess = (response: string): boolean =>
|
export const isLogInSuccess = (response: string): boolean =>
|
||||||
/You have signed in/gm.test(response);
|
/You have signed in/gm.test(response)
|
||||||
|
|||||||
5
src/utils/isUri.ts
Normal file
5
src/utils/isUri.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/**
|
||||||
|
* Checks if string is in URI format
|
||||||
|
* @param str string to check
|
||||||
|
*/
|
||||||
|
export const isUri = (str: string): boolean => /^\/folders\/folders\//.test(str)
|
||||||
12
src/utils/isUrl.ts
Normal file
12
src/utils/isUrl.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export const isUrl = (url: string): boolean => {
|
||||||
|
const pattern = new RegExp(
|
||||||
|
'^(http://|https://)[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(/.*)?$',
|
||||||
|
'gi'
|
||||||
|
)
|
||||||
|
|
||||||
|
if (pattern.test(url)) return true
|
||||||
|
else
|
||||||
|
throw new Error(
|
||||||
|
`'${url}' is not a valid url. An example of a valid url is 'http://valid-url.com'.`
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,107 +1,107 @@
|
|||||||
import { CsrfToken } from "../types";
|
import { CsrfToken } from '../types'
|
||||||
import { needsRetry } from "./needsRetry";
|
import { needsRetry } from './needsRetry'
|
||||||
|
|
||||||
let retryCount: number = 0;
|
let retryCount: number = 0
|
||||||
let retryLimit: number = 5;
|
let retryLimit: number = 5
|
||||||
|
|
||||||
export async function makeRequest<T>(
|
export async function makeRequest<T>(
|
||||||
url: string,
|
url: string,
|
||||||
request: RequestInit,
|
request: RequestInit,
|
||||||
callback: (value: CsrfToken) => any,
|
callback: (value: CsrfToken) => any,
|
||||||
contentType: "text" | "json" = "json"
|
contentType: 'text' | 'json' = 'json'
|
||||||
): Promise<{ result: T; etag: string | null }> {
|
): Promise<{ result: T; etag: string | null }> {
|
||||||
let retryRequest: any = null;
|
let retryRequest: any = null
|
||||||
|
|
||||||
const responseTransform =
|
const responseTransform =
|
||||||
contentType === "json"
|
contentType === 'json'
|
||||||
? (res: Response) => res.json()
|
? (res: Response) => res.json()
|
||||||
: (res: Response) => res.text();
|
: (res: Response) => res.text()
|
||||||
let etag = null;
|
let etag = null
|
||||||
const result = await fetch(url, request).then(async (response) => {
|
const result = await fetch(url, request).then(async (response) => {
|
||||||
if (response.redirected && response.url.includes("SASLogon/login")) {
|
if (response.redirected && response.url.includes('SASLogon/login')) {
|
||||||
return Promise.reject({ status: 401 });
|
return Promise.reject({ status: 401 })
|
||||||
}
|
}
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 403) {
|
if (response.status === 403) {
|
||||||
const tokenHeader = response.headers.get("X-CSRF-HEADER");
|
const tokenHeader = response.headers.get('X-CSRF-HEADER')
|
||||||
|
|
||||||
if (tokenHeader) {
|
if (tokenHeader) {
|
||||||
const token = response.headers.get(tokenHeader);
|
const token = response.headers.get(tokenHeader)
|
||||||
callback({
|
callback({
|
||||||
headerName: tokenHeader,
|
headerName: tokenHeader,
|
||||||
value: token || ""
|
value: token || ''
|
||||||
});
|
})
|
||||||
|
|
||||||
retryRequest = {
|
retryRequest = {
|
||||||
...request,
|
...request,
|
||||||
headers: { ...request.headers, [tokenHeader]: token }
|
headers: { ...request.headers, [tokenHeader]: token }
|
||||||
};
|
}
|
||||||
return fetch(url, retryRequest).then((res) => {
|
return fetch(url, retryRequest).then((res) => {
|
||||||
etag = res.headers.get("ETag");
|
etag = res.headers.get('ETag')
|
||||||
return responseTransform(res);
|
return responseTransform(res)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const body = await response.text();
|
const body = await response.text()
|
||||||
|
|
||||||
if (needsRetry(body)) {
|
if (needsRetry(body)) {
|
||||||
if (retryCount < retryLimit) {
|
if (retryCount < retryLimit) {
|
||||||
retryCount++;
|
retryCount++
|
||||||
let retryResponse = await makeRequest(
|
let retryResponse = await makeRequest(
|
||||||
url,
|
url,
|
||||||
retryRequest || request,
|
retryRequest || request,
|
||||||
callback,
|
callback,
|
||||||
contentType
|
contentType
|
||||||
);
|
)
|
||||||
retryCount = 0;
|
retryCount = 0
|
||||||
|
|
||||||
etag = retryResponse.etag;
|
etag = retryResponse.etag
|
||||||
return retryResponse.result;
|
return retryResponse.result
|
||||||
} else {
|
} else {
|
||||||
retryCount = 0;
|
retryCount = 0
|
||||||
|
|
||||||
throw new Error("Request retry limit exceeded");
|
throw new Error('Request retry limit exceeded')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject({ status: response.status, body });
|
return Promise.reject({ status: response.status, body })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (response.status === 204) {
|
if (response.status === 204) {
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
const responseTransformed = await responseTransform(response);
|
const responseTransformed = await responseTransform(response)
|
||||||
let responseText = "";
|
let responseText = ''
|
||||||
|
|
||||||
if (typeof responseTransformed === "string") {
|
if (typeof responseTransformed === 'string') {
|
||||||
responseText = responseTransformed;
|
responseText = responseTransformed
|
||||||
} else {
|
} else {
|
||||||
responseText = JSON.stringify(responseTransformed);
|
responseText = JSON.stringify(responseTransformed)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsRetry(responseText)) {
|
if (needsRetry(responseText)) {
|
||||||
if (retryCount < retryLimit) {
|
if (retryCount < retryLimit) {
|
||||||
retryCount++;
|
retryCount++
|
||||||
const retryResponse = await makeRequest(
|
const retryResponse = await makeRequest(
|
||||||
url,
|
url,
|
||||||
retryRequest || request,
|
retryRequest || request,
|
||||||
callback,
|
callback,
|
||||||
contentType
|
contentType
|
||||||
);
|
)
|
||||||
retryCount = 0;
|
retryCount = 0
|
||||||
|
|
||||||
etag = retryResponse.etag;
|
etag = retryResponse.etag
|
||||||
return retryResponse.result;
|
return retryResponse.result
|
||||||
} else {
|
} else {
|
||||||
retryCount = 0;
|
retryCount = 0
|
||||||
|
|
||||||
throw new Error("Request retry limit exceeded");
|
throw new Error('Request retry limit exceeded')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
etag = response.headers.get("ETag");
|
etag = response.headers.get('ETag')
|
||||||
return responseTransformed;
|
return responseTransformed
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
return { result, etag };
|
return { result, etag }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ export const needsRetry = (responseText: string): boolean => {
|
|||||||
return (
|
return (
|
||||||
!!responseText &&
|
!!responseText &&
|
||||||
((responseText.includes('"errorCode":403') &&
|
((responseText.includes('"errorCode":403') &&
|
||||||
responseText.includes("_csrf") &&
|
responseText.includes('_csrf') &&
|
||||||
responseText.includes("X-CSRF-TOKEN")) ||
|
responseText.includes('X-CSRF-TOKEN')) ||
|
||||||
(responseText.includes('"status":403') &&
|
(responseText.includes('"status":403') &&
|
||||||
responseText.includes('"error":"Forbidden"')) ||
|
responseText.includes('"error":"Forbidden"')) ||
|
||||||
(responseText.includes('"status":449') &&
|
(responseText.includes('"status":449') &&
|
||||||
responseText.includes(
|
responseText.includes(
|
||||||
"Authentication success, retry original request"
|
'Authentication success, retry original request'
|
||||||
)))
|
)))
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -2,48 +2,48 @@ export const parseAndSubmitAuthorizeForm = async (
|
|||||||
response: string,
|
response: string,
|
||||||
serverUrl: string
|
serverUrl: string
|
||||||
) => {
|
) => {
|
||||||
let authUrl: string | null = null;
|
let authUrl: string | null = null
|
||||||
const params: any = {};
|
const params: any = {}
|
||||||
|
|
||||||
const responseBody = response.split("<body>")[1].split("</body>")[0];
|
const responseBody = response.split('<body>')[1].split('</body>')[0]
|
||||||
const bodyElement = document.createElement("div");
|
const bodyElement = document.createElement('div')
|
||||||
bodyElement.innerHTML = responseBody;
|
bodyElement.innerHTML = responseBody
|
||||||
|
|
||||||
const form = bodyElement.querySelector("#application_authorization");
|
const form = bodyElement.querySelector('#application_authorization')
|
||||||
authUrl = form ? serverUrl + form.getAttribute("action") : null;
|
authUrl = form ? serverUrl + form.getAttribute('action') : null
|
||||||
|
|
||||||
const inputs: any = form?.querySelectorAll("input");
|
const inputs: any = form?.querySelectorAll('input')
|
||||||
|
|
||||||
for (const input of inputs) {
|
for (const input of inputs) {
|
||||||
if (input.name === "user_oauth_approval") {
|
if (input.name === 'user_oauth_approval') {
|
||||||
input.value = "true";
|
input.value = 'true'
|
||||||
}
|
}
|
||||||
|
|
||||||
params[input.name] = input.value;
|
params[input.name] = input.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData()
|
||||||
|
|
||||||
for (const key in params) {
|
for (const key in params) {
|
||||||
if (params.hasOwnProperty(key)) {
|
if (params.hasOwnProperty(key)) {
|
||||||
formData.append(key, params[key]);
|
formData.append(key, params[key])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (authUrl) {
|
if (authUrl) {
|
||||||
fetch(authUrl, {
|
fetch(authUrl, {
|
||||||
method: "POST",
|
method: 'POST',
|
||||||
credentials: "include",
|
credentials: 'include',
|
||||||
body: formData,
|
body: formData,
|
||||||
referrerPolicy: "same-origin"
|
referrerPolicy: 'same-origin'
|
||||||
})
|
})
|
||||||
.then((res) => res.text())
|
.then((res) => res.text())
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
resolve(res);
|
resolve(res)
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
reject("Auth form url is null");
|
reject('Auth form url is null')
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export const parseGeneratedCode = (log: string) => {
|
export const parseGeneratedCode = (log: string) => {
|
||||||
const startsWith = "MPRINT";
|
const startsWith = 'MPRINT'
|
||||||
const isGeneratedCodeLine = (line: string) =>
|
const isGeneratedCodeLine = (line: string) =>
|
||||||
line.trim().startsWith(startsWith);
|
line.trim().startsWith(startsWith)
|
||||||
const logLines = log.split("\n").filter(isGeneratedCodeLine);
|
const logLines = log.split('\n').filter(isGeneratedCodeLine)
|
||||||
return logLines.join("\r\n");
|
return logLines.join('\r\n')
|
||||||
};
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user