diff --git a/package-lock.json b/package-lock.json index 68adaae..eb587c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "hasInstallScript": true, "license": "ISC", "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", @@ -1143,23 +1143,50 @@ } }, "node_modules/@sasjs/utils": { - "version": "2.35.0", - "resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.35.0.tgz", - "integrity": "sha512-q9ZKV+TXqwiaj+0z5U7/00eBpp2QpjKfC9BKx7A6rQjBl10WtoWd5C9Em+RQULWVEdRbVS2XcnNsWelbKq/Zsw==", + "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.13", - "@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", - "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-release/commit-analyzer": { @@ -1644,9 +1671,9 @@ "dev": true }, "node_modules/@types/prompts": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.0.14.tgz", - "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": "*" } @@ -2032,6 +2059,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { "node": ">=8" } @@ -2656,6 +2684,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2726,12 +2755,14 @@ } }, "node_modules/cli-table": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.9.tgz", - "integrity": "sha512-7eA6hFtAZwVx3dWAGoaBqTrzWko5jRUFKpHT64ZHkJpaA3y5wf5NlLjguqTRmqycatJZiwftODYYyGNLbQ7MuA==", + "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", - "strip-ansi": "^6.0.1" + "colors": "1.0.3" + }, + "engines": { + "node": ">= 0.2.0" } }, "node_modules/cli-table3": { @@ -2876,9 +2907,9 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "node_modules/consola": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "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", @@ -10537,6 +10568,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" @@ -11686,6 +11718,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -14048,22 +14081,42 @@ } }, "@sasjs/utils": { - "version": "2.35.0", - "resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.35.0.tgz", - "integrity": "sha512-q9ZKV+TXqwiaj+0z5U7/00eBpp2QpjKfC9BKx7A6rQjBl10WtoWd5C9Em+RQULWVEdRbVS2XcnNsWelbKq/Zsw==", + "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.13", - "@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", - "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-release/commit-analyzer": { @@ -14488,9 +14541,9 @@ "dev": true }, "@types/prompts": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.0.14.tgz", - "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": "*" } @@ -14835,7 +14888,8 @@ "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true }, "ansi-styles": { "version": "4.3.0", @@ -15319,6 +15373,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -15371,12 +15426,11 @@ "dev": true }, "cli-table": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.9.tgz", - "integrity": "sha512-7eA6hFtAZwVx3dWAGoaBqTrzWko5jRUFKpHT64ZHkJpaA3y5wf5NlLjguqTRmqycatJZiwftODYYyGNLbQ7MuA==", + "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", - "strip-ansi": "^6.0.1" + "colors": "1.0.3" } }, "cli-table3": { @@ -15498,9 +15552,9 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "consola": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "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", @@ -21248,6 +21302,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, "requires": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" @@ -22150,6 +22205,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "requires": { "ansi-regex": "^5.0.1" } diff --git a/package.json b/package.json index 1d0a17b..0447332 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ }, "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", diff --git a/sasjs-tests/README.md b/sasjs-tests/README.md index 53b0a5f..573ae53 100644 --- a/sasjs-tests/README.md +++ b/sasjs-tests/README.md @@ -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() diff --git a/sasjs-tests/package-lock.json b/sasjs-tests/package-lock.json index 9d6d4fa..1117451 100644 --- a/sasjs-tests/package-lock.json +++ b/sasjs-tests/package-lock.json @@ -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": { diff --git a/sasjs-tests/src/testSuites/SpecialCases.ts b/sasjs-tests/src/testSuites/SpecialCases.ts index 2bf9c88..23db5c2 100644 --- a/sasjs-tests/src/testSuites/SpecialCases.ts +++ b/sasjs-tests/src/testSuites/SpecialCases.ts @@ -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 } }) diff --git a/src/api/viya/uploadTables.ts b/src/api/viya/uploadTables.ts index b9e4402..47f3208 100644 --- a/src/api/viya/uploadTables.ts +++ b/src/api/viya/uploadTables.ts @@ -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.' diff --git a/src/file/generateFileUploadForm.ts b/src/file/generateFileUploadForm.ts index dd5661c..04e9c30 100644 --- a/src/file/generateFileUploadForm.ts +++ b/src/file/generateFileUploadForm.ts @@ -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( diff --git a/src/file/generateTableUploadForm.ts b/src/file/generateTableUploadForm.ts index 450333b..f2e8b08 100644 --- a/src/file/generateTableUploadForm.ts +++ b/src/file/generateTableUploadForm.ts @@ -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 } diff --git a/src/job-execution/Sas9JobExecutor.ts b/src/job-execution/Sas9JobExecutor.ts index 82336b4..b1c1fd0 100644 --- a/src/job-execution/Sas9JobExecutor.ts +++ b/src/job-execution/Sas9JobExecutor.ts @@ -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.' diff --git a/src/utils/convertToCsv.spec.ts b/src/utils/convertToCsv.spec.ts index 9ce9f9d..7c5f927 100644 --- a/src/utils/convertToCsv.spec.ts +++ b/src/utils/convertToCsv.spec.ts @@ -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('') + }) }) diff --git a/src/utils/convertToCsv.ts b/src/utils/convertToCsv.ts index 73af3ba..bab7788 100644 --- a/src/utils/convertToCsv.ts +++ b/src/utils/convertToCsv.ts @@ -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] diff --git a/src/utils/formatDataForRequest.ts b/src/utils/formatDataForRequest.ts index d223432..bf73457 100644 --- a/src/utils/formatDataForRequest.ts +++ b/src/utils/formatDataForRequest.ts @@ -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 })