mirror of
https://github.com/sasjs/adapter.git
synced 2025-12-11 01:14:36 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
827c93886a | ||
|
|
f6abb61c69 | ||
|
|
4f3478c215 | ||
|
|
5927910a52 | ||
| 88f1c2f843 | |||
|
|
ff5dc5f196 | ||
|
|
f0f33cee52 | ||
|
|
82e061a09c | ||
| 888a2b9bd3 | |||
|
|
b9defdd1dc | ||
| 6e8b19eda1 | |||
|
|
83350b5dd8 | ||
|
|
2e14d4f28c | ||
|
|
96cb77da45 | ||
|
|
1ee07eeecf |
17
README.md
17
README.md
@@ -20,9 +20,9 @@
|
||||
|
||||
SASjs is a open-source framework for building Web Apps on SAS® platforms. You can use as much or as little of it as you like. This repository contains the JS adapter, the part that handles the to/from SAS communication on the client side. There are 3 ways to install it:
|
||||
|
||||
1 - `npm install @sasjs/adapter` - for use in a node project
|
||||
1 - `npm install @sasjs/adapter` - for use in a nodeJS project (recommended)
|
||||
|
||||
2 - [Download](https://cdn.jsdelivr.net/npm/@sasjs/adapter@2/index.js) and use a copy of the latest JS file
|
||||
2 - [Download](https://cdn.jsdelivr.net/npm/@sasjs/adapter@3/index.min.js) and use a copy of the latest JS file
|
||||
|
||||
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.
|
||||
|
||||
@@ -96,7 +96,12 @@ const sasJs = new SASjs({your config})
|
||||
More on the config later.
|
||||
|
||||
### SAS Logon
|
||||
The login process can be handled directly, as below, or as a callback function to a SAS request.
|
||||
All authentication from the adapter is done against SASLogon. There are two approaches that can be taken, which are configured using the `LoginMechanism` attribute of the sasJs config object (above):
|
||||
|
||||
* `LoginMechanism:'Redirected'` - this approach enables authentication through a SASLogon window, supporting complex authentication flows (such as 2FA) and avoids the need to handle passwords in the application itself. The styling of the window can be modified using CSS.
|
||||
* `LoginMechanism:'Default'` - this approach requires that the username and password are captured, and used within the `.login()` method. This can be helpful for development, or automated testing.
|
||||
|
||||
Sample code for logging in with the `Default` approach:
|
||||
|
||||
```javascript
|
||||
sasJs.logIn('USERNAME','PASSWORD'
|
||||
@@ -109,6 +114,8 @@ sasJs.logIn('USERNAME','PASSWORD'
|
||||
}
|
||||
```
|
||||
|
||||
More examples of using authentication, and more, can be found in the [SASjs Seed Apps](https://github.com/search?q=topic%3Asasjs-app+org%3Asasjs+fork%3Atrue) on github.
|
||||
|
||||
### Request / Response
|
||||
A simple request can be sent to SAS in the following fashion:
|
||||
|
||||
@@ -247,11 +254,11 @@ Where an entire column is made up of special missing numerics, there would be no
|
||||
|
||||
Configuration on the client side involves passing an object on startup, which can also be passed with each request. Technical documentation on the SASjsConfig class is available [here](https://adapter.sasjs.io/classes/types.sasjsconfig.html). The main config items are:
|
||||
|
||||
* `appLoc` - this is the folder under which the SAS services will be created.
|
||||
* `appLoc` - this is the folder (eg in metadata or SAS Drive) under which the SAS services are created.
|
||||
* `serverType` - either `SAS9`, `SASVIYA` or `SASJS`. The `SASJS` server type is for use with [sasjs/server](https://github.com/sasjs/server).
|
||||
* `serverUrl` - the location (including http protocol and port) of the SAS Server. Can be omitted, eg if serving directly from the SAS Web Server, or in streaming mode.
|
||||
* `debug` - if `true` then SAS Logs and extra debug information is returned.
|
||||
* `LoginMechanism` - either `Default` or `Redirected`. If `Redirected` then authentication occurs through the injection of an additional screen, which contains the SASLogon prompt. This allows for more complex authentication flows (such as 2FA) and avoids the need to handle passwords in the application itself. The styling of the redirect flow can also be modified. If left at "Default" then the developer must capture the username and password and use these with the `.login()` method.
|
||||
* `LoginMechanism` - either `Default` or `Redirected`. See [SAS Logon](#sas-logon) section.
|
||||
* `useComputeApi` - Only relevant when the serverType is `SASVIYA`. If `true` the [Compute API](#using-the-compute-api) is used. If `false` the [JES API](#using-the-jes-api) is used. If `null` or `undefined` the [Web](#using-jes-web-app) approach is used.
|
||||
* `contextName` - Compute context on which the requests will be called. If missing or not provided, defaults to `Job Execution Compute context`.
|
||||
* `requestHistoryLimit` - Request history limit. Increasing this limit may affect browser performance, especially with debug (logs) enabled. Default is 10.
|
||||
|
||||
859
package-lock.json
generated
859
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
@@ -43,23 +43,23 @@
|
||||
"@types/axios": "0.14.0",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/form-data": "2.5.0",
|
||||
"@types/jest": "27.0.2",
|
||||
"@types/jest": "27.4.0",
|
||||
"@types/mime": "2.0.3",
|
||||
"@types/pem": "1.9.6",
|
||||
"@types/tough-cookie": "4.0.1",
|
||||
"copyfiles": "2.4.1",
|
||||
"cp": "0.2.0",
|
||||
"dotenv": "10.0.0",
|
||||
"express": "4.17.1",
|
||||
"dotenv": "16.0.0",
|
||||
"express": "4.17.3",
|
||||
"jest": "27.4.7",
|
||||
"jest-extended": "2.0.0",
|
||||
"node-polyfill-webpack-plugin": "1.1.4",
|
||||
"path": "0.12.7",
|
||||
"pem": "1.14.4",
|
||||
"pem": "1.14.6",
|
||||
"process": "0.11.10",
|
||||
"rimraf": "3.0.2",
|
||||
"semantic-release": "18.0.0",
|
||||
"terser-webpack-plugin": "5.3.0",
|
||||
"terser-webpack-plugin": "5.3.1",
|
||||
"ts-jest": "27.1.3",
|
||||
"ts-loader": "9.2.6",
|
||||
"tslint": "6.1.3",
|
||||
@@ -67,13 +67,13 @@
|
||||
"typedoc": "0.22.11",
|
||||
"typedoc-neo-theme": "1.1.1",
|
||||
"typedoc-plugin-external-module-name": "4.0.6",
|
||||
"typescript": "4.5.4",
|
||||
"webpack": "5.66.0",
|
||||
"webpack-cli": "4.7.2"
|
||||
"typescript": "4.5.5",
|
||||
"webpack": "5.69.0",
|
||||
"webpack-cli": "4.9.2"
|
||||
},
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@sasjs/utils": "2.35.0",
|
||||
"@sasjs/utils": "2.36.1",
|
||||
"axios": "0.26.0",
|
||||
"axios-cookiejar-support": "1.0.1",
|
||||
"form-data": "4.0.0",
|
||||
|
||||
@@ -70,7 +70,7 @@ parmcards4;
|
||||
%webout(FETCH)
|
||||
%webout(OPEN)
|
||||
%macro x();
|
||||
%do i=1 %to &_webin_file_count; %webout(OBJ,&&_webin_name&i,missing=STRING) %end;
|
||||
%do i=1 %to &_webin_file_count; %webout(OBJ,&&_webin_name&i,missing=STRING,showmeta=YES) %end;
|
||||
%mend; %x()
|
||||
%webout(CLOSE)
|
||||
;;;;
|
||||
@@ -79,7 +79,7 @@ parmcards4;
|
||||
%webout(FETCH)
|
||||
%webout(OPEN)
|
||||
%macro x();
|
||||
%do i=1 %to &_webin_file_count; %webout(ARR,&&_webin_name&i,missing=STRING) %end;
|
||||
%do i=1 %to &_webin_file_count; %webout(ARR,&&_webin_name&i,missing=STRING,showmeta=YES) %end;
|
||||
%mend; %x()
|
||||
%webout(CLOSE)
|
||||
;;;;
|
||||
@@ -111,7 +111,7 @@ parmcards4;
|
||||
%macro x();
|
||||
%do i=1 %to %sysfunc(countw(&sasjs_tables));
|
||||
%let table=%scan(&sasjs_tables,&i);
|
||||
%webout(OBJ,&table,missing=STRING)
|
||||
%webout(OBJ,&table,missing=STRING,showmeta=YES)
|
||||
%end;
|
||||
%mend;
|
||||
%x()
|
||||
@@ -125,7 +125,7 @@ parmcards4;
|
||||
%macro x();
|
||||
%do i=1 %to %sysfunc(countw(&sasjs_tables));
|
||||
%let table=%scan(&sasjs_tables,&i);
|
||||
%webout(ARR,&table,missing=STRING)
|
||||
%webout(ARR,&table,missing=STRING,showmeta=YES)
|
||||
%end;
|
||||
%mend;
|
||||
%x()
|
||||
|
||||
219
sasjs-tests/package-lock.json
generated
219
sasjs-tests/package-lock.json
generated
@@ -2388,16 +2388,16 @@
|
||||
"node_modules/@sasjs/adapter": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "file:../build/sasjs-adapter-5.0.0.tgz",
|
||||
"integrity": "sha512-O9BBQCqMR7l1fsGPD+onh1ET93ZCuIr8sJMA7Y8sOm8JXigUooDtdk/Lo6emBT7Ibwu1kR3gW88oeL+jN3kH/w==",
|
||||
"integrity": "sha512-lbDWueAEnfNlu4OGrc9hBEzT0aoLfAy7eLd2nLHArrF6zukcSGBNhUgOqxIhlz4WeBdf1gt3nk1G7p5X1mrWYQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@sasjs/utils": "^2.32.0",
|
||||
"axios": "^0.21.4",
|
||||
"axios-cookiejar-support": "^1.0.1",
|
||||
"form-data": "^4.0.0",
|
||||
"https": "^1.0.0",
|
||||
"tough-cookie": "^4.0.0"
|
||||
"@sasjs/utils": "2.36.1",
|
||||
"axios": "0.26.0",
|
||||
"axios-cookiejar-support": "1.0.1",
|
||||
"form-data": "4.0.0",
|
||||
"https": "1.0.0",
|
||||
"tough-cookie": "4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/test-framework": {
|
||||
@@ -2422,24 +2422,50 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/utils": {
|
||||
"version": "2.34.1",
|
||||
"integrity": "sha512-hd1qieH3d7+xH96n5DpRGTEazeAhYyBBKCdnKhOXMgF2TZVoHFdRs5REfT88CKza6DHBGRVGnIVm5ORGP4cVLg==",
|
||||
"version": "2.36.1",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.36.1.tgz",
|
||||
"integrity": "sha512-JkGUpLOODsvkeU+S25jb9k2WnvzyD2w6cEk7YyQ/byuqKL8xawH91PPWegrVcJlDY8WmqKE4CPcA3d1mM3B3LA==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@types/fs-extra": "^9.0.11",
|
||||
"@types/prompts": "^2.0.13",
|
||||
"chalk": "^4.1.1",
|
||||
"cli-table": "^0.3.6",
|
||||
"consola": "^2.15.0",
|
||||
"csv-stringify": "^5.6.5",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/prompts": "2.0.13",
|
||||
"chalk": "4.1.1",
|
||||
"cli-table": "0.3.6",
|
||||
"consola": "2.15.0",
|
||||
"csv-stringify": "5.6.5",
|
||||
"find": "0.3.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"lodash.groupby": "4.6.0",
|
||||
"lodash.uniqby": "4.7.0",
|
||||
"prompts": "^2.4.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"valid-url": "^1.0.9"
|
||||
"fs-extra": "10.0.0",
|
||||
"jwt-decode": "3.1.2",
|
||||
"prompts": "2.4.1",
|
||||
"rimraf": "3.0.2",
|
||||
"valid-url": "1.0.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/utils/node_modules/chalk": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
|
||||
"integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/utils/node_modules/prompts": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz",
|
||||
"integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==",
|
||||
"dependencies": {
|
||||
"kleur": "^3.0.3",
|
||||
"sisteransi": "^1.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/@semantic-ui-react/event-stack": {
|
||||
@@ -2723,6 +2749,7 @@
|
||||
},
|
||||
"node_modules/@types/fs-extra": {
|
||||
"version": "9.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
|
||||
"integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
@@ -2811,8 +2838,9 @@
|
||||
"integrity": "sha512-ekoj4qOQYp7CvjX8ZDBgN86w3MqQhLE1hczEJbEIjgFEumDy+na/4AJAbLXfgEWFNB2pKadM5rPFtuSGMWK7xA=="
|
||||
},
|
||||
"node_modules/@types/prompts": {
|
||||
"version": "2.0.14",
|
||||
"integrity": "sha512-HZBd99fKxRWpYCErtm2/yxUZv6/PBI9J7N4TNFffl5JbrYMHBwF25DjQGTW3b3jmXq+9P6/8fCIb2ee57BFfYA==",
|
||||
"version": "2.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.0.13.tgz",
|
||||
"integrity": "sha512-jwMOIGy49VruR/gYehhJYgpVzB+EVpEE7t7j9m1oTo4HMpOe7KmsyqdBuoxAzA5B4caUgx0cKrWr7wUEqMXJ7Q==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@@ -3751,10 +3779,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.21.4",
|
||||
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
||||
"version": "0.26.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz",
|
||||
"integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.0"
|
||||
"follow-redirects": "^1.14.8"
|
||||
}
|
||||
},
|
||||
"node_modules/axios-cookiejar-support": {
|
||||
@@ -4834,8 +4863,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cli-table": {
|
||||
"version": "0.3.11",
|
||||
"integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==",
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz",
|
||||
"integrity": "sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ==",
|
||||
"dependencies": {
|
||||
"colors": "1.0.3"
|
||||
},
|
||||
@@ -5002,6 +5032,7 @@
|
||||
},
|
||||
"node_modules/colors": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
|
||||
"integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=",
|
||||
"engines": {
|
||||
"node": ">=0.1.90"
|
||||
@@ -5112,8 +5143,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/consola": {
|
||||
"version": "2.15.3",
|
||||
"integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw=="
|
||||
"version": "2.15.0",
|
||||
"resolved": "https://registry.npmjs.org/consola/-/consola-2.15.0.tgz",
|
||||
"integrity": "sha512-vlcSGgdYS26mPf7qNi+dCisbhiyDnrN1zaRbw3CSuc2wGOMEGGPsp46PdRG5gqXwgtJfjxDkxRNAgRPr1B77vQ=="
|
||||
},
|
||||
"node_modules/console-browserify": {
|
||||
"version": "1.2.0",
|
||||
@@ -5705,6 +5737,7 @@
|
||||
},
|
||||
"node_modules/csv-stringify": {
|
||||
"version": "5.6.5",
|
||||
"resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.5.tgz",
|
||||
"integrity": "sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A=="
|
||||
},
|
||||
"node_modules/cyclist": {
|
||||
@@ -7786,6 +7819,7 @@
|
||||
},
|
||||
"node_modules/find": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/find/-/find-0.3.0.tgz",
|
||||
"integrity": "sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw==",
|
||||
"dependencies": {
|
||||
"traverse-chain": "~0.1.0"
|
||||
@@ -7843,8 +7877,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.14.6",
|
||||
"integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==",
|
||||
"version": "1.14.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
||||
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@@ -8114,6 +8149,7 @@
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz",
|
||||
"integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
@@ -10633,6 +10669,7 @@
|
||||
},
|
||||
"node_modules/jwt-decode": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
|
||||
"integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
|
||||
},
|
||||
"node_modules/keyboard-key": {
|
||||
@@ -10750,10 +10787,6 @@
|
||||
"version": "4.0.8",
|
||||
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
|
||||
},
|
||||
"node_modules/lodash.groupby": {
|
||||
"version": "4.6.0",
|
||||
"integrity": "sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E="
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4="
|
||||
@@ -10785,10 +10818,6 @@
|
||||
"version": "4.5.0",
|
||||
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
|
||||
},
|
||||
"node_modules/lodash.uniqby": {
|
||||
"version": "4.7.0",
|
||||
"integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI="
|
||||
},
|
||||
"node_modules/loglevel": {
|
||||
"version": "1.8.0",
|
||||
"integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==",
|
||||
@@ -17128,6 +17157,7 @@
|
||||
},
|
||||
"node_modules/traverse-chain": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz",
|
||||
"integrity": "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE="
|
||||
},
|
||||
"node_modules/trim-newlines": {
|
||||
@@ -17619,6 +17649,7 @@
|
||||
},
|
||||
"node_modules/valid-url": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz",
|
||||
"integrity": "sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA="
|
||||
},
|
||||
"node_modules/validate-npm-package-license": {
|
||||
@@ -20967,14 +20998,14 @@
|
||||
},
|
||||
"@sasjs/adapter": {
|
||||
"version": "file:../build/sasjs-adapter-5.0.0.tgz",
|
||||
"integrity": "sha512-O9BBQCqMR7l1fsGPD+onh1ET93ZCuIr8sJMA7Y8sOm8JXigUooDtdk/Lo6emBT7Ibwu1kR3gW88oeL+jN3kH/w==",
|
||||
"integrity": "sha512-lbDWueAEnfNlu4OGrc9hBEzT0aoLfAy7eLd2nLHArrF6zukcSGBNhUgOqxIhlz4WeBdf1gt3nk1G7p5X1mrWYQ==",
|
||||
"requires": {
|
||||
"@sasjs/utils": "^2.32.0",
|
||||
"axios": "^0.21.4",
|
||||
"axios-cookiejar-support": "^1.0.1",
|
||||
"form-data": "^4.0.0",
|
||||
"https": "^1.0.0",
|
||||
"tough-cookie": "^4.0.0"
|
||||
"@sasjs/utils": "2.36.1",
|
||||
"axios": "0.26.0",
|
||||
"axios-cookiejar-support": "1.0.1",
|
||||
"form-data": "4.0.0",
|
||||
"https": "1.0.0",
|
||||
"tough-cookie": "4.0.0"
|
||||
}
|
||||
},
|
||||
"@sasjs/test-framework": {
|
||||
@@ -20991,23 +21022,42 @@
|
||||
}
|
||||
},
|
||||
"@sasjs/utils": {
|
||||
"version": "2.34.1",
|
||||
"integrity": "sha512-hd1qieH3d7+xH96n5DpRGTEazeAhYyBBKCdnKhOXMgF2TZVoHFdRs5REfT88CKza6DHBGRVGnIVm5ORGP4cVLg==",
|
||||
"version": "2.36.1",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.36.1.tgz",
|
||||
"integrity": "sha512-JkGUpLOODsvkeU+S25jb9k2WnvzyD2w6cEk7YyQ/byuqKL8xawH91PPWegrVcJlDY8WmqKE4CPcA3d1mM3B3LA==",
|
||||
"requires": {
|
||||
"@types/fs-extra": "^9.0.11",
|
||||
"@types/prompts": "^2.0.13",
|
||||
"chalk": "^4.1.1",
|
||||
"cli-table": "^0.3.6",
|
||||
"consola": "^2.15.0",
|
||||
"csv-stringify": "^5.6.5",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/prompts": "2.0.13",
|
||||
"chalk": "4.1.1",
|
||||
"cli-table": "0.3.6",
|
||||
"consola": "2.15.0",
|
||||
"csv-stringify": "5.6.5",
|
||||
"find": "0.3.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"lodash.groupby": "4.6.0",
|
||||
"lodash.uniqby": "4.7.0",
|
||||
"prompts": "^2.4.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"valid-url": "^1.0.9"
|
||||
"fs-extra": "10.0.0",
|
||||
"jwt-decode": "3.1.2",
|
||||
"prompts": "2.4.1",
|
||||
"rimraf": "3.0.2",
|
||||
"valid-url": "1.0.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
|
||||
"integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"prompts": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz",
|
||||
"integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==",
|
||||
"requires": {
|
||||
"kleur": "^3.0.3",
|
||||
"sisteransi": "^1.0.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@semantic-ui-react/event-stack": {
|
||||
@@ -21186,6 +21236,7 @@
|
||||
},
|
||||
"@types/fs-extra": {
|
||||
"version": "9.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
|
||||
"integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
@@ -21274,8 +21325,9 @@
|
||||
"integrity": "sha512-ekoj4qOQYp7CvjX8ZDBgN86w3MqQhLE1hczEJbEIjgFEumDy+na/4AJAbLXfgEWFNB2pKadM5rPFtuSGMWK7xA=="
|
||||
},
|
||||
"@types/prompts": {
|
||||
"version": "2.0.14",
|
||||
"integrity": "sha512-HZBd99fKxRWpYCErtm2/yxUZv6/PBI9J7N4TNFffl5JbrYMHBwF25DjQGTW3b3jmXq+9P6/8fCIb2ee57BFfYA==",
|
||||
"version": "2.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.0.13.tgz",
|
||||
"integrity": "sha512-jwMOIGy49VruR/gYehhJYgpVzB+EVpEE7t7j9m1oTo4HMpOe7KmsyqdBuoxAzA5B4caUgx0cKrWr7wUEqMXJ7Q==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@@ -21992,10 +22044,11 @@
|
||||
"integrity": "sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.21.4",
|
||||
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
||||
"version": "0.26.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz",
|
||||
"integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.14.0"
|
||||
"follow-redirects": "^1.14.8"
|
||||
}
|
||||
},
|
||||
"axios-cookiejar-support": {
|
||||
@@ -22832,8 +22885,9 @@
|
||||
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="
|
||||
},
|
||||
"cli-table": {
|
||||
"version": "0.3.11",
|
||||
"integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==",
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz",
|
||||
"integrity": "sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ==",
|
||||
"requires": {
|
||||
"colors": "1.0.3"
|
||||
}
|
||||
@@ -22967,6 +23021,7 @@
|
||||
},
|
||||
"colors": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
|
||||
"integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs="
|
||||
},
|
||||
"combined-stream": {
|
||||
@@ -23055,8 +23110,9 @@
|
||||
"integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg=="
|
||||
},
|
||||
"consola": {
|
||||
"version": "2.15.3",
|
||||
"integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw=="
|
||||
"version": "2.15.0",
|
||||
"resolved": "https://registry.npmjs.org/consola/-/consola-2.15.0.tgz",
|
||||
"integrity": "sha512-vlcSGgdYS26mPf7qNi+dCisbhiyDnrN1zaRbw3CSuc2wGOMEGGPsp46PdRG5gqXwgtJfjxDkxRNAgRPr1B77vQ=="
|
||||
},
|
||||
"console-browserify": {
|
||||
"version": "1.2.0",
|
||||
@@ -23501,6 +23557,7 @@
|
||||
},
|
||||
"csv-stringify": {
|
||||
"version": "5.6.5",
|
||||
"resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.5.tgz",
|
||||
"integrity": "sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A=="
|
||||
},
|
||||
"cyclist": {
|
||||
@@ -25029,6 +25086,7 @@
|
||||
},
|
||||
"find": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/find/-/find-0.3.0.tgz",
|
||||
"integrity": "sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw==",
|
||||
"requires": {
|
||||
"traverse-chain": "~0.1.0"
|
||||
@@ -25076,8 +25134,9 @@
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.6",
|
||||
"integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A=="
|
||||
"version": "1.14.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
||||
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w=="
|
||||
},
|
||||
"for-in": {
|
||||
"version": "1.0.2",
|
||||
@@ -25274,6 +25333,7 @@
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz",
|
||||
"integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
@@ -27139,6 +27199,7 @@
|
||||
},
|
||||
"jwt-decode": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
|
||||
"integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
|
||||
},
|
||||
"keyboard-key": {
|
||||
@@ -27232,10 +27293,6 @@
|
||||
"version": "4.0.8",
|
||||
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
|
||||
},
|
||||
"lodash.groupby": {
|
||||
"version": "4.6.0",
|
||||
"integrity": "sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E="
|
||||
},
|
||||
"lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4="
|
||||
@@ -27267,10 +27324,6 @@
|
||||
"version": "4.5.0",
|
||||
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
|
||||
},
|
||||
"lodash.uniqby": {
|
||||
"version": "4.7.0",
|
||||
"integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI="
|
||||
},
|
||||
"loglevel": {
|
||||
"version": "1.8.0",
|
||||
"integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA=="
|
||||
@@ -32152,6 +32205,7 @@
|
||||
},
|
||||
"traverse-chain": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz",
|
||||
"integrity": "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE="
|
||||
},
|
||||
"trim-newlines": {
|
||||
@@ -32524,6 +32578,7 @@
|
||||
},
|
||||
"valid-url": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz",
|
||||
"integrity": "sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA="
|
||||
},
|
||||
"validate-npm-package-license": {
|
||||
|
||||
@@ -80,16 +80,24 @@ const errorAndCsrfData: any = {
|
||||
}
|
||||
|
||||
const testTable = 'sometable'
|
||||
export const testTableWithNullVars: { [key: string]: any } = {
|
||||
export const testTableWithSpecialNumeric: { [key: string]: any } = {
|
||||
[testTable]: [
|
||||
{ var1: 'string', var2: 232, nullvar: 'A' },
|
||||
{ var1: 'string', var2: 232, nullvar: 'B' },
|
||||
{ var1: 'string', var2: 232, nullvar: '_' },
|
||||
{ var1: 'string', var2: 232, nullvar: 0 },
|
||||
{ var1: 'string', var2: 232, nullvar: 'z' },
|
||||
{ var1: 'string', var2: 232, nullvar: null }
|
||||
{ var1: 'string', var2: 232, specialnumeric: 'A' },
|
||||
{ var1: 'string', var2: 232, specialnumeric: 'B' },
|
||||
{ var1: 'string', var2: 232, specialnumeric: '_' },
|
||||
{ var1: 'string', var2: 232, specialnumeric: 0 },
|
||||
{ var1: 'string', var2: 232, specialnumeric: 'Z' },
|
||||
{ var1: 'string', var2: 232, specialnumeric: null }
|
||||
],
|
||||
[`$${testTable}`]: { formats: { var1: '$char12.', nullvar: 'best.' } }
|
||||
[`$${testTable}`]: { formats: { var1: '$char12.', specialnumeric: 'best.' } }
|
||||
}
|
||||
export const testTableWithSpecialNumericOneRow: { [key: string]: any } = {
|
||||
[testTable]: [{ var1: 'string', var2: 232, specialnumeric: 'S' }],
|
||||
[`$${testTable}`]: { formats: { var1: '$char12.', specialnumeric: 'best.' } }
|
||||
}
|
||||
export const testTableWithSpecialNumericLowercase: { [key: string]: any } = {
|
||||
[testTable]: [{ var1: 'string', var2: 232, specialnumeric: 's' }],
|
||||
[`$${testTable}`]: { formats: { var1: '$char12.', specialnumeric: 'best.' } }
|
||||
}
|
||||
|
||||
export const specialCaseTests = (adapter: SASjs): TestSuite => ({
|
||||
@@ -265,27 +273,191 @@ export const specialCaseTests = (adapter: SASjs): TestSuite => ({
|
||||
title: 'Special missing values',
|
||||
description: 'Should support special missing values',
|
||||
test: () => {
|
||||
return adapter.request('common/sendObj', testTableWithNullVars)
|
||||
return adapter.request('common/sendObj', testTableWithSpecialNumeric)
|
||||
},
|
||||
assertion: (res: any) => {
|
||||
let assertionRes = true
|
||||
|
||||
testTableWithNullVars[testTable].forEach(
|
||||
// We receive formats in response. We compare it with formats that we included in request to make sure they are equal
|
||||
const resVars = res[`$${testTable}`].vars
|
||||
|
||||
Object.keys(resVars).forEach((key: any, i: number) => {
|
||||
let formatValue =
|
||||
testTableWithSpecialNumeric[`$${testTable}`].formats[
|
||||
key.toLowerCase()
|
||||
]
|
||||
// If it is char, we change it to $ to be compatible for comparsion
|
||||
// If it is number, it will already be compatible to comapre (best.)
|
||||
formatValue = formatValue?.includes('$') ? '$' : formatValue
|
||||
|
||||
if (
|
||||
formatValue !== undefined &&
|
||||
!resVars[key].format.includes(formatValue)
|
||||
) {
|
||||
assertionRes = false
|
||||
}
|
||||
})
|
||||
|
||||
// Here we will compare the response values with values we send
|
||||
const resValues = res[testTable]
|
||||
|
||||
testTableWithSpecialNumeric[testTable].forEach(
|
||||
(row: { [key: string]: any }, i: number) =>
|
||||
Object.keys(row).forEach((col: string) => {
|
||||
const resValue = res[testTable][i][col.toUpperCase()]
|
||||
|
||||
if (
|
||||
typeof row[col] === 'string' &&
|
||||
testTableWithNullVars[`$${testTable}`].formats[col] ===
|
||||
'best.' &&
|
||||
row[col].toUpperCase() !== resValue
|
||||
) {
|
||||
if (resValues[i][col.toUpperCase()] !== row[col]) {
|
||||
assertionRes = false
|
||||
} else if (
|
||||
typeof row[col] !== 'string' &&
|
||||
row[col] !== resValue
|
||||
) {
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
return assertionRes
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Special missing values (ONE ROW)',
|
||||
description:
|
||||
'Should support special missing values, when one row is send',
|
||||
test: () => {
|
||||
return adapter.request(
|
||||
'common/sendObj',
|
||||
testTableWithSpecialNumericOneRow
|
||||
)
|
||||
},
|
||||
assertion: (res: any) => {
|
||||
let assertionRes = true
|
||||
|
||||
// We receive formats in response. We compare it with formats that we included in request to make sure they are equal
|
||||
const resVars = res[`$${testTable}`].vars
|
||||
|
||||
Object.keys(resVars).forEach((key: any, i: number) => {
|
||||
let formatValue =
|
||||
testTableWithSpecialNumeric[`$${testTable}`].formats[
|
||||
key.toLowerCase()
|
||||
]
|
||||
// If it is char, we change it to $ to be compatible for comparsion
|
||||
// If it is number, it will already be compatible to comapre (best.)
|
||||
formatValue = formatValue?.includes('$') ? '$' : formatValue
|
||||
|
||||
if (
|
||||
formatValue !== undefined &&
|
||||
!resVars[key].format.includes(formatValue)
|
||||
) {
|
||||
assertionRes = false
|
||||
}
|
||||
})
|
||||
|
||||
// Here we will compare the response values with values we send
|
||||
const resValues = res[testTable]
|
||||
|
||||
testTableWithSpecialNumericOneRow[testTable].forEach(
|
||||
(row: { [key: string]: any }, i: number) =>
|
||||
Object.keys(row).forEach((col: string) => {
|
||||
if (resValues[i][col.toUpperCase()] !== row[col]) {
|
||||
assertionRes = false
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
return assertionRes
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Special missing values (LOWERCASE)',
|
||||
description:
|
||||
'Should support special missing values, when LOWERCASE value is sent',
|
||||
test: () => {
|
||||
return adapter.request(
|
||||
'common/sendObj',
|
||||
testTableWithSpecialNumericLowercase
|
||||
)
|
||||
},
|
||||
assertion: (res: any) => {
|
||||
let assertionRes = true
|
||||
|
||||
// We receive formats in response. We compare it with formats that we included in request to make sure they are equal
|
||||
const resVars = res[`$${testTable}`].vars
|
||||
|
||||
Object.keys(resVars).forEach((key: any, i: number) => {
|
||||
let formatValue =
|
||||
testTableWithSpecialNumericLowercase[`$${testTable}`].formats[
|
||||
key.toLowerCase()
|
||||
]
|
||||
// If it is a char, we change it to $ to be compatible for comparison
|
||||
// If it is a number, it will already be compatible to compare (best.)
|
||||
formatValue = formatValue?.includes('$') ? '$' : formatValue
|
||||
|
||||
if (
|
||||
formatValue !== undefined &&
|
||||
!resVars[key].format.includes(formatValue)
|
||||
) {
|
||||
assertionRes = false
|
||||
}
|
||||
})
|
||||
|
||||
// Here we will compare the response values with values we send
|
||||
const resValues = res[testTable]
|
||||
|
||||
testTableWithSpecialNumericLowercase[testTable].forEach(
|
||||
(row: { [key: string]: any }, i: number) =>
|
||||
Object.keys(row).forEach((col: string) => {
|
||||
if (col === 'specialnumeric') {
|
||||
if (
|
||||
resValues[i][col.toUpperCase()] !== row[col].toUpperCase()
|
||||
) {
|
||||
assertionRes = false
|
||||
}
|
||||
} else {
|
||||
if (resValues[i][col.toUpperCase()] !== row[col]) {
|
||||
assertionRes = false
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
return assertionRes
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Special missing values (ONE ROW) useComputeApi undefined',
|
||||
description:
|
||||
'Should support special missing values, when one row is send (On VIYA Web Approach)',
|
||||
test: () => {
|
||||
return adapter.request(
|
||||
'common/sendObj',
|
||||
testTableWithSpecialNumericOneRow,
|
||||
{ useComputeApi: undefined }
|
||||
)
|
||||
},
|
||||
assertion: (res: any) => {
|
||||
let assertionRes = true
|
||||
|
||||
// We receive formats in response. We compare it with formats that we included in request to make sure they are equal
|
||||
const resVars = res[`$${testTable}`].vars
|
||||
|
||||
Object.keys(resVars).forEach((key: any, i: number) => {
|
||||
let formatValue =
|
||||
testTableWithSpecialNumeric[`$${testTable}`].formats[
|
||||
key.toLowerCase()
|
||||
]
|
||||
// If it is char, we change it to $ to be compatible for comparsion
|
||||
// If it is number, it will already be compatible to comapre (best.)
|
||||
formatValue = formatValue?.includes('$') ? '$' : formatValue
|
||||
|
||||
if (
|
||||
formatValue !== undefined &&
|
||||
!resVars[key].format.includes(formatValue)
|
||||
) {
|
||||
assertionRes = false
|
||||
}
|
||||
})
|
||||
|
||||
// Here we will compare the response values with values we send
|
||||
const resValues = res[testTable]
|
||||
|
||||
testTableWithSpecialNumericOneRow[testTable].forEach(
|
||||
(row: { [key: string]: any }, i: number) =>
|
||||
Object.keys(row).forEach((col: string) => {
|
||||
if (resValues[i][col.toUpperCase()] !== row[col]) {
|
||||
assertionRes = false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,7 +18,7 @@ export async function uploadTables(
|
||||
const uploadedFiles = []
|
||||
|
||||
for (const tableName in data) {
|
||||
const csv = convertToCSV(data[tableName])
|
||||
const csv = convertToCSV(data, tableName)
|
||||
if (csv === 'ERROR: LARGE STRING LENGTH') {
|
||||
throw new Error(
|
||||
'The max length of a string value in SASjs is 32765 characters.'
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { SasAuthResponse } from '@sasjs/utils/types'
|
||||
import { prefixMessage } from '@sasjs/utils/error'
|
||||
import * as NodeFormData from 'form-data'
|
||||
import { RequestClient } from '../request/RequestClient'
|
||||
|
||||
/**
|
||||
@@ -24,26 +23,17 @@ export async function getAccessTokenForViya(
|
||||
token = Buffer.from(clientId + ':' + clientSecret).toString('base64')
|
||||
}
|
||||
const headers = {
|
||||
Authorization: 'Basic ' + token
|
||||
Authorization: 'Basic ' + token,
|
||||
Accept: 'application/json'
|
||||
}
|
||||
|
||||
let formData
|
||||
if (typeof FormData === 'undefined') {
|
||||
formData = new NodeFormData()
|
||||
} else {
|
||||
formData = new FormData()
|
||||
}
|
||||
formData.append('grant_type', 'authorization_code')
|
||||
formData.append('code', authCode)
|
||||
const data = new URLSearchParams({
|
||||
grant_type: 'authorization_code',
|
||||
code: authCode
|
||||
})
|
||||
|
||||
const authResponse = await requestClient
|
||||
.post(
|
||||
url,
|
||||
formData,
|
||||
undefined,
|
||||
'multipart/form-data; boundary=' + (formData as any)._boundary,
|
||||
headers
|
||||
)
|
||||
.post(url, data, undefined, 'application/x-www-form-urlencoded', headers)
|
||||
.then((res) => res.result as SasAuthResponse)
|
||||
.catch((err) => {
|
||||
throw prefixMessage(err, 'Error while getting access token. ')
|
||||
|
||||
@@ -35,11 +35,12 @@ describe('getAccessTokenForViya', () => {
|
||||
|
||||
expect(requestClient.post).toHaveBeenCalledWith(
|
||||
'/SASLogon/oauth/token',
|
||||
expect.any(NodeFormData),
|
||||
expect.any(URLSearchParams),
|
||||
undefined,
|
||||
expect.stringContaining('multipart/form-data; boundary='),
|
||||
'application/x-www-form-urlencoded',
|
||||
{
|
||||
Authorization: 'Basic ' + token
|
||||
Authorization: 'Basic ' + token,
|
||||
Accept: 'application/json'
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
@@ -18,7 +18,7 @@ export const generateFileUploadForm = (
|
||||
if (!Array.isArray(data[tableName])) continue
|
||||
|
||||
const name = tableName
|
||||
const csv = convertToCSV(data[tableName])
|
||||
const csv = convertToCSV(data, tableName)
|
||||
|
||||
if (csv === 'ERROR: LARGE STRING LENGTH') {
|
||||
throw new Error(
|
||||
|
||||
@@ -9,18 +9,24 @@ export const generateTableUploadForm = (
|
||||
const sasjsTables = []
|
||||
const requestParams: any = {}
|
||||
let tableCounter = 0
|
||||
|
||||
for (const tableName in data) {
|
||||
tableCounter++
|
||||
|
||||
sasjsTables.push(tableName)
|
||||
const csv = convertToCSV(data[tableName])
|
||||
|
||||
const csv = convertToCSV(data, tableName)
|
||||
|
||||
if (csv === 'ERROR: LARGE STRING LENGTH') {
|
||||
throw new Error(
|
||||
'The max length of a string value in SASjs is 32765 characters.'
|
||||
)
|
||||
}
|
||||
|
||||
// if csv has length more then 16k, send in chunks
|
||||
if (csv.length > 16000) {
|
||||
const csvChunks = splitChunks(csv)
|
||||
|
||||
// append chunks to form data with same key
|
||||
csvChunks.map((chunk) => {
|
||||
formData.append(`sasjs${tableCounter}data`, chunk)
|
||||
@@ -29,6 +35,7 @@ export const generateTableUploadForm = (
|
||||
requestParams[`sasjs${tableCounter}data`] = csv
|
||||
}
|
||||
}
|
||||
|
||||
requestParams['sasjs_tables'] = sasjsTables.join(' ')
|
||||
|
||||
return { formData, requestParams }
|
||||
|
||||
@@ -125,7 +125,8 @@ const generateFileUploadForm = (
|
||||
): NodeFormData => {
|
||||
for (const tableName in data) {
|
||||
const name = tableName
|
||||
const csv = convertToCSV(data[tableName])
|
||||
const csv = convertToCSV(data, tableName)
|
||||
|
||||
if (csv === 'ERROR: LARGE STRING LENGTH') {
|
||||
throw new Error(
|
||||
'The max length of a string value in SASjs is 32765 characters.'
|
||||
|
||||
@@ -1,183 +1,228 @@
|
||||
import { convertToCSV } from './convertToCsv'
|
||||
|
||||
describe('convertToCsv', () => {
|
||||
const tableName = 'testTable'
|
||||
|
||||
it('should convert single quoted values', () => {
|
||||
const data = [
|
||||
{ foo: `'bar'`, bar: 'abc' },
|
||||
{ foo: 'sadf', bar: 'def' },
|
||||
{ foo: 'asd', bar: `'qwert'` }
|
||||
]
|
||||
const data = {
|
||||
[tableName]: [
|
||||
{ foo: `'bar'`, bar: 'abc' },
|
||||
{ foo: 'sadf', bar: 'def' },
|
||||
{ foo: 'asd', bar: `'qwert'` }
|
||||
]
|
||||
}
|
||||
|
||||
const expectedOutput = `foo:$char5. bar:$char7.\r\n"'bar'",abc\r\nsadf,def\r\nasd,"'qwert'"`
|
||||
|
||||
expect(convertToCSV(data)).toEqual(expectedOutput)
|
||||
expect(convertToCSV(data, tableName)).toEqual(expectedOutput)
|
||||
})
|
||||
|
||||
it('should convert double quoted values', () => {
|
||||
const data = [
|
||||
{ foo: `"bar"`, bar: 'abc' },
|
||||
{ foo: 'sadf', bar: 'def' },
|
||||
{ foo: 'asd', bar: `"qwert"` }
|
||||
]
|
||||
const data = {
|
||||
[tableName]: [
|
||||
{ foo: `"bar"`, bar: 'abc' },
|
||||
{ foo: 'sadf', bar: 'def' },
|
||||
{ foo: 'asd', bar: `"qwert"` }
|
||||
]
|
||||
}
|
||||
|
||||
const expectedOutput = `foo:$char5. bar:$char7.\r\n"""bar""",abc\r\nsadf,def\r\nasd,"""qwert"""`
|
||||
|
||||
expect(convertToCSV(data)).toEqual(expectedOutput)
|
||||
expect(convertToCSV(data, tableName)).toEqual(expectedOutput)
|
||||
})
|
||||
|
||||
it('should convert values with mixed quotes', () => {
|
||||
const data = [{ foo: `'blah'`, bar: `"blah"` }]
|
||||
const data = { [tableName]: [{ foo: `'blah'`, bar: `"blah"` }] }
|
||||
|
||||
const expectedOutput = `foo:$char6. bar:$char6.\r\n"'blah'","""blah"""`
|
||||
|
||||
expect(convertToCSV(data)).toEqual(expectedOutput)
|
||||
expect(convertToCSV(data, tableName)).toEqual(expectedOutput)
|
||||
})
|
||||
|
||||
it('should convert values with mixed quotes', () => {
|
||||
const data = [{ foo: `'blah,"'`, bar: `"blah,blah" "` }]
|
||||
const data = { [tableName]: [{ foo: `'blah,"'`, bar: `"blah,blah" "` }] }
|
||||
|
||||
const expectedOutput = `foo:$char8. bar:$char13.\r\n"'blah,""'","""blah,blah"" """`
|
||||
|
||||
expect(convertToCSV(data)).toEqual(expectedOutput)
|
||||
expect(convertToCSV(data, tableName)).toEqual(expectedOutput)
|
||||
})
|
||||
|
||||
it('should convert values with mixed quotes', () => {
|
||||
const data = [{ foo: `',''`, bar: `","` }]
|
||||
const data = { [tableName]: [{ foo: `',''`, bar: `","` }] }
|
||||
|
||||
const expectedOutput = `foo:$char4. bar:$char3.\r\n"',''",""","""`
|
||||
|
||||
expect(convertToCSV(data)).toEqual(expectedOutput)
|
||||
expect(convertToCSV(data, tableName)).toEqual(expectedOutput)
|
||||
})
|
||||
|
||||
it('should convert values with mixed quotes', () => {
|
||||
const data = [{ foo: `','`, bar: `,"` }]
|
||||
const data = { [tableName]: [{ foo: `','`, bar: `,"` }] }
|
||||
|
||||
const expectedOutput = `foo:$char3. bar:$char2.\r\n"','",","""`
|
||||
|
||||
expect(convertToCSV(data)).toEqual(expectedOutput)
|
||||
expect(convertToCSV(data, tableName)).toEqual(expectedOutput)
|
||||
})
|
||||
|
||||
it('should convert values with mixed quotes', () => {
|
||||
const data = [{ foo: `"`, bar: `'` }]
|
||||
const data = { [tableName]: [{ foo: `"`, bar: `'` }] }
|
||||
|
||||
const expectedOutput = `foo:$char1. bar:$char1.\r\n"""","'"`
|
||||
|
||||
expect(convertToCSV(data)).toEqual(expectedOutput)
|
||||
expect(convertToCSV(data, tableName)).toEqual(expectedOutput)
|
||||
})
|
||||
|
||||
it('should convert values with mixed quotes', () => {
|
||||
const data = [{ foo: `,`, bar: `',` }]
|
||||
const data = { [tableName]: [{ foo: `,`, bar: `',` }] }
|
||||
|
||||
const expectedOutput = `foo:$char1. bar:$char2.\r\n",","',"`
|
||||
|
||||
expect(convertToCSV(data)).toEqual(expectedOutput)
|
||||
expect(convertToCSV(data, tableName)).toEqual(expectedOutput)
|
||||
})
|
||||
|
||||
it('should convert values with number cases 1', () => {
|
||||
const data = [
|
||||
{ col1: 42, col2: null, col3: 'x', col4: null },
|
||||
{ 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: '' }
|
||||
]
|
||||
const data = {
|
||||
[tableName]: [
|
||||
{ col1: 42, col2: null, col3: 'x', col4: null },
|
||||
{ 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: '' }
|
||||
]
|
||||
}
|
||||
|
||||
const expectedOutput = `col1:best. col2:best. col3:$char1. col4:$char1.\r\n42,.,x,\r\n42,.,x,\r\n42,.,x,\r\n42,.,x,\r\n42,.,x,`
|
||||
|
||||
expect(convertToCSV(data)).toEqual(expectedOutput)
|
||||
expect(convertToCSV(data, tableName)).toEqual(expectedOutput)
|
||||
})
|
||||
|
||||
it('should convert values with number cases 2', () => {
|
||||
const data = [
|
||||
{ col1: 42, col2: null, col3: 'x', col4: '' },
|
||||
{ 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' }
|
||||
]
|
||||
const data = {
|
||||
[tableName]: [
|
||||
{ col1: 42, col2: null, col3: 'x', col4: '' },
|
||||
{ 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' }
|
||||
]
|
||||
}
|
||||
|
||||
const expectedOutput = `col1:best. col2:best. col3:$char1. col4:$char1.\r\n42,.,x,\r\n42,.,x,\r\n42,.,x,\r\n42,1.62,x,x\r\n42,1.62,x,x`
|
||||
|
||||
expect(convertToCSV(data)).toEqual(expectedOutput)
|
||||
expect(convertToCSV(data, tableName)).toEqual(expectedOutput)
|
||||
})
|
||||
|
||||
it('should convert values with common special characters', () => {
|
||||
expect(convertToCSV([{ tab: '\t' }])).toEqual(`tab:$char1.\r\n\"\t\"`)
|
||||
expect(convertToCSV([{ lf: '\n' }])).toEqual(`lf:$char1.\r\n\"\n\"`)
|
||||
expect(convertToCSV([{ semicolon: ';semi' }])).toEqual(
|
||||
`semicolon:$char5.\r\n;semi`
|
||||
expect(convertToCSV({ [tableName]: [{ tab: '\t' }] }, tableName)).toEqual(
|
||||
`tab:$char1.\r\n\"\t\"`
|
||||
)
|
||||
expect(convertToCSV([{ percent: '%' }])).toEqual(`percent:$char1.\r\n%`)
|
||||
expect(convertToCSV([{ singleQuote: "'" }])).toEqual(
|
||||
`singleQuote:$char1.\r\n\"'\"`
|
||||
)
|
||||
expect(convertToCSV([{ doubleQuote: '"' }])).toEqual(
|
||||
`doubleQuote:$char1.\r\n""""`
|
||||
)
|
||||
expect(convertToCSV([{ crlf: '\r\n' }])).toEqual(`crlf:$char2.\r\n\"\n\"`)
|
||||
expect(convertToCSV([{ euro: '€euro' }])).toEqual(`euro:$char7.\r\n€euro`)
|
||||
expect(convertToCSV([{ banghash: '!#banghash' }])).toEqual(
|
||||
`banghash:$char10.\r\n!#banghash`
|
||||
expect(convertToCSV({ [tableName]: [{ lf: '\n' }] }, tableName)).toEqual(
|
||||
`lf:$char1.\r\n\"\n\"`
|
||||
)
|
||||
expect(
|
||||
convertToCSV({ [tableName]: [{ semicolon: ';semi' }] }, tableName)
|
||||
).toEqual(`semicolon:$char5.\r\n;semi`)
|
||||
expect(
|
||||
convertToCSV({ [tableName]: [{ percent: '%' }] }, tableName)
|
||||
).toEqual(`percent:$char1.\r\n%`)
|
||||
expect(
|
||||
convertToCSV({ [tableName]: [{ singleQuote: "'" }] }, tableName)
|
||||
).toEqual(`singleQuote:$char1.\r\n\"'\"`)
|
||||
expect(
|
||||
convertToCSV({ [tableName]: [{ doubleQuote: '"' }] }, tableName)
|
||||
).toEqual(`doubleQuote:$char1.\r\n""""`)
|
||||
expect(
|
||||
convertToCSV({ [tableName]: [{ crlf: '\r\n' }] }, tableName)
|
||||
).toEqual(`crlf:$char2.\r\n\"\n\"`)
|
||||
expect(
|
||||
convertToCSV({ [tableName]: [{ euro: '€euro' }] }, tableName)
|
||||
).toEqual(`euro:$char7.\r\n€euro`)
|
||||
expect(
|
||||
convertToCSV({ [tableName]: [{ banghash: '!#banghash' }] }, tableName)
|
||||
).toEqual(`banghash:$char10.\r\n!#banghash`)
|
||||
})
|
||||
|
||||
it('should convert values with other special characters', () => {
|
||||
const data = [
|
||||
{
|
||||
speech0: '"speech',
|
||||
pct: '%percent',
|
||||
speech: '"speech',
|
||||
slash: '\\slash',
|
||||
slashWithSpecial: '\\\tslash',
|
||||
macvar: '&sysuserid',
|
||||
chinese: '传/傳chinese',
|
||||
sigma: 'Σsigma',
|
||||
at: '@at',
|
||||
serbian: 'Српски',
|
||||
dollar: '$'
|
||||
}
|
||||
]
|
||||
const data = {
|
||||
[tableName]: [
|
||||
{
|
||||
speech0: '"speech',
|
||||
pct: '%percent',
|
||||
speech: '"speech',
|
||||
slash: '\\slash',
|
||||
slashWithSpecial: '\\\tslash',
|
||||
macvar: '&sysuserid',
|
||||
chinese: '传/傳chinese',
|
||||
sigma: 'Σsigma',
|
||||
at: '@at',
|
||||
serbian: 'Српски',
|
||||
dollar: '$'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const expectedOutput = `speech0:$char7. pct:$char8. speech:$char7. slash:$char6. slashWithSpecial:$char7. macvar:$char10. chinese:$char14. sigma:$char7. at:$char3. serbian:$char12. dollar:$char1.\r\n"""speech",%percent,"""speech",\\slash,\"\\\tslash\",&sysuserid,传/傳chinese,Σsigma,@at,Српски,$`
|
||||
|
||||
expect(convertToCSV(data)).toEqual(expectedOutput)
|
||||
expect(convertToCSV(data, tableName)).toEqual(expectedOutput)
|
||||
|
||||
expect(convertToCSV([{ speech: 'menext' }])).toEqual(
|
||||
`speech:$char6.\r\nmenext`
|
||||
)
|
||||
expect(convertToCSV([{ speech: 'me\nnext' }])).toEqual(
|
||||
`speech:$char7.\r\n\"me\nnext\"`
|
||||
)
|
||||
expect(convertToCSV([{ speech: `me'next` }])).toEqual(
|
||||
`speech:$char7.\r\n\"me'next\"`
|
||||
)
|
||||
expect(convertToCSV([{ speech: `me"next` }])).toEqual(
|
||||
`speech:$char7.\r\n\"me""next\"`
|
||||
)
|
||||
expect(convertToCSV([{ speech: `me""next` }])).toEqual(
|
||||
`speech:$char8.\r\n\"me""""next\"`
|
||||
)
|
||||
expect(convertToCSV([{ slashWithSpecial: '\\\tslash' }])).toEqual(
|
||||
`slashWithSpecial:$char7.\r\n\"\\\tslash\"`
|
||||
)
|
||||
expect(convertToCSV([{ slashWithSpecial: '\\ \tslash' }])).toEqual(
|
||||
`slashWithSpecial:$char8.\r\n\"\\ \tslash\"`
|
||||
)
|
||||
expect(
|
||||
convertToCSV([{ slashWithSpecialExtra: '\\\ts\tl\ta\ts\t\th\t' }])
|
||||
convertToCSV({ [tableName]: [{ speech: 'menext' }] }, tableName)
|
||||
).toEqual(`speech:$char6.\r\nmenext`)
|
||||
expect(
|
||||
convertToCSV({ [tableName]: [{ speech: 'me\nnext' }] }, tableName)
|
||||
).toEqual(`speech:$char7.\r\n\"me\nnext\"`)
|
||||
expect(
|
||||
convertToCSV({ [tableName]: [{ speech: `me'next` }] }, tableName)
|
||||
).toEqual(`speech:$char7.\r\n\"me'next\"`)
|
||||
expect(
|
||||
convertToCSV({ [tableName]: [{ speech: `me"next` }] }, tableName)
|
||||
).toEqual(`speech:$char7.\r\n\"me""next\"`)
|
||||
expect(
|
||||
convertToCSV({ [tableName]: [{ speech: `me""next` }] }, tableName)
|
||||
).toEqual(`speech:$char8.\r\n\"me""""next\"`)
|
||||
expect(
|
||||
convertToCSV(
|
||||
{ [tableName]: [{ slashWithSpecial: '\\\tslash' }] },
|
||||
tableName
|
||||
)
|
||||
).toEqual(`slashWithSpecial:$char7.\r\n\"\\\tslash\"`)
|
||||
expect(
|
||||
convertToCSV(
|
||||
{ [tableName]: [{ slashWithSpecial: '\\ \tslash' }] },
|
||||
tableName
|
||||
)
|
||||
).toEqual(`slashWithSpecial:$char8.\r\n\"\\ \tslash\"`)
|
||||
expect(
|
||||
convertToCSV(
|
||||
{ [tableName]: [{ slashWithSpecialExtra: '\\\ts\tl\ta\ts\t\th\t' }] },
|
||||
tableName
|
||||
)
|
||||
).toEqual(`slashWithSpecialExtra:$char13.\r\n\"\\\ts\tl\ta\ts\t\th\t\"`)
|
||||
})
|
||||
|
||||
it('should console log error if data has mixed types', () => {
|
||||
const colName = 'var1'
|
||||
const data = [{ [colName]: 'string' }, { [colName]: 232 }]
|
||||
const data = { [tableName]: [{ [colName]: 'string' }, { [colName]: 232 }] }
|
||||
|
||||
jest.spyOn(console, 'error').mockImplementation(() => {})
|
||||
|
||||
convertToCSV(data)
|
||||
convertToCSV(data, tableName)
|
||||
|
||||
expect(console.error).toHaveBeenCalledWith(
|
||||
`Row (2), Column (${colName}) has mixed types: ERROR`
|
||||
)
|
||||
})
|
||||
|
||||
it('should throw an error if table was not found in data object', () => {
|
||||
const data = { [tableName]: [{ var1: 'string' }] }
|
||||
|
||||
expect(() => convertToCSV(data, 'wrongTableName')).toThrow(
|
||||
new Error('No table provided to be converted to CSV')
|
||||
)
|
||||
})
|
||||
|
||||
it('should empty string if table is not an array', () => {
|
||||
const data = { [tableName]: true }
|
||||
|
||||
expect(convertToCSV(data, tableName)).toEqual('')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,10 +3,18 @@
|
||||
* @param data - the array of JSON objects to convert.
|
||||
*/
|
||||
export const convertToCSV = (
|
||||
data: any[],
|
||||
sasFormats?: { formats: { [key: string]: string } }
|
||||
data: { [key: string]: any },
|
||||
tableName: string
|
||||
) => {
|
||||
let formats = sasFormats?.formats
|
||||
if (!data[tableName]) {
|
||||
throw new Error('No table provided to be converted to CSV')
|
||||
}
|
||||
|
||||
const table = data[tableName]
|
||||
|
||||
if (!Array.isArray(table)) return ''
|
||||
|
||||
let formats = data[`$${tableName}`]?.formats
|
||||
let headers: string[] = []
|
||||
let csvTest
|
||||
let invalidString = false
|
||||
@@ -16,14 +24,14 @@ export const convertToCSV = (
|
||||
headers = Object.keys(formats).map((key) => `${key}:${formats![key]}`)
|
||||
}
|
||||
|
||||
const headerFields = Object.keys(data[0])
|
||||
const headerFields = Object.keys(table[0])
|
||||
|
||||
headerFields.forEach((field) => {
|
||||
if (!formats || !Object.keys(formats).includes(field)) {
|
||||
let hasNullOrNumber = false
|
||||
let hasSpecialMissingString = false
|
||||
|
||||
data.forEach((row: { [key: string]: any }) => {
|
||||
table.forEach((row: { [key: string]: any }) => {
|
||||
if (row[field] === null || typeof row[field] === 'number') {
|
||||
hasNullOrNumber = true
|
||||
} else if (
|
||||
@@ -45,7 +53,7 @@ export const convertToCSV = (
|
||||
let hasMixedTypes: boolean = false
|
||||
let rowNumError: number = -1
|
||||
|
||||
const longestValueForField = data
|
||||
const longestValueForField = table
|
||||
.map((row: any, index: number) => {
|
||||
if (row[field] || row[field] === '') {
|
||||
if (firstFoundType) {
|
||||
@@ -101,7 +109,7 @@ export const convertToCSV = (
|
||||
}
|
||||
})
|
||||
|
||||
if (sasFormats) {
|
||||
if (formats) {
|
||||
headers = headers.sort(
|
||||
(a, b) =>
|
||||
headerFields.indexOf(a.replace(/:.*/, '')) -
|
||||
@@ -111,7 +119,7 @@ export const convertToCSV = (
|
||||
|
||||
if (invalidString) return 'ERROR: LARGE STRING LENGTH'
|
||||
|
||||
csvTest = data.map((row: any) => {
|
||||
csvTest = table.map((row: any) => {
|
||||
const fields = Object.keys(row).map((fieldName, index) => {
|
||||
let value
|
||||
const currentCell = row[fieldName]
|
||||
|
||||
@@ -15,19 +15,24 @@ export const formatDataForRequest = (data: any) => {
|
||||
}
|
||||
|
||||
tableCounter++
|
||||
|
||||
sasjsTables.push(tableName)
|
||||
const csv = convertToCSV(data[tableName], data[`$${tableName}`])
|
||||
|
||||
const csv = convertToCSV(data, tableName)
|
||||
|
||||
if (csv === 'ERROR: LARGE STRING LENGTH') {
|
||||
throw new Error(
|
||||
'The max length of a string value in SASjs is 32765 characters.'
|
||||
)
|
||||
}
|
||||
|
||||
// if csv has length more then 16k, send in chunks
|
||||
if (csv.length > 16000) {
|
||||
const csvChunks = splitChunks(csv)
|
||||
|
||||
// append chunks to form data with same key
|
||||
result[`sasjs${tableCounter}data0`] = csvChunks.length
|
||||
|
||||
csvChunks.forEach((chunk, index) => {
|
||||
result[`sasjs${tableCounter}data${index + 1}`] = chunk
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user