mirror of
https://github.com/sasjs/adapter.git
synced 2025-12-11 09:24:35 +00:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cbc2bda08 | ||
|
|
5bc2afba8c | ||
| dd3a7fe393 | |||
|
|
ba1b1e6e80 | ||
|
|
3daa85a74a | ||
|
|
d1139857a4 | ||
|
|
cb106c76cb | ||
|
|
ab8da28de1 | ||
| a729d67d3e | |||
| 548a44d665 | |||
|
|
afda43fc7f | ||
| 5291e7f01c | |||
| 39abdad518 | |||
|
|
6aa12ee950 | ||
|
|
b5b5093295 | ||
|
|
114ca21c17 | ||
|
|
6aee95b21d | ||
| 3d281abbf8 | |||
| 99d783e174 | |||
| 17a3d1b8a9 | |||
|
|
01af5eb634 | ||
| 0c3797e2de | |||
| c33c509207 | |||
| af351d7375 | |||
| 2b53406cac | |||
| 99cfb8b2af | |||
|
|
22fa185715 |
@@ -106,6 +106,16 @@
|
||||
"userTesting",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "saramartinelli1992",
|
||||
"name": "Sara",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/100193908?v=4",
|
||||
"profile": "https://github.com/saramartinelli1992",
|
||||
"contributions": [
|
||||
"userTesting",
|
||||
"platform"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
29
README.md
29
README.md
@@ -332,7 +332,7 @@ If you find this library useful, help us grow our star graph!
|
||||
|
||||
## Contributors ✨
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
[](#contributors-)
|
||||
[](#contributors-)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||
@@ -341,18 +341,21 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://krishna-acondy.io/"><img src="https://avatars.githubusercontent.com/u/2980428?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Krishna Acondy</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=krishna-acondy" title="Code">💻</a> <a href="#infra-krishna-acondy" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#blog-krishna-acondy" title="Blogposts">📝</a> <a href="#content-krishna-acondy" title="Content">🖋</a> <a href="#ideas-krishna-acondy" title="Ideas, Planning, & Feedback">🤔</a> <a href="#video-krishna-acondy" title="Videos">📹</a></td>
|
||||
<td align="center"><a href="https://www.erudicat.com/"><img src="https://avatars.githubusercontent.com/u/25773492?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yury Shkoda</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=YuryShkoda" title="Code">💻</a> <a href="#infra-YuryShkoda" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#ideas-YuryShkoda" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/sasjs/adapter/commits?author=YuryShkoda" title="Tests">⚠️</a> <a href="#video-YuryShkoda" title="Videos">📹</a></td>
|
||||
<td align="center"><a href="https://github.com/medjedovicm"><img src="https://avatars.githubusercontent.com/u/18329105?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mihajlo Medjedovic</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=medjedovicm" title="Code">💻</a> <a href="#infra-medjedovicm" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/sasjs/adapter/commits?author=medjedovicm" title="Tests">⚠️</a> <a href="https://github.com/sasjs/adapter/pulls?q=is%3Apr+reviewed-by%3Amedjedovicm" title="Reviewed Pull Requests">👀</a></td>
|
||||
<td align="center"><a href="https://github.com/allanbowe"><img src="https://avatars.githubusercontent.com/u/4420615?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Allan Bowe</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=allanbowe" title="Code">💻</a> <a href="https://github.com/sasjs/adapter/pulls?q=is%3Apr+reviewed-by%3Aallanbowe" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sasjs/adapter/commits?author=allanbowe" title="Tests">⚠️</a> <a href="#mentoring-allanbowe" title="Mentoring">🧑🏫</a> <a href="#maintenance-allanbowe" title="Maintenance">🚧</a></td>
|
||||
<td align="center"><a href="https://github.com/saadjutt01"><img src="https://avatars.githubusercontent.com/u/8914650?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Muhammad Saad </b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=saadjutt01" title="Code">💻</a> <a href="https://github.com/sasjs/adapter/pulls?q=is%3Apr+reviewed-by%3Asaadjutt01" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sasjs/adapter/commits?author=saadjutt01" title="Tests">⚠️</a> <a href="#mentoring-saadjutt01" title="Mentoring">🧑🏫</a> <a href="#infra-saadjutt01" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
|
||||
<td align="center"><a href="https://github.com/sabhas"><img src="https://avatars.githubusercontent.com/u/82647447?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sabir Hassan</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=sabhas" title="Code">💻</a> <a href="https://github.com/sasjs/adapter/pulls?q=is%3Apr+reviewed-by%3Asabhas" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sasjs/adapter/commits?author=sabhas" title="Tests">⚠️</a> <a href="#ideas-sabhas" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center"><a href="https://github.com/VladislavParhomchik"><img src="https://avatars.githubusercontent.com/u/83717836?v=4?s=100" width="100px;" alt=""/><br /><sub><b>VladislavParhomchik</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=VladislavParhomchik" title="Tests">⚠️</a> <a href="https://github.com/sasjs/adapter/pulls?q=is%3Apr+reviewed-by%3AVladislavParhomchik" title="Reviewed Pull Requests">👀</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://rudvfaden.github.io/"><img src="https://avatars.githubusercontent.com/u/2445577?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rud Faden</b></sub></a><br /><a href="#userTesting-rudvfaden" title="User Testing">📓</a> <a href="https://github.com/sasjs/adapter/commits?author=rudvfaden" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://krishna-acondy.io/"><img src="https://avatars.githubusercontent.com/u/2980428?v=4?s=100" width="100px;" alt="Krishna Acondy"/><br /><sub><b>Krishna Acondy</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=krishna-acondy" title="Code">💻</a> <a href="#infra-krishna-acondy" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#blog-krishna-acondy" title="Blogposts">📝</a> <a href="#content-krishna-acondy" title="Content">🖋</a> <a href="#ideas-krishna-acondy" title="Ideas, Planning, & Feedback">🤔</a> <a href="#video-krishna-acondy" title="Videos">📹</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.erudicat.com/"><img src="https://avatars.githubusercontent.com/u/25773492?v=4?s=100" width="100px;" alt="Yury Shkoda"/><br /><sub><b>Yury Shkoda</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=YuryShkoda" title="Code">💻</a> <a href="#infra-YuryShkoda" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#ideas-YuryShkoda" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/sasjs/adapter/commits?author=YuryShkoda" title="Tests">⚠️</a> <a href="#video-YuryShkoda" title="Videos">📹</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/medjedovicm"><img src="https://avatars.githubusercontent.com/u/18329105?v=4?s=100" width="100px;" alt="Mihajlo Medjedovic"/><br /><sub><b>Mihajlo Medjedovic</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=medjedovicm" title="Code">💻</a> <a href="#infra-medjedovicm" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/sasjs/adapter/commits?author=medjedovicm" title="Tests">⚠️</a> <a href="https://github.com/sasjs/adapter/pulls?q=is%3Apr+reviewed-by%3Amedjedovicm" title="Reviewed Pull Requests">👀</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/allanbowe"><img src="https://avatars.githubusercontent.com/u/4420615?v=4?s=100" width="100px;" alt="Allan Bowe"/><br /><sub><b>Allan Bowe</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=allanbowe" title="Code">💻</a> <a href="https://github.com/sasjs/adapter/pulls?q=is%3Apr+reviewed-by%3Aallanbowe" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sasjs/adapter/commits?author=allanbowe" title="Tests">⚠️</a> <a href="#mentoring-allanbowe" title="Mentoring">🧑🏫</a> <a href="#maintenance-allanbowe" title="Maintenance">🚧</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/saadjutt01"><img src="https://avatars.githubusercontent.com/u/8914650?v=4?s=100" width="100px;" alt="Muhammad Saad "/><br /><sub><b>Muhammad Saad </b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=saadjutt01" title="Code">💻</a> <a href="https://github.com/sasjs/adapter/pulls?q=is%3Apr+reviewed-by%3Asaadjutt01" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sasjs/adapter/commits?author=saadjutt01" title="Tests">⚠️</a> <a href="#mentoring-saadjutt01" title="Mentoring">🧑🏫</a> <a href="#infra-saadjutt01" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sabhas"><img src="https://avatars.githubusercontent.com/u/82647447?v=4?s=100" width="100px;" alt="Sabir Hassan"/><br /><sub><b>Sabir Hassan</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=sabhas" title="Code">💻</a> <a href="https://github.com/sasjs/adapter/pulls?q=is%3Apr+reviewed-by%3Asabhas" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sasjs/adapter/commits?author=sabhas" title="Tests">⚠️</a> <a href="#ideas-sabhas" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/VladislavParhomchik"><img src="https://avatars.githubusercontent.com/u/83717836?v=4?s=100" width="100px;" alt="VladislavParhomchik"/><br /><sub><b>VladislavParhomchik</b></sub></a><br /><a href="https://github.com/sasjs/adapter/commits?author=VladislavParhomchik" title="Tests">⚠️</a> <a href="https://github.com/sasjs/adapter/pulls?q=is%3Apr+reviewed-by%3AVladislavParhomchik" title="Reviewed Pull Requests">👀</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://rudvfaden.github.io/"><img src="https://avatars.githubusercontent.com/u/2445577?v=4?s=100" width="100px;" alt="Rud Faden"/><br /><sub><b>Rud Faden</b></sub></a><br /><a href="#userTesting-rudvfaden" title="User Testing">📓</a> <a href="https://github.com/sasjs/adapter/commits?author=rudvfaden" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/saramartinelli1992"><img src="https://avatars.githubusercontent.com/u/100193908?v=4?s=100" width="100px;" alt="Sara"/><br /><sub><b>Sara</b></sub></a><br /><a href="#userTesting-saramartinelli1992" title="User Testing">📓</a> <a href="#platform-saramartinelli1992" title="Packaging/porting to new platform">📦</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-restore -->
|
||||
|
||||
4486
package-lock.json
generated
4486
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -59,12 +59,12 @@
|
||||
"jest-extended": "2.0.0",
|
||||
"node-polyfill-webpack-plugin": "1.1.4",
|
||||
"path": "0.12.7",
|
||||
"pem": "1.14.6",
|
||||
"pem": "1.14.5",
|
||||
"prettier": "2.7.1",
|
||||
"process": "0.11.10",
|
||||
"rimraf": "3.0.2",
|
||||
"semantic-release": "19.0.3",
|
||||
"terser-webpack-plugin": "5.3.1",
|
||||
"terser-webpack-plugin": "5.3.6",
|
||||
"ts-jest": "27.1.3",
|
||||
"ts-loader": "9.4.0",
|
||||
"tslint": "6.1.3",
|
||||
@@ -72,7 +72,7 @@
|
||||
"typedoc": "0.23.24",
|
||||
"typedoc-plugin-rename-defaults": "0.6.4",
|
||||
"typescript": "4.8.3",
|
||||
"webpack": "5.69.0",
|
||||
"webpack": "5.76.2",
|
||||
"webpack-cli": "4.9.2"
|
||||
},
|
||||
"main": "index.js",
|
||||
|
||||
3
sasjs-tests/.gitignore
vendored
3
sasjs-tests/.gitignore
vendored
@@ -21,3 +21,6 @@
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
sasjsbuild
|
||||
sasjsresults
|
||||
19
sasjs-tests/.sasjslint
Normal file
19
sasjs-tests/.sasjslint
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"lineEndings": "off",
|
||||
"noTrailingSpaces": true,
|
||||
"noEncodedPasswords": true,
|
||||
"hasDoxygenHeader": true,
|
||||
"noSpacesInFileNames": true,
|
||||
"lowerCaseFileNames": true,
|
||||
"maxLineLength": 80,
|
||||
"maxHeaderLineLength": 80,
|
||||
"maxDataLineLength": 80,
|
||||
"noTabIndentation": true,
|
||||
"indentationMultiple": 2,
|
||||
"hasMacroNameInMend": true,
|
||||
"noNestedMacros": true,
|
||||
"hasMacroParentheses": true,
|
||||
"strictMacroDefinition": true,
|
||||
"noGremlins": true,
|
||||
"defaultHeader": "/**{lineEnding} @file{lineEnding} @brief <Your brief here>{lineEnding} <h4> SAS Macros </h4>{lineEnding}**/"
|
||||
}
|
||||
@@ -65,6 +65,7 @@ The code below will work on ALL SAS platforms (Viya, SAS 9 EBI, SASjs Server).
|
||||
```sas
|
||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
%let apploc=/Public/app/adapter-tests;
|
||||
filename ft15f001 temp lrecl=1000;
|
||||
parmcards4;
|
||||
%webout(FETCH)
|
||||
@@ -80,7 +81,7 @@ parmcards4;
|
||||
%mend; %x()
|
||||
%webout(CLOSE)
|
||||
;;;;
|
||||
%mx_createwebservice(path=/Public/app/common,name=sendObj)
|
||||
%mx_createwebservice(path=&apploc/services/common,name=sendObj)
|
||||
parmcards4;
|
||||
%webout(FETCH)
|
||||
%webout(OPEN)
|
||||
@@ -95,7 +96,7 @@ parmcards4;
|
||||
%mend; %x()
|
||||
%webout(CLOSE)
|
||||
;;;;
|
||||
%mx_createwebservice(path=/Public/app/common,name=sendArr)
|
||||
%mx_createwebservice(path=&apploc/services/common,name=sendArr)
|
||||
parmcards4;
|
||||
data work.macvars;
|
||||
set sashelp.vmacro;
|
||||
@@ -104,14 +105,14 @@ parmcards4;
|
||||
%webout(OBJ,macvars)
|
||||
%webout(CLOSE)
|
||||
;;;;
|
||||
%mx_createwebservice(path=/Public/app/common,name=sendMacVars)
|
||||
%mx_createwebservice(path=&apploc/services/common,name=sendMacVars)
|
||||
parmcards4;
|
||||
If you can keep your head when all about you
|
||||
Are losing theirs and blaming it on you,
|
||||
If you can trust yourself when all men doubt you,
|
||||
But make allowance for their doubting too;
|
||||
;;;;
|
||||
%mx_createwebservice(path=/Public/app/common,name=makeErr)
|
||||
%mx_createwebservice(path=&apploc/services/common,name=makeErr)
|
||||
parmcards4;
|
||||
%webout(OPEN)
|
||||
data _null_;
|
||||
@@ -120,7 +121,7 @@ data _null_;
|
||||
run;
|
||||
%webout(CLOSE)
|
||||
;;;;
|
||||
%mx_createwebservice(path=/Public/app/common,name=invalidJSON)
|
||||
%mx_createwebservice(path=&apploc/services/common,name=invalidJSON)
|
||||
```
|
||||
|
||||
You should now be able to access the tests in your browser at the deployed path on your server.
|
||||
|
||||
@@ -45,4 +45,4 @@
|
||||
"devDependencies": {
|
||||
"node-sass": "7.0.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,10 @@
|
||||
"password": "",
|
||||
"sasJsConfig": {
|
||||
"serverUrl": "",
|
||||
"appLoc": "/Public/app",
|
||||
"serverType": "SASVIYA",
|
||||
"appLoc": "/Public/app/adapter-tests",
|
||||
"serverType": "SASJS",
|
||||
"debug": false,
|
||||
"contextName": "sasjs adapter compute context",
|
||||
"useComputeApi": true
|
||||
}
|
||||
}
|
||||
}
|
||||
13
sasjs-tests/sasjs/common/invalidJSON.sas
Normal file
13
sasjs-tests/sasjs/common/invalidJSON.sas
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
@file
|
||||
@brief Makes an invalid JSON file
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
**/
|
||||
|
||||
%webout(OPEN)
|
||||
data _null_;
|
||||
file _webout;
|
||||
put ' the discovery channel ';
|
||||
run;
|
||||
%webout(CLOSE)
|
||||
11
sasjs-tests/sasjs/common/makeErr.sas
Normal file
11
sasjs-tests/sasjs/common/makeErr.sas
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
@file
|
||||
@brief Makes an error
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
**/
|
||||
|
||||
If you can keep your head when all about you
|
||||
Are losing theirs and blaming it on you,
|
||||
If you can trust yourself when all men doubt you,
|
||||
But make allowance for their doubting too;
|
||||
21
sasjs-tests/sasjs/common/sendArr.sas
Normal file
21
sasjs-tests/sasjs/common/sendArr.sas
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns JSON in Array format
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
**/
|
||||
|
||||
%webout(FETCH)
|
||||
%webout(OPEN)
|
||||
%macro x();
|
||||
%if %symexist(sasjs_tables) %then
|
||||
%do i=1 %to %sysfunc(countw(&sasjs_tables));
|
||||
%let table=%scan(&sasjs_tables,&i);
|
||||
%webout(ARR,&table,missing=STRING,showmeta=YES)
|
||||
%end;
|
||||
%else %do i=1 %to &_webin_file_count;
|
||||
%webout(ARR,&&_webin_name&i,missing=STRING,showmeta=YES)
|
||||
%end;
|
||||
%mend x;
|
||||
%x()
|
||||
%webout(CLOSE)
|
||||
13
sasjs-tests/sasjs/common/sendMacVars.sas
Normal file
13
sasjs-tests/sasjs/common/sendMacVars.sas
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns Macro Variables
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
**/
|
||||
|
||||
data work.macvars;
|
||||
set sashelp.vmacro;
|
||||
run;
|
||||
%webout(OPEN)
|
||||
%webout(OBJ,macvars)
|
||||
%webout(CLOSE)
|
||||
21
sasjs-tests/sasjs/common/sendObj.sas
Normal file
21
sasjs-tests/sasjs/common/sendObj.sas
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns JSON in Object format
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
**/
|
||||
|
||||
%webout(FETCH)
|
||||
%webout(OPEN)
|
||||
%macro x();
|
||||
%if %symexist(sasjs_tables) %then
|
||||
%do i=1 %to %sysfunc(countw(&sasjs_tables));
|
||||
%let table=%scan(&sasjs_tables,&i);
|
||||
%webout(OBJ,&table,missing=STRING,showmeta=YES)
|
||||
%end;
|
||||
%else %do i=1 %to &_webin_file_count;
|
||||
%webout(OBJ,&&_webin_name&i,missing=STRING,showmeta=YES)
|
||||
%end;
|
||||
%mend x;
|
||||
%x()
|
||||
%webout(CLOSE)
|
||||
40
sasjs-tests/sasjs/doxy/Doxyfile
Normal file
40
sasjs-tests/sasjs/doxy/Doxyfile
Normal file
@@ -0,0 +1,40 @@
|
||||
ALPHABETICAL_INDEX = NO
|
||||
|
||||
ENABLE_PREPROCESSING = NO
|
||||
EXTENSION_MAPPING = sas=Java ddl=Java
|
||||
EXTRACT_LOCAL_CLASSES = NO
|
||||
FILE_PATTERNS = *.sas \
|
||||
*.ddl \
|
||||
*.dox
|
||||
GENERATE_LATEX = NO
|
||||
GENERATE_TREEVIEW = YES
|
||||
HIDE_FRIEND_COMPOUNDS = YES
|
||||
HIDE_IN_BODY_DOCS = YES
|
||||
HIDE_SCOPE_NAMES = YES
|
||||
HIDE_UNDOC_CLASSES = YES
|
||||
HIDE_UNDOC_MEMBERS = YES
|
||||
HTML_OUTPUT = $(DOXY_HTML_OUTPUT)
|
||||
HTML_HEADER = $(HTML_HEADER)
|
||||
HTML_EXTRA_FILES = $(HTML_EXTRA_FILES)
|
||||
HTML_FOOTER = $(HTML_FOOTER)
|
||||
HTML_EXTRA_STYLESHEET = $(HTML_EXTRA_STYLESHEET)
|
||||
INHERIT_DOCS = NO
|
||||
INLINE_INFO = NO
|
||||
INPUT = $(DOXY_INPUT)
|
||||
LAYOUT_FILE = $(LAYOUT_FILE)
|
||||
USE_MDFILE_AS_MAINPAGE = README.md
|
||||
MAX_INITIALIZER_LINES = 0
|
||||
PROJECT_NAME = $(PROJECT_NAME)
|
||||
PROJECT_LOGO = $(PROJECT_LOGO)
|
||||
PROJECT_BRIEF = $(PROJECT_BRIEF)
|
||||
RECURSIVE = YES
|
||||
REPEAT_BRIEF = NO
|
||||
SHOW_NAMESPACES = NO
|
||||
SHOW_USED_FILES = NO
|
||||
SOURCE_BROWSER = YES
|
||||
SOURCE_TOOLTIPS = NO
|
||||
STRICT_PROTO_MATCHING = YES
|
||||
STRIP_CODE_COMMENTS = NO
|
||||
SUBGROUPING = NO
|
||||
TAB_SIZE = 2
|
||||
VERBATIM_HEADERS = NO
|
||||
112
sasjs-tests/sasjs/doxy/DoxygenLayout.xml
Normal file
112
sasjs-tests/sasjs/doxy/DoxygenLayout.xml
Normal file
@@ -0,0 +1,112 @@
|
||||
<doxygenlayout version="1.0">
|
||||
<!-- Generated by doxygen 1.8.14 -->
|
||||
<!-- Navigation index tabs for HTML output -->
|
||||
<navindex>
|
||||
<tab type="mainpage" visible="yes" title="Home"/>
|
||||
<tab type="pages" visible="no" title="" intro=""/>
|
||||
<tab type="modules" visible="no" title="" intro=""/>
|
||||
<tab type="namespaces" visible="no" title="">
|
||||
<tab type="namespacelist" visible="no" title="" intro=""/>
|
||||
<tab type="namespacemembers" visible="no" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="classes" visible="no" title="">
|
||||
<tab type="classlist" visible="no" title="" intro=""/>
|
||||
<tab type="classindex" visible="no" title=""/>
|
||||
<tab type="hierarchy" visible="no" title="" intro=""/>
|
||||
<tab type="classmembers" visible="no" title="" intro=""/>
|
||||
</tab>
|
||||
|
||||
<tab type="filelist" visible="yes" title="" intro="List of Files"/>
|
||||
|
||||
<tab type="examples" visible="no" title="" intro=""/>
|
||||
<tab type="user" url="data_lineage.svg" title="Lineage"/>
|
||||
</navindex>
|
||||
|
||||
|
||||
<!-- Layout definition for a file page -->
|
||||
<file>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_INCLUDE_FILES"/>
|
||||
<includegraph visible="$INCLUDE_GRAPH"/>
|
||||
<includedbygraph visible="$INCLUDED_BY_GRAPH"/>
|
||||
<sourcelink visible="yes"/>
|
||||
<memberdecl>
|
||||
<classes visible="no" title=""/>
|
||||
<namespaces visible="no" title=""/>
|
||||
<constantgroups visible="no" title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<membergroups visible="no"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
</memberdef>
|
||||
<authorsection/>
|
||||
</file>
|
||||
|
||||
<!-- Layout definition for a group page -->
|
||||
<group>
|
||||
<briefdescription visible="no"/>
|
||||
<groupgraph visible="$GROUP_GRAPHS"/>
|
||||
<memberdecl>
|
||||
<nestedgroups visible="no" title=""/>
|
||||
<dirs visible="yes" title=""/>
|
||||
<files visible="yes" title=""/>
|
||||
<namespaces visible="no" title=""/>
|
||||
<classes visible="no" title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<enumvalues title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<signals title=""/>
|
||||
<publicslots title=""/>
|
||||
<protectedslots title=""/>
|
||||
<privateslots title=""/>
|
||||
<events title=""/>
|
||||
<properties title=""/>
|
||||
<friends title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<pagedocs/>
|
||||
<inlineclasses title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<enumvalues title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<signals title=""/>
|
||||
<publicslots title=""/>
|
||||
<protectedslots title=""/>
|
||||
<privateslots title=""/>
|
||||
<events title=""/>
|
||||
<properties title=""/>
|
||||
<friends title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</group>
|
||||
|
||||
<!-- Layout definition for a directory page -->
|
||||
<directory>
|
||||
<briefdescription visible="yes"/>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<directorygraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<dirs visible="yes"/>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
</directory>
|
||||
</doxygenlayout>
|
||||
26
sasjs-tests/sasjs/doxy/doxygen.svg
Normal file
26
sasjs-tests/sasjs/doxy/doxygen.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 15 KiB |
BIN
sasjs-tests/sasjs/doxy/favicon.ico
Normal file
BIN
sasjs-tests/sasjs/doxy/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
BIN
sasjs-tests/sasjs/doxy/logo.png
Normal file
BIN
sasjs-tests/sasjs/doxy/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.5 KiB |
33
sasjs-tests/sasjs/doxy/new_footer.html
Normal file
33
sasjs-tests/sasjs/doxy/new_footer.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<!-- HTML footer for doxygen 1.8.17-->
|
||||
<!-- start footer part -->
|
||||
<!--BEGIN GENERATE_TREEVIEW-->
|
||||
<div id="nav-path" class="navpath">
|
||||
<!-- id is needed for treeview function! -->
|
||||
<ul>
|
||||
$navpath
|
||||
<li class="footer">
|
||||
$generatedby
|
||||
<a href="https://www.doxygen.org/index.html">
|
||||
<img class="footer" src="$relpath^doxygen.svg" alt="doxygen"
|
||||
/></a>
|
||||
$doxygenversion
|
||||
</li>
|
||||
<li>
|
||||
<i> For more information visit the </i>
|
||||
<a href="https://cli.sasjs.io">SASjs cli</a> documentation.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!--END GENERATE_TREEVIEW-->
|
||||
<!--BEGIN !GENERATE_TREEVIEW-->
|
||||
<hr class="footer" />
|
||||
<address class="footer">
|
||||
<small>
|
||||
$generatedby  <a href="http://www.doxygen.org/index.html">
|
||||
<img class="footer" src="$relpath^doxygen.svg" alt="doxygen" />
|
||||
</a>
|
||||
$doxygenversion
|
||||
</small>
|
||||
</address>
|
||||
|
||||
<!--END !GENERATE_TREEVIEW-->
|
||||
57
sasjs-tests/sasjs/doxy/new_header.html
Normal file
57
sasjs-tests/sasjs/doxy/new_header.html
Normal file
@@ -0,0 +1,57 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<!-- HTML header for doxygen 1.8.17-->
|
||||
<html xmlns="https://www.w3.org/1999/xhtml" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=9" />
|
||||
<meta name="generator" content="Doxygen $doxygenversion" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<!--BEGIN PROJECT_NAME-->
|
||||
<title>$projectname: $title</title>
|
||||
<meta name="description" content="$projectbrief" />
|
||||
<!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME-->
|
||||
<title>$title</title>
|
||||
<!--END !PROJECT_NAME-->
|
||||
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css" />
|
||||
<script type="text/javascript" src="$relpath^jquery.js"></script>
|
||||
<script type="text/javascript" src="$relpath^dynsections.js"></script>
|
||||
$treeview $search $mathjax
|
||||
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
||||
<link rel="shortcut icon" href="$relpath^favicon.ico" type="image/x-icon" />
|
||||
$extrastylesheet
|
||||
</head>
|
||||
<body>
|
||||
<div id="top">
|
||||
<!-- do not remove this div, it is closed by doxygen! -->
|
||||
|
||||
<!--BEGIN TITLEAREA-->
|
||||
<div id="titlearea">
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr style="height: 56px">
|
||||
<!--BEGIN PROJECT_LOGO-->
|
||||
<td id="projectlogo">
|
||||
<a href="$relpath^"
|
||||
><img alt="Logo" src="$relpath^$projectlogo"
|
||||
/></a>
|
||||
</td>
|
||||
<!--END PROJECT_LOGO-->
|
||||
<td id="projectalign" style="padding-left: 0.5em">
|
||||
<div id="projectname">$projectname</div>
|
||||
<div id="projectbrief">$projectbrief</div>
|
||||
</td>
|
||||
<!--BEGIN DISABLE_INDEX-->
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
<td>$searchbox</td>
|
||||
<!--END SEARCHENGINE-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--END TITLEAREA-->
|
||||
<!-- end header part -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
4
sasjs-tests/sasjs/doxy/new_stylesheet.css
Normal file
4
sasjs-tests/sasjs/doxy/new_stylesheet.css
Normal file
@@ -0,0 +1,4 @@
|
||||
#projectlogo img {
|
||||
border: 0px none;
|
||||
max-height: 70px;
|
||||
}
|
||||
17
sasjs-tests/sasjs/sasjsconfig.json
Normal file
17
sasjs-tests/sasjs/sasjsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "https://cli.sasjs.io/sasjsconfig-schema.json",
|
||||
"serviceConfig": {
|
||||
"serviceFolders": [
|
||||
"sasjs/common"
|
||||
]
|
||||
},
|
||||
"defaultTarget": "4gl",
|
||||
"targets": [
|
||||
{
|
||||
"name": "4gl",
|
||||
"serverType": "SASJS",
|
||||
"serverUrl": "https://sas9.4gl.io",
|
||||
"appLoc": "/Public/app/adapter-tests"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -48,7 +48,7 @@ export const computeTests = (adapter: SASjs, appLoc: string): TestSuite => ({
|
||||
test: () => {
|
||||
const data: any = { table1: [{ col1: 'first col value' }] }
|
||||
return adapter.startComputeJob(
|
||||
'/Public/app/common/sendArr',
|
||||
'/Public/app/adapter-tests/services/common/sendArr',
|
||||
data,
|
||||
{},
|
||||
undefined,
|
||||
|
||||
@@ -4,8 +4,7 @@ import {
|
||||
UploadFile,
|
||||
EditContextInput,
|
||||
PollOptions,
|
||||
LoginMechanism,
|
||||
ExecutionQuery
|
||||
LoginMechanism
|
||||
} from './types'
|
||||
import { SASViyaApiClient } from './SASViyaApiClient'
|
||||
import { SAS9ApiClient } from './SAS9ApiClient'
|
||||
@@ -17,7 +16,6 @@ import {
|
||||
AuthConfig,
|
||||
ExtraResponseAttributes,
|
||||
SasAuthResponse,
|
||||
ServicePackSASjs,
|
||||
AuthConfigSas9
|
||||
} from '@sasjs/utils/types'
|
||||
import { RequestClient } from './request/RequestClient'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ServerType } from '@sasjs/utils/types'
|
||||
import { RequestClient } from '../request/RequestClient'
|
||||
import { NotFoundError } from '../types/errors'
|
||||
import { LoginOptions, LoginResult, LoginResultInternal } from '../types/Login'
|
||||
import { serialize } from '../utils'
|
||||
import { extractUserLongNameSas9 } from '../utils/sas9/extractUserLongNameSas9'
|
||||
@@ -12,7 +13,7 @@ export class AuthManager {
|
||||
public userLongName = ''
|
||||
private loginUrl: string
|
||||
private logoutUrl: string
|
||||
private redirectedLoginUrl = `/SASLogon/home`
|
||||
private redirectedLoginUrl = `/SASLogon` //SAS 9 M8 no longer redirects from `/SASLogon/home` to the login page. `/SASLogon` seems to be stable enough across SAS versions
|
||||
constructor(
|
||||
private serverUrl: string,
|
||||
private serverType: ServerType,
|
||||
@@ -42,6 +43,9 @@ export class AuthManager {
|
||||
} = await this.fetchUserName()
|
||||
|
||||
if (isLoggedInAlready) {
|
||||
const logger = process.logger || console
|
||||
logger.log('login was not attempted as a valid session already exists')
|
||||
|
||||
await this.loginCallback()
|
||||
|
||||
return {
|
||||
@@ -109,6 +113,9 @@ export class AuthManager {
|
||||
} = await this.checkSession()
|
||||
|
||||
if (isLoggedInAlready) {
|
||||
const logger = process.logger || console
|
||||
logger.log('login was not attempted as a valid session already exists')
|
||||
|
||||
await this.loginCallback()
|
||||
|
||||
this.userName = loginParams.username
|
||||
@@ -131,6 +138,10 @@ export class AuthManager {
|
||||
loginResponse = await this.sendLoginRequest(newLoginForm, loginParams)
|
||||
}
|
||||
|
||||
// Sometimes due to redirection on SAS9 and SASViya we don't get the login response that says
|
||||
// You have signed in. Therefore, we have to make an extra request for checking session to
|
||||
// ensure either user is logged in or not.
|
||||
|
||||
const res = await this.checkSession()
|
||||
isLoggedIn = res.isLoggedIn
|
||||
this.userLongName = res.userLongName
|
||||
@@ -155,10 +166,12 @@ export class AuthManager {
|
||||
private async performCASSecurityCheck() {
|
||||
const casAuthenticationUrl = `${this.serverUrl}/SASStoredProcess/j_spring_cas_security_check`
|
||||
|
||||
await this.requestClient.get<string>(
|
||||
`/SASLogon/login?service=${casAuthenticationUrl}`,
|
||||
undefined
|
||||
)
|
||||
await this.requestClient
|
||||
.get<string>(`/SASLogon/login?service=${casAuthenticationUrl}`, undefined)
|
||||
.catch((err) => {
|
||||
// ignore if resource not found error
|
||||
if (!(err instanceof NotFoundError)) throw err
|
||||
})
|
||||
}
|
||||
|
||||
private async sendLoginRequest(
|
||||
@@ -312,12 +325,13 @@ export class AuthManager {
|
||||
}
|
||||
|
||||
private getLoginForm(response: any) {
|
||||
const pattern: RegExp = /<form.+action="(.*Logon[^"]*).*>/
|
||||
const pattern: RegExp = /<form.+action="(.*(Logon|login)[^"]*).*>/
|
||||
const matches = pattern.exec(response)
|
||||
const formInputs: any = {}
|
||||
|
||||
if (matches && matches.length) {
|
||||
this.setLoginUrl(matches)
|
||||
response = response.replace(/<input/g, '\n<input')
|
||||
const inputs = response.match(/<input.*"hidden"[^>]*>/g)
|
||||
|
||||
if (inputs) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const isLogInRequired = (response: string): boolean => {
|
||||
const pattern: RegExp = /<form.+action="(.*Logon[^"]*).*>/gm
|
||||
const pattern: RegExp = /<form.+action="(.*(Logon)|(login)[^"]*).*>/gm
|
||||
const matches = pattern.test(response)
|
||||
return matches
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ describe('AuthManager', () => {
|
||||
expect(loginResponse.userName).toEqual(userName)
|
||||
|
||||
expect(openWebPageModule.openWebPage).toHaveBeenCalledWith(
|
||||
`/SASLogon/home`,
|
||||
`/SASLogon`,
|
||||
'SASLogon',
|
||||
{
|
||||
width: 500,
|
||||
@@ -409,7 +409,7 @@ describe('AuthManager', () => {
|
||||
expect(loginResponse.userName).toEqual(userName)
|
||||
|
||||
expect(openWebPageModule.openWebPage).toHaveBeenCalledWith(
|
||||
`/SASLogon/home`,
|
||||
`/SASLogon`,
|
||||
'SASLogon',
|
||||
{
|
||||
width: 500,
|
||||
@@ -453,7 +453,7 @@ describe('AuthManager', () => {
|
||||
expect(loginResponse.userName).toEqual('')
|
||||
|
||||
expect(openWebPageModule.openWebPage).toHaveBeenCalledWith(
|
||||
`/SASLogon/home`,
|
||||
`/SASLogon`,
|
||||
'SASLogon',
|
||||
{
|
||||
width: 500,
|
||||
@@ -497,7 +497,7 @@ describe('AuthManager', () => {
|
||||
expect(loginResponse.userName).toEqual('')
|
||||
|
||||
expect(openWebPageModule.openWebPage).toHaveBeenCalledWith(
|
||||
`/SASLogon/home`,
|
||||
`/SASLogon`,
|
||||
'SASLogon',
|
||||
{
|
||||
width: 500,
|
||||
|
||||
@@ -17,7 +17,7 @@ describe('verifySas9Login', () => {
|
||||
it('should return isLoggedIn true by checking state of popup', async () => {
|
||||
const popup = {
|
||||
window: {
|
||||
location: { href: serverUrl + `/SASLogon/home` },
|
||||
location: { href: serverUrl + `/SASLogon` },
|
||||
document: { body: { innerText: '<h3>You have signed in.</h3>' } }
|
||||
}
|
||||
} as unknown as Window
|
||||
|
||||
@@ -18,7 +18,7 @@ describe('verifySasViyaLogin', () => {
|
||||
it('should return isLoggedIn true by checking state of popup', async () => {
|
||||
const popup = {
|
||||
window: {
|
||||
location: { href: serverUrl + `/SASLogon/home` },
|
||||
location: { href: serverUrl + `/SASLogon` },
|
||||
document: { body: { innerText: '<h3>You have signed in.</h3>' } }
|
||||
}
|
||||
} as unknown as Window
|
||||
|
||||
273
src/minified/sas9/SASjs.ts
Normal file
273
src/minified/sas9/SASjs.ts
Normal file
@@ -0,0 +1,273 @@
|
||||
import { validateInput, compareTimestamps } from '../../utils'
|
||||
import { SASjsConfig, UploadFile, LoginMechanism } from '../../types'
|
||||
import { AuthManager } from '../../auth'
|
||||
import {
|
||||
ServerType,
|
||||
AuthConfig,
|
||||
ExtraResponseAttributes
|
||||
} from '@sasjs/utils/types'
|
||||
import { RequestClient } from '../../request/RequestClient'
|
||||
import { FileUploader } from '../../job-execution/FileUploader'
|
||||
import { WebJobExecutor } from './WebJobExecutor'
|
||||
import { ErrorResponse } from '../../types/errors/ErrorResponse'
|
||||
import { LoginOptions, LoginResult } from '../../types/Login'
|
||||
|
||||
const defaultConfig: SASjsConfig = {
|
||||
serverUrl: '',
|
||||
pathSASJS: '/SASjsApi/stp/execute',
|
||||
pathSAS9: '/SASStoredProcess/do',
|
||||
pathSASViya: '/SASJobExecution',
|
||||
appLoc: '/Public/seedapp',
|
||||
serverType: ServerType.Sas9,
|
||||
debug: false,
|
||||
contextName: 'SAS Job Execution compute context',
|
||||
useComputeApi: null,
|
||||
loginMechanism: LoginMechanism.Default
|
||||
}
|
||||
|
||||
/**
|
||||
* SASjs is a JavaScript adapter for SAS.
|
||||
*
|
||||
*/
|
||||
export default class SASjs {
|
||||
private sasjsConfig: SASjsConfig = new SASjsConfig()
|
||||
private jobsPath: string = ''
|
||||
private fileUploader: FileUploader | null = null
|
||||
private authManager: AuthManager | null = null
|
||||
private requestClient: RequestClient | null = null
|
||||
private webJobExecutor: WebJobExecutor | null = null
|
||||
|
||||
constructor(config?: Partial<SASjsConfig>) {
|
||||
this.sasjsConfig = {
|
||||
...defaultConfig,
|
||||
...config
|
||||
}
|
||||
|
||||
this.setupConfiguration()
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs into the SAS server with the supplied credentials.
|
||||
* @param username - a string representing the username.
|
||||
* @param password - a string representing the password.
|
||||
* @param clientId - a string representing the client ID.
|
||||
*/
|
||||
public async logIn(
|
||||
username?: string,
|
||||
password?: string,
|
||||
clientId?: string,
|
||||
options: LoginOptions = {}
|
||||
): Promise<LoginResult> {
|
||||
if (this.sasjsConfig.loginMechanism === LoginMechanism.Default) {
|
||||
if (!username || !password)
|
||||
throw new Error(
|
||||
'A username and password are required when using the default login mechanism.'
|
||||
)
|
||||
|
||||
return this.authManager!.logIn(username, password)
|
||||
}
|
||||
|
||||
if (typeof window === typeof undefined) {
|
||||
throw new Error(
|
||||
'The redirected login mechanism is only available for use in the browser.'
|
||||
)
|
||||
}
|
||||
|
||||
return this.authManager!.redirectedLogIn(options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs out of the configured SAS server.
|
||||
*/
|
||||
public logOut() {
|
||||
return this.authManager!.logOut()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current SASjs configuration.
|
||||
*
|
||||
*/
|
||||
public getSasjsConfig() {
|
||||
return this.sasjsConfig
|
||||
}
|
||||
|
||||
/**
|
||||
* this method returns an array of SASjsRequest
|
||||
* @returns SASjsRequest[]
|
||||
*/
|
||||
public getSasRequests() {
|
||||
const requests = [...this.requestClient!.getRequests()]
|
||||
const sortedRequests = requests.sort(compareTimestamps)
|
||||
return sortedRequests
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the debug state. Turning this on will enable additional logging in the adapter.
|
||||
* @param value - boolean indicating debug state (on/off).
|
||||
*/
|
||||
public setDebugState(value: boolean) {
|
||||
this.sasjsConfig.debug = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a file to the given service.
|
||||
* @param sasJob - the path to the SAS program (ultimately resolves to
|
||||
* the SAS `_program` parameter to run a Job Definition or SAS 9 Stored
|
||||
* Process). Is prepended at runtime with the value of `appLoc`.
|
||||
* @param files - array of files to be uploaded, including File object and file name.
|
||||
* @param params - request URL parameters.
|
||||
* @param config - provide any changes to the config here, for instance to
|
||||
* enable/disable `debug`. Any change provided will override the global config,
|
||||
* for that particular function call.
|
||||
* @param loginRequiredCallback - a function that is called if the
|
||||
* user is not logged in (eg to display a login form). The request will be
|
||||
* resubmitted after successful login.
|
||||
*/
|
||||
public async uploadFile(
|
||||
sasJob: string,
|
||||
files: UploadFile[],
|
||||
params: { [key: string]: any } | null,
|
||||
config: { [key: string]: any } = {},
|
||||
loginRequiredCallback?: () => any
|
||||
) {
|
||||
config = {
|
||||
...this.sasjsConfig,
|
||||
...config
|
||||
}
|
||||
const data = { files, params }
|
||||
|
||||
return await this.fileUploader!.execute(
|
||||
sasJob,
|
||||
data,
|
||||
config,
|
||||
loginRequiredCallback
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a request to program specified in `SASjob` (could be a Viya Job, a
|
||||
* SAS 9 Stored Process, or a SASjs Server Stored Program). The response
|
||||
* object will always contain table names in lowercase, and column names in
|
||||
* uppercase. Values are returned formatted by default, unformatted
|
||||
* values can be configured as an option in the `%webout` macro.
|
||||
*
|
||||
* @param sasJob - the path to the SAS program (ultimately resolves to
|
||||
* the SAS `_program` parameter to run a Job Definition or SAS 9 Stored
|
||||
* Process). Is prepended at runtime with the value of `appLoc`.
|
||||
* @param data - a JSON object containing one or more tables to be sent to
|
||||
* SAS. For an example of the table structure, see the project README. This
|
||||
* value can be `null` if no inputs are required.
|
||||
* @param config - provide any changes to the config here, for instance to
|
||||
* enable/disable `debug`. Any change provided will override the global config,
|
||||
* for that particular function call.
|
||||
* @param loginRequiredCallback - a function that is called if the
|
||||
* user is not logged in (eg to display a login form). The request will be
|
||||
* resubmitted after successful login.
|
||||
* When using a `loginRequiredCallback`, the call to the request will look, for example, like so:
|
||||
* `await request(sasJobPath, data, config, () => setIsLoggedIn(false))`
|
||||
* If you are not passing in any data and configuration, it will look like so:
|
||||
* `await request(sasJobPath, {}, {}, () => setIsLoggedIn(false))`
|
||||
* @param extraResponseAttributes - a array of predefined values that are used
|
||||
* to provide extra attributes (same names as those values) to be added in response
|
||||
* Supported values are declared in ExtraResponseAttributes type.
|
||||
*/
|
||||
public async request(
|
||||
sasJob: string,
|
||||
data: { [key: string]: any } | null,
|
||||
config: { [key: string]: any } = {},
|
||||
loginRequiredCallback?: () => any,
|
||||
authConfig?: AuthConfig,
|
||||
extraResponseAttributes: ExtraResponseAttributes[] = []
|
||||
) {
|
||||
config = {
|
||||
...this.sasjsConfig,
|
||||
...config
|
||||
}
|
||||
|
||||
const validationResult = validateInput(data)
|
||||
|
||||
// status is true if the data passes validation checks above
|
||||
if (validationResult.status) {
|
||||
return await this.webJobExecutor!.execute(
|
||||
sasJob,
|
||||
data,
|
||||
config,
|
||||
loginRequiredCallback,
|
||||
authConfig,
|
||||
extraResponseAttributes
|
||||
)
|
||||
} else {
|
||||
return Promise.reject(new ErrorResponse(validationResult.msg))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a session is active, or login is required.
|
||||
* @returns - a promise which resolves with an object containing two values - a boolean `isLoggedIn`, and a string `userName`.
|
||||
*/
|
||||
public async checkSession() {
|
||||
return this.authManager!.checkSession()
|
||||
}
|
||||
|
||||
private setupConfiguration() {
|
||||
if (
|
||||
this.sasjsConfig.serverUrl === undefined ||
|
||||
this.sasjsConfig.serverUrl === ''
|
||||
) {
|
||||
if (typeof location !== 'undefined') {
|
||||
let url = `${location.protocol}//${location.hostname}`
|
||||
|
||||
if (location.port) url = `${url}:${location.port}`
|
||||
|
||||
this.sasjsConfig.serverUrl = url
|
||||
} else {
|
||||
this.sasjsConfig.serverUrl = ''
|
||||
}
|
||||
}
|
||||
|
||||
if (this.sasjsConfig.serverUrl.slice(-1) === '/') {
|
||||
this.sasjsConfig.serverUrl = this.sasjsConfig.serverUrl.slice(0, -1)
|
||||
}
|
||||
|
||||
if (!this.requestClient) {
|
||||
this.requestClient = new RequestClient(
|
||||
this.sasjsConfig.serverUrl,
|
||||
this.sasjsConfig.httpsAgentOptions,
|
||||
this.sasjsConfig.requestHistoryLimit
|
||||
)
|
||||
} else {
|
||||
this.requestClient.setConfig(
|
||||
this.sasjsConfig.serverUrl,
|
||||
this.sasjsConfig.httpsAgentOptions
|
||||
)
|
||||
}
|
||||
|
||||
this.jobsPath = this.sasjsConfig.pathSAS9
|
||||
|
||||
this.authManager = new AuthManager(
|
||||
this.sasjsConfig.serverUrl,
|
||||
this.sasjsConfig.serverType!,
|
||||
this.requestClient,
|
||||
this.resendWaitingRequests
|
||||
)
|
||||
|
||||
this.fileUploader = new FileUploader(
|
||||
this.sasjsConfig.serverUrl,
|
||||
this.sasjsConfig.serverType!,
|
||||
this.jobsPath,
|
||||
this.requestClient
|
||||
)
|
||||
|
||||
this.webJobExecutor = new WebJobExecutor(
|
||||
this.sasjsConfig.serverUrl,
|
||||
this.sasjsConfig.serverType!,
|
||||
this.jobsPath,
|
||||
this.requestClient
|
||||
)
|
||||
}
|
||||
|
||||
private resendWaitingRequests = async () => {
|
||||
await this.webJobExecutor?.resendWaitingRequests()
|
||||
await this.fileUploader?.resendWaitingRequests()
|
||||
}
|
||||
}
|
||||
157
src/minified/sas9/WebJobExecutor.ts
Normal file
157
src/minified/sas9/WebJobExecutor.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import {
|
||||
AuthConfig,
|
||||
ExtraResponseAttributes,
|
||||
ServerType
|
||||
} from '@sasjs/utils/types'
|
||||
import {
|
||||
ErrorResponse,
|
||||
JobExecutionError,
|
||||
LoginRequiredError
|
||||
} from '../../types/errors'
|
||||
import { RequestClient } from '../../request/RequestClient'
|
||||
import {
|
||||
isRelativePath,
|
||||
parseSasViyaDebugResponse,
|
||||
appendExtraResponseAttributes,
|
||||
convertToCSV
|
||||
} from '../../utils'
|
||||
import { BaseJobExecutor } from '../../job-execution/JobExecutor'
|
||||
import { parseWeboutResponse } from '../../utils/parseWeboutResponse'
|
||||
|
||||
export interface WaitingRequstPromise {
|
||||
promise: Promise<any> | null
|
||||
resolve: any
|
||||
reject: any
|
||||
}
|
||||
export class WebJobExecutor extends BaseJobExecutor {
|
||||
constructor(
|
||||
serverUrl: string,
|
||||
serverType: ServerType,
|
||||
private jobsPath: string,
|
||||
private requestClient: RequestClient
|
||||
) {
|
||||
super(serverUrl, serverType)
|
||||
}
|
||||
|
||||
async execute(
|
||||
sasJob: string,
|
||||
data: any,
|
||||
config: any,
|
||||
loginRequiredCallback?: any,
|
||||
authConfig?: AuthConfig,
|
||||
extraResponseAttributes: ExtraResponseAttributes[] = []
|
||||
) {
|
||||
const loginCallback = loginRequiredCallback
|
||||
const program = isRelativePath(sasJob)
|
||||
? config.appLoc
|
||||
? config.appLoc.replace(/\/?$/, '/') + sasJob.replace(/^\//, '')
|
||||
: sasJob
|
||||
: sasJob
|
||||
let apiUrl = `${config.serverUrl}${this.jobsPath}/?${'_program=' + program}`
|
||||
|
||||
let requestParams = {
|
||||
...this.getRequestParams(config)
|
||||
}
|
||||
|
||||
let formData = new FormData()
|
||||
|
||||
if (data) {
|
||||
try {
|
||||
formData = generateFileUploadForm(formData, data)
|
||||
} catch (e: any) {
|
||||
return Promise.reject(new ErrorResponse(e?.message, e))
|
||||
}
|
||||
}
|
||||
|
||||
for (const key in requestParams) {
|
||||
if (requestParams.hasOwnProperty(key)) {
|
||||
formData.append(key, requestParams[key])
|
||||
}
|
||||
}
|
||||
|
||||
const requestPromise = new Promise((resolve, reject) => {
|
||||
this.requestClient!.post(apiUrl, formData, authConfig?.access_token)
|
||||
.then(async (res: any) => {
|
||||
this.requestClient!.appendRequest(res, sasJob, config.debug)
|
||||
|
||||
const jsonResponse =
|
||||
config.debug && typeof res.result === 'string'
|
||||
? parseWeboutResponse(res.result, apiUrl)
|
||||
: res.result
|
||||
|
||||
const responseObject = appendExtraResponseAttributes(
|
||||
{ result: jsonResponse, log: res.log },
|
||||
extraResponseAttributes
|
||||
)
|
||||
resolve(responseObject)
|
||||
})
|
||||
.catch(async (e: Error) => {
|
||||
if (e instanceof JobExecutionError) {
|
||||
this.requestClient!.appendRequest(e, sasJob, config.debug)
|
||||
reject(new ErrorResponse(e?.message, e))
|
||||
}
|
||||
|
||||
if (e instanceof LoginRequiredError) {
|
||||
if (!loginRequiredCallback) {
|
||||
reject(
|
||||
new ErrorResponse(
|
||||
'Request is not authenticated. Make sure .env file exists with valid credentials.',
|
||||
e
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
this.appendWaitingRequest(() => {
|
||||
return this.execute(
|
||||
sasJob,
|
||||
data,
|
||||
config,
|
||||
loginRequiredCallback,
|
||||
authConfig,
|
||||
extraResponseAttributes
|
||||
).then(
|
||||
(res: any) => {
|
||||
resolve(res)
|
||||
},
|
||||
(err: any) => {
|
||||
reject(err)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
if (loginCallback) await loginCallback()
|
||||
} else reject(new ErrorResponse(e?.message, e))
|
||||
})
|
||||
})
|
||||
|
||||
return requestPromise
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* One of the approaches SASjs takes to send tables-formatted JSON (see README)
|
||||
* to SAS is as multipart form data, where each table is provided as a specially
|
||||
* formatted CSV file.
|
||||
*/
|
||||
const generateFileUploadForm = (formData: FormData, data: any): FormData => {
|
||||
for (const tableName in data) {
|
||||
if (!Array.isArray(data[tableName])) continue
|
||||
|
||||
const name = 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.'
|
||||
)
|
||||
}
|
||||
|
||||
const file = new Blob([csv], {
|
||||
type: 'application/csv'
|
||||
})
|
||||
|
||||
formData.append(name, file, `${name}.csv`)
|
||||
}
|
||||
|
||||
return formData
|
||||
}
|
||||
3
src/minified/sas9/index.ts
Normal file
3
src/minified/sas9/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import SASjs from './SASjs'
|
||||
export * from '../../types'
|
||||
export default SASjs
|
||||
@@ -21,8 +21,8 @@ export class ErrorResponse {
|
||||
}
|
||||
}
|
||||
|
||||
interface ErrorBody {
|
||||
export interface ErrorBody {
|
||||
message: string
|
||||
details: string
|
||||
details: any
|
||||
raw: any
|
||||
}
|
||||
|
||||
@@ -23,8 +23,16 @@ const optimization = {
|
||||
}
|
||||
|
||||
const browserConfig = {
|
||||
entry: './src/index.ts',
|
||||
devtool: 'inline-source-map',
|
||||
entry: {
|
||||
index: './src/index.ts',
|
||||
minified_sas9: './src/minified/sas9/index.ts'
|
||||
},
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
path: path.resolve(__dirname, 'build'),
|
||||
libraryTarget: 'umd',
|
||||
library: 'SASjs'
|
||||
},
|
||||
mode: 'production',
|
||||
optimization: optimization,
|
||||
module: {
|
||||
@@ -40,12 +48,6 @@ const browserConfig = {
|
||||
extensions: ['.ts', '.js'],
|
||||
fallback: { https: false, fs: false, readline: false }
|
||||
},
|
||||
output: {
|
||||
filename: 'index.js',
|
||||
path: path.resolve(__dirname, 'build'),
|
||||
libraryTarget: 'umd',
|
||||
library: 'SASjs'
|
||||
},
|
||||
plugins: [
|
||||
...defaultPlugins,
|
||||
new webpack.ProvidePlugin({
|
||||
@@ -55,6 +57,18 @@ const browserConfig = {
|
||||
]
|
||||
}
|
||||
|
||||
const browserConfigWithDevTool = {
|
||||
...browserConfig,
|
||||
entry: './src/index.ts',
|
||||
output: {
|
||||
filename: 'index-dev.js',
|
||||
path: path.resolve(__dirname, 'build'),
|
||||
libraryTarget: 'umd',
|
||||
library: 'SASjs'
|
||||
},
|
||||
devtool: 'inline-source-map'
|
||||
}
|
||||
|
||||
const browserConfigWithoutProcessPlugin = {
|
||||
entry: browserConfig.entry,
|
||||
devtool: browserConfig.devtool,
|
||||
@@ -76,4 +90,4 @@ const nodeConfig = {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = [browserConfig, nodeConfig]
|
||||
module.exports = [browserConfig, browserConfigWithDevTool, nodeConfig]
|
||||
|
||||
Reference in New Issue
Block a user